在flutter中Provider是比较常用的Widget, Provider通常用来管理value的生命周期,通过Create和Dispose,它们是成对出现的,可以在Create进行value的初始化操作,在dispose进行value的释放操作。使用Provider可以避免一些琐碎的操作,比如实例化一个BLoC操作,事实上,Provider等效于状态管理类State.initState和State.dispose的组合,Create只会在State.initState中调用一次。我们不能直接使用InheritedWidget,因为它需要构造函数已初始化,并且为最终值。
下面是一个例子,初始化一次Model,并且当Provider从数中被移除的时候,dispose会被调用:
class Model { void dispose() {} } class Stateless extends StatelessWidget { @override Widget build(BuildContext context) { return Provider<Model>( create: (context) => Model(), dispose: (context, value) => value.dispose(), child: ..., ); } }
'create'回调是懒加载式的,只有第一次获取value的时候才会被调用,而不是在provider第一次被插入到widget树中的时候调用。当然,这个行为是可以被禁用的,通过设置Provider的"lazy:false"。
Provider提供了几种获取实例的方式:
Provider({ Key key, @required Create<T> create, Dispose<T> dispose, bool lazy, TransitionBuilder builder, Widget child, }) : assert(create != null), super( key: key, lazy: lazy, builder: builder, create: create, dispose: dispose, debugCheckInvalidValueType: kReleaseMode ? null : (T value) => Provider.debugCheckInvalidValueType?.call<T>(value), child: child, );
这中方式可以创建一个value,并且存储它,和暴露给它的后代,value可以选择性在dispose回调中释放,dispose会在Provider从树中移出的时候被调用。
Provider.value({ Key key, @required T value, UpdateShouldNotify<T> updateShouldNotify, TransitionBuilder builder, Widget child, }) : assert(() { Provider.debugCheckInvalidValueType?.call<T>(value); return true; }()), super.value( key: key, builder: builder, value: value, updateShouldNotify: updateShouldNotify, child: child, );
这中方式暴露一个已存在的不用释放的值,其中可选参数updateShouldNotify可以用来控制是否需要重绘,当value值改变的时候,一般来说(previous, next) => previous != next时,会重绘。
static T of<T>(BuildContext context, {bool listen = true})
这个静态方法可以获取widget树中最近的Provider<T>,并且返回它的值。如果listen为true,那么随后value发生的变化会触发State.build和State.didChangeDependencies. 如果需要在initState中调用Provider.of或者如下调用Provider的create方法时,需要设置listen为false:
Provider( create: (context) { return Model(Provider.of<Something>(context, listen: false)), }, )
如果试图在widget树外监听Provider暴露出来的值,也需要设置listen为false,比如在点击事件中调用,否则的话可能会导致跟事件句柄关联的wieget重建,如果这个widget并不在意这个value的话。
讲到Provider的用法,我们还需了解MultiProvider, 一般来说,MultiProvider用于单个线性widget树中,可以增强可读性和减少多层嵌套的样板代码,如下:
Provider<Something>( create: (_) => Something(), child: Provider<SomethingElse>( create: (_) => SomethingElse(), child: Provider<AnotherThing>( create: (_) => AnotherThing(), child: someWidget, ), ), ),
使用MultiProvider则可以简化为:
MultiProvider( providers: [ Provider<Something>(create: (_) => Something()), Provider<SomethingElse>(create: (_) => SomethingElse()), Provider<AnotherThing>(create: (_) => AnotherThing()), ], child: someWidget, )
这两种方式是等效的。下面我们看下MultiProvider的构造方法:
MultiProvider({ Key key, @required List<SingleChildWidget> providers, Widget child, TransitionBuilder builder, }) : assert(providers != null), super( key: key, children: providers, child: builder != null ? Builder( builder: (context) => builder(context, child), ) : child, );
参数builder是个语法糖,用来获取BuildContext,可以用它来获取创建的providers:
MultiProvider( providers: [ Provider<Something>(create: (_) => Something()), Provider<SomethingElse>(create: (_) => SomethingElse()), Provider<AnotherThing>(create: (_) => AnotherThing()), ], builder: (context, child) { final something = context.watch<Something>(); return Text('$something'); }, ) 等效于 MultiProvider( providers: [ Provider<Something>(create: (_) => Something()), Provider<SomethingElse>(create: (_) => SomethingElse()), Provider<AnotherThing>(create: (_) => AnotherThing()), ], child: Builder( builder: (context) { final something = context.watch<Something>(); return Text('$something'); }, ), )
上面两种方式是等效的。当providers中存在provider有child widget的,将会被忽略,下面两种方式是等效的:
MultiProvider( providers: [ Provider<Something>(create: (_) => Something(), child: SomeWidget()), ], child: Text('Something'), ) 等效: MultiProvider( providers: [ Provider<Something>(create: (_) => Something()), ], child: Text('Something'), )