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 关联的小部件会被重新构建,从而刷新界面。

相关推荐
Zsnoin能6 小时前
flutter国际化、主题配置、视频播放器UI、扫码功能、水波纹问题
flutter
早起的年轻人7 小时前
Flutter CupertinoNavigationBar iOS 风格导航栏的组件
flutter·ios
HappyAcmen7 小时前
关于Flutter前端面试题及其答案解析
前端·flutter
coooliang16 小时前
Flutter 中的单例模式
javascript·flutter·单例模式
coooliang16 小时前
Flutter项目中设置安卓启动页
android·flutter
JIngles12316 小时前
flutter将utf-8编码的字节序列转换为中英文字符串
java·javascript·flutter
B.-19 小时前
在 Flutter 中实现文件读写
开发语言·学习·flutter·android studio·xcode
freflying11191 天前
使用jenkins构建Android+Flutter项目依赖自动升级带来兼容性问题及Jenkins构建速度慢问题解决
android·flutter·jenkins
机器瓦力1 天前
Flutter应用开发:对象存储管理图片
flutter
江上清风山间明月2 天前
Flutter最简单的路由管理方式Navigator
android·flutter·ios·路由·页面管理·navigator