回顾
Flutter State Management状态管理全面分析上期我们对Flutter的状态管理有了全局的认知,也知道了如何分辨是非好坏,不知道也没关系哦,我们接下来还会更加详细的分析,通过阅读Provider源码,来看看框架到底如何组织的,是如何给我们提供便利的。
本期内容
通过官方我们已经知道其实Provider就是对InheritedWidget的包装,只是让InheritedWidget用起来更加简单且高可复用。我们也知道它有一些缺点,如
- 容易造成不必要的刷新
- 不支持跨页面(route)的状态,意思是跨树,如果不在一个树中,我们无法获取
- 数据是不可变的,必须结合StatefulWidget、ChangeNotifier或者Steam使用
我特别想弄明白,这些缺点在Provider的设计中是如何规避的,还有一个是Stream不会主动的close掉流的通道,不得不结合StatefulWidget使用,而Provider提供了dispose回调,你可以在该函数中主动关闭,好厉害,如何做到的呢?带着这些疑问,我们去寻找答案
如何使用
我们先来使用它,然后在根据用例分析源码,找到我们想要的答案,先看一个简单的例子
step 1
第一步定义一个ChangeNotifier,来负责数据的变化通知
class Counter with ChangeNotifier { int _count = 0; int get count => _count; void increment() { _count++; notifyListeners(); } }
step 2
第二步,用ChangeNotifierProvider来订阅Counter,不难猜出,ChangeNotifierProvider肯定是InheritedWidget的包装类,负责将Counter的状态共享给子Widget,我这里将ChangeNotifierProvider放到了Main函数中,并在整个Widget树的顶端,当然这里是个简单的例子,我这么写问题不大,但你要考虑,如果是特别局部的状态,请将ChangeNotifierProvider放到局部的地方而不是全局,希望你能明白我的用意
void main() { runApp( /// Providers are above [MyApp] instead of inside it, so that tests /// can use [MyApp] while mocking the providers MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => Counter()), ], child: MyApp(), ), ); } 复制代码
step 3
第三步,接收数据通过Consumer,Consumer是个消费者,它负责消费ChangeNotifierProvider生产的数据
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return const MaterialApp( home: MyHomePage(), ); } } class MyHomePage extends StatelessWidget { const MyHomePage({Key key}) : super(key: key); @override Widget build(BuildContext context) { print('MyHomePage build'); return Scaffold( appBar: AppBar( title: const Text('Example'), ), body: Center( child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text('You have pushed the button this many times:'), /// Extracted as a separate widget for performance optimization. /// As a separate widget, it will rebuild independently from [MyHomePage]. /// /// This is totally optional (and rarely needed). /// Similarly, we could also use [Consumer] or [Selector]. Consumer<Counter>( builder: (BuildContext context, Counter value, Widget child) { return Text('${value.count}'); }, ), OtherWidget(), const OtherWidget2() ], ), ), floatingActionButton: FloatingActionButton( /// Calls `context.read` instead of `context.watch` so that it does not rebuild /// when [Counter] changes. onPressed: () => context.read<Counter>().increment(), tooltip: 'Increment', child: const Icon(Icons.add), ), ); } }
通过这个例子,可以判断出Provider封装的足够易用,而且Counter作为Model层使用的with ChangeNotifier 而不是extends ,所以说侵入性也比较低,感觉还不错,那么InheritedWidget的缺点它规避了吗?
- 容易造成不必要的刷新(解决了吗?)
我们多加两个子WIdget进去,排在Consumer的后面,OtherWidget什么都不干,不去订阅Counter,OtherWidget2通过context.watch().count函数监听而不是Consumer,来看下效果一样不,然后在build函数中都加入了print
class OtherWidget extends StatelessWidget { const OtherWidget({Key key}) : super(key: key); @override Widget build(BuildContext context) { print('OtherWidget build'); // Provider.of<Counter>(context); return Text( /// Calls `context.watch` to make [MyHomePage] rebuild when [Counter] changes. 'OtherWidget', style: Theme.of(context).textTheme.headline4); } } class OtherWidget2 extends StatelessWidget { const OtherWidget2({Key key}) : super(key: key); @override Widget build(BuildContext context) { print('OtherWidget2 build'); return Text( /// Calls `context.watch` to make [MyHomePage] rebuild when [Counter] changes. '${context.watch<Counter>().count}', style: Theme.of(context).textTheme.headline4); } }
项目运行看下效果,跑起来是这样的
print日志
点击刷新后
分析结论如下:
- Consumer、context.watch都可以监听Counter变化
- Consumer只会刷新自己
- context.watch所在子widget不管是否是const都被重建后刷新数据
- OtherWidget并没有被重建,因为它没有订阅Counter
局部刷新确实实现了但要通过Consumer,第二个问题不支持跨页面(route)的状态,这个可以确定的说不支持,第三个问题数据是不可变的(只读),经过这个例子可以分辨出数据确实是可变的对吧,那么数据是如何变化的呢?留个悬念,下面分析源码中来看本质。
当然要想更完整的理解ChangeNotifier、ChangeNotifierProvider、Consumer的关系 请看图
设计模式真是无处不在哈,ChangeNotifier与ChangeNotifierProvider实现了观察者模式,ChangeNotifierProvider与Consumer又实现了生产者消费者模式,这里不具体聊这俩个模式,如果还不了解,请你自行搜索学习哦。下面直接源码分析
源码分析
ChangeNotifier
在包package:meta/meta.dart下,是flutter sdk的代码,并不属于Provider框架的一部分哦,通过下方代码可以看出,这是一个标准的观察者模型,而真正的监听者就是typedef VoidCallback = void Function(); 是dart.ui包下定义的一个函数,没人任何返回参数的函数。ChangerNotifier实现自抽象类Listenable,通过源码的注释我们看到Listenable是一个专门负责维护监听列表的一个抽象类。
ChangeNotifierProvider
class ChangeNotifierProvider<T extends ChangeNotifier> extends ListenableProvider<T> { static void _dispose(BuildContext context, ChangeNotifier notifier) { notifier?.dispose(); } /// 使用`create`创建一个[ChangeNotifier] /// 当ChangeNotifierProvider从树中被移除时,自动取消订阅通过 /// notifier?.dispose(); ChangeNotifierProvider({ Key key, @required Create<T> create, bool lazy, TransitionBuilder builder, Widget child, }) : super( key: key, create: create, dispose: _dispose, lazy: lazy, builder: builder, child: child, ); /// 生成一个已存在ChangeNotifier的Provider ChangeNotifierProvider.value({ Key key, @required T value, TransitionBuilder builder, Widget child, }) : super.value( key: key, builder: builder, value: value, child: child, ); }
分析下构造
- Create create 是个通用函数typedef Create = T Function(BuildContext context)用于创建T类,这里负责创建ChangeNotifier
- bool lazy 是否懒加载
- TransitionBuilder builder 当builder存在时将不会用child做为子Widget,追踪到源码实现可以看到如下图
- Widget child builder不存在时就用child
继承自ListenableProvider,来继续分析它的源码
class ListenableProvider<T extends Listenable> extends InheritedProvider<T> { /// 使用 [create] 创建一个 [Listenable] 订阅它 /// [dispose] 可以选择性的释放资源当 [ListenableProvider] 被移除树的时候 /// [create] 不能为空 ListenableProvider({ Key key, @required Create<T> create, Dispose<T> dispose, bool lazy, TransitionBuilder builder, Widget child, }) : assert(create != null), super( key: key, startListening: _startListening, create: create, dispose: dispose, debugCheckInvalidValueType: kReleaseMode ? null : (value) { if (value is ChangeNotifier) { // ignore: invalid_use_of_protected_member ... } }, lazy: lazy, builder: builder, child: child, ); /// 生成已存在 [Listenable] 的Provider ListenableProvider.value({ Key key, @required T value, UpdateShouldNotify<T> updateShouldNotify, TransitionBuilder builder, Widget child, }) : super.value( key: key, builder: builder, value: value, updateShouldNotify: updateShouldNotify, startListening: _startListening, child: child, ); static VoidCallback _startListening( InheritedContext<Listenable> e, Listenable value, ) { value?.addListener(e.markNeedsNotifyDependents); return () => value?.removeListener(e.markNeedsNotifyDependents); } }
- Listenable 上面已经分析,它是负责管理观察者列表的抽象
- 它比子类ChangeNotifierProvider多了一个构造参数dispose,这个函数是typedef Dispose = void Function(BuildContext context, T value); 是个回调,应该是当页面被销毁时触发(等再深入了源码才能认证,目前只是猜测,我们继续看)
又继承自InheritedProvider ,别放弃,来跟我一起往下看
class InheritedProvider<T> extends SingleChildStatelessWidget { /// 创建数据value并共享给子Widget /// 当 [InheritedProvider] 从树中被释放时,将自动释放数据value InheritedProvider({ Key key, Create<T> create, T update(BuildContext context, T value), UpdateShouldNotify<T> updateShouldNotify, void Function(T value) debugCheckInvalidValueType, StartListening<T> startListening, Dispose<T> dispose, TransitionBuilder builder, bool lazy, Widget child, }) : _lazy = lazy, _builder = builder, _delegate = _CreateInheritedProvider( create: create, update: update, updateShouldNotify: updateShouldNotify, debugCheckInvalidValueType: debugCheckInvalidValueType, startListening: startListening, dispose: dispose, ), super(key: key, child: child); /// 暴漏给子孙一个已存在的数据value InheritedProvider.value({ Key key, @required T value, UpdateShouldNotify<T> updateShouldNotify, StartListening<T> startListening, bool lazy, TransitionBuilder builder, Widget child, }) : _lazy = lazy, _builder = builder, _delegate = _ValueInheritedProvider( value: value, updateShouldNotify: updateShouldNotify, startListening: startListening, ), super(key: key, child: child); InheritedProvider._constructor({ Key key, _Delegate<T> delegate, bool lazy, TransitionBuilder builder, Widget child, }) : _lazy = lazy, _builder = builder, _delegate = delegate, super(key: key, child: child); final _Delegate<T> _delegate; final bool _lazy; final TransitionBuilder _builder; @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); _delegate.debugFillProperties(properties); } @override _InheritedProviderElement<T> createElement() { return _InheritedProviderElement<T>(this); } @override Widget buildWithChild(BuildContext context, Widget child) { assert( _builder != null || child != null, '$runtimeType used outside of MultiProvider must specify a child', ); return _InheritedProviderScope<T>( owner: this, child: _builder != null ? Builder( builder: (context) => _builder(context, child), ) : child, ); } }
构造中多出来的参数
- T update(BuildContext context, T value) 该函数返回数据变更值value,具体实现在_CreateInheritedProvider类中,说白了InheritedProvider是个无状态组件对吗?那么它要变更状态肯定要依赖于别人,而它创建出一个_CreateInheritedProvider类,_CreateInheritedProvider是_Delegate的实现类,_Delegate就是一个状态的代理类,来看下_Delegate具体实现
@immutable abstract class _Delegate<T> { _DelegateState<T, _Delegate<T>> createState(); void debugFillProperties(DiagnosticPropertiesBuilder properties) {} } abstract class _DelegateState<T, D extends _Delegate<T>> { _InheritedProviderScopeElement<T> element; T get value; D get delegate => element.widget.owner._delegate as D; bool get hasValue; bool debugSetInheritedLock(bool value) { return element._debugSetInheritedLock(value); } bool willUpdateDelegate(D newDelegate) => false; void dispose() {} void debugFillProperties(DiagnosticPropertiesBuilder properties) {} void build(bool isBuildFromExternalSources) {} }
这是用到了委托模式,这里就有点类似StatefulWidget和State的关系,同样的_DelegateState提供了类似生命周期的函数,如willUpdateDelegate更新新的委托,dispose注销等
- UpdateShouldNotify updateShouldNotify, void Function(T value) debugCheckInvalidValueType, StartListening startListening, Dispose dispose, 这些函数全部交给了委托类
- 最关键的实现来了,到目前位置还没看到InheritedWidget的逻辑对吧,它来了Widget buildWithChild(BuildContext context, Widget child),我们传入的Widget就被叫_InheritedProviderScope的类给包裹了,看下源码
class _InheritedProviderScope<T> extends InheritedWidget { _InheritedProviderScope({ this.owner, @required Widget child, }) : super(child: child); final InheritedProvider<T> owner; @override bool updateShouldNotify(InheritedWidget oldWidget) { return false; } @override _InheritedProviderScopeElement<T> createElement() { return _InheritedProviderScopeElement<T>(this); } }