在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'),
)