flutter从provider看flutter页面刷新的两种机制

1,调用setState方法刷新界面

使用 StatefulWidget 作为页面,StatefulWidget基类实现为如下:

less 复制代码
abstract class StatefulWidget extends Widget {
  
  @override
  StatefulElement createElement() => StatefulElement(this);

 
  @protected
  @factory
  State createState();  
}

通过createState方法创建state,调用state的build方法创建视图:

scala 复制代码
//使用 StatefulElement element类型
class StatefulElement extends ComponentElement {
  // state
  State<StatefulWidget> get state => _state!;
  //调用state的build方法创建视图
  Widget build() => state.build(this);
}

调用state的setState方法刷新界面:

scala 复制代码
 class State<T extends StatefulWidget> .. {
//..
 
  void setState(VoidCallback fn) {
    
    _element!.markNeedsBuild();
  }
///
}

我们看到就是调用 element的markNeedsBuild方法刷新界面 element:

scala 复制代码
abstract class Element extends DiagnosticableTree implements BuildContext {

//标记Element 为dirty
 void markNeedsBuild() {
   
    if (dirty) {
      return;
    }
    _dirty = true;
    owner!.scheduleBuildFor(this);
  }

}

owner核心实现如下:

csharp 复制代码
class BuildOwner {
 //...
//
 void scheduleBuildFor(Element element) {
    
    _dirtyElements.add(element);
    element._inDirtyList = true;
 
  }
}

了解了这种调用原理,我们就可以在咱们实现的statefullwidget的state中,手动调用下面方法刷新界面:

kotlin 复制代码
  (context as Element).owner!.scheduleBuildFor(this);

效果是跟调用setState一样的。

2,provider简单实例

一个使用provider的简单页面代码如下:

scala 复制代码
class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners(); // 通知观察者状态已更改
  }
}

class ProvdiderTestPage1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => Counter(), // 创建Counter实例并提供给整个应用程序
      child: Scaffold(body: ProvdierPage1()),
    );
  }
}
class ProvdierPage1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 使用Provider.of获取Counter实例
    final counter = Provider.of<Counter>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Provider 示例'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              '计数器值:',
              style: TextStyle(fontSize: 20),
            ),
            Text(
              '${counter.count}',
              style: TextStyle(fontSize: 40),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 调用Counter实例的increment方法
          counter.increment();
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

通过上面页面,我们发现provider的调用为如下:

  • ChangeNotifierProvider
  • Provider.of(context);
  • counter.increment();
  • notifyListeners(); // 通知观察者状态已更改

3,使用provider刷新界面

下面我们看一下provider原理

provider没有使用StatefulWidget 和State 核心区别就是没有使用State类作为管理数据变化。

scala 复制代码
class ProvdiderTestPage1 extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return ChangeNotifierProvider(
     create: (context) => Counter(), // 创建Counter实例并提供给整个应用程序
     child: Scaffold(body: ProvdierPage1()),
   );
 }
}

看下这个ChangeNotifierProvider实现:

scala 复制代码
// 继承 ListenableProvider
class ChangeNotifierProvider<T extends ChangeNotifier?>
    extends ListenableProvider<T> {
 
}

继续看父类:

scala 复制代码
//ChangeNotifierProvider继承ListenableProvider
class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {

}
//ListenableProvider继承InheritedProvider:
class InheritedProvider<T> extends SingleChildStatelessWidget {
}
 
abstract class SingleChildStatelessWidget extends StatelessWidget
    implements SingleChildWidget {
}

所以我们的页面widget实际上是一个StatelessWidget类型的。

那么这是如何做到数据刷新页面刷新的呢? 先看页面如何build的

scala 复制代码
abstract class SingleChildStatelessWidget extends StatelessWidget
    implements SingleChildWidget {
 

  @override
  Widget build(BuildContext context) => buildWithChild(context, _child);
 
}

build为调用buildWithChild ,子类实现为:

scala 复制代码
class InheritedProvider<T> extends SingleChildStatelessWidget {
   
  @override
  Widget buildWithChild(BuildContext context, Widget? child) {
 
    return _InheritedProviderScope<T?>(
      owner: this,
      debugType: kDebugMode ? '$runtimeType' : '',
      child: builder != null
          ? Builder(
              builder: (context) => builder!(context, child),
            )
          : child!,
    );
  }
}

我们创建 ChangeNotifierProvider 时的 build 就是在这里被调用。

通过组合包装,在SingleChildStatelessWidget类中 build返回的一个 _InheritedProviderScope 类型的widget:

scala 复制代码
class _InheritedProviderScope<T> extends InheritedWidget {
  
}

abstract class InheritedWidget extends ProxyWidget {
}

abstract class ProxyWidget extends Widget {
}

相比于StatefulWidget+StatefulElement+ State 的管理机制,我们用的是 _InheritedProviderScopeElement类型的element对象:

scala 复制代码
class _InheritedProviderScope<T> extends InheritedWidget {
 
  @override
  _InheritedProviderScopeElement<T> createElement() {
    return _InheritedProviderScopeElement<T>(this);
  }
}

_InheritedProviderScopeElement实现如下:

scala 复制代码
class _InheritedProviderScopeElement<T> extends InheritedElement
    implements InheritedContext<T> {
 
  @override
  _InheritedProviderScope<T> get widget =>
      super.widget as _InheritedProviderScope<T>;
  

  @override
  void markNeedsNotifyDependents() {
    if (!_isNotifyDependentsEnabled) {
      return;
    }

    markNeedsBuild();
    _shouldNotifyDependents = true;
  }
 
}

看到这里,跟StatefullWidget一样,也是通过markNeedsBuild方法 来标记dirty来完成界面刷新的

回到一开始,刷新数据我们是通过 notifyListeners 方法进行。

scala 复制代码
class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners(); // 通知观察者状态已更改
  }
} 

notifyListeners 是怎么走到markNeedsBuild呢?

先看下:

ini 复制代码
Provider.of<Counter>(context);

因为使用ChangeNotifierProvider包裹,所以Provider.of(context);

调用create:

less 复制代码
 ChangeNotifierProvider(
      create: (context) => Counter(), // 创建Counter实例并提供给整个应用程序
      child: Scaffold(body: ProvdierPage1()),
    );

_CreateInheritedProviderState

csharp 复制代码
  class _CreateInheritedProviderState<T>
    extends _DelegateState<T, _CreateInheritedProvider<T>> {


       T get value {
        

         if (!_didInitValue) {
           _didInitValue = true;
           if (delegate.create != null) {
          
               _value = delegate.create!(element!); //创建 Counter 对象
          }
        
         _removeListener ??= delegate.startListening?.call(element!, _value as T); //Counter添加监听函数 counter数据改变通过notifyListeners通知监听函数
 
         return _value as T;
       }
}

ListenableProvider

scala 复制代码
  class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {
 
       static VoidCallback _startListening(
         InheritedContext<Listenable?> e,
         Listenable? value,
       ) {
         value?.addListener(e.markNeedsNotifyDependents);
         return () => value?.removeListener(e.markNeedsNotifyDependents);
       }
 
  }

InheritedContext

e.markNeedsNotifyDependents

scala 复制代码
abstract class InheritedContext<T> extends BuildContext {

    void markNeedsNotifyDependents();
}

_InheritedProviderScopeElement

scala 复制代码
class _InheritedProviderScopeElement<T> extends InheritedElement
    implements InheritedContext<T> {

  @override
  void markNeedsNotifyDependents() {
    if (!_isNotifyDependentsEnabled) {
      return;
    }

    markNeedsBuild();
    _shouldNotifyDependents = true;
  }

}

所以不难看出,counter对象状态改变,都将会通知到 InheritedContext 的markNeedsNotifyDependents 函数从而调用markNeedsBuild方法.

scala 复制代码
class _InheritedProviderScopeElement<T> extends InheritedElement
    implements InheritedContext<T>
class InheritedElement extends ProxyElement 
abstract class ProxyElement extends ComponentElement
abstract class ComponentElement extends Element
abstract class Element extends DiagnosticableTree implements BuildContext
BuildContext:
  void markNeedsBuild() {
    owner!.scheduleBuildFor(this);
  }

总结

provider使用

provider 是Flutter中一种常用的状态管理库,它通过使用 ChangeNotifier 或其他类似的可监听对象,使得在状态改变时能够通知相关的观察者进行界面刷新。下面是 provider 更新界面的基本机制:

  • ChangeNotifier: ChangeNotifier 是 provider 的核心。它是一个轻量级的类,用于表示应用程序状态。当状态发生变化时,ChangeNotifier 会调用 notifyListeners() 方法。

  • Provider: Provider 是一个用于管理和获取状态的类。它接收一个 ChangeNotifier 的实例,并将它提供给整个应用程序的小部件树。

  • Consumer Widget: Consumer 是一个小部件,它订阅 ChangeNotifier 的变化。当 ChangeNotifier 调用 notifyListeners() 时,与 Consumer 关联的小部件会被重新构建,从而刷新界面。

相关推荐
Jinkey3 小时前
FlutterBasic - GetBuilder、Obx、GetX<Controller>、GetxController 有啥区别
android·flutter·ios
Summer不秃7 小时前
Flutter之使用mqtt进行连接和信息传输的使用案例
前端·flutter
旭日猎鹰7 小时前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
sunly_7 小时前
Flutter:AnimatedSwitcher当子元素改变时,触发动画
flutter
AiFlutter7 小时前
Flutter封装Coap
flutter
旭日猎鹰13 小时前
Flutter踩坑记录(三)-- 更改入口执行文件
flutter
旭日猎鹰13 小时前
Flutter踩坑记录(一)debug运行生成的项目,不能手动点击运行
flutter
️ 邪神13 小时前
【Android、IOS、Flutter、鸿蒙、ReactNative 】自定义View
flutter·ios·鸿蒙·reactnative·anroid
比格丽巴格丽抱1 天前
flutter项目苹果编译运行打包上线
flutter·ios
SoaringHeart1 天前
Flutter进阶:基于 MLKit 的 OCR 文字识别
前端·flutter