Flutter 简介 Provider
什么是Provider?
Provider是 Flutter 官方推荐的状态管理方式之一,它的特点是:不复杂,好理解,代码量不大的情况下,可以方便组合和控制刷新颗粒度。 Provider是 是可继承组件 InheritedWidget 的一个外包,但它允许向外暴露任何状态对象,包括了 bloC、stream、future 等等。
Provider 分类
- 基础 Provider
- ChangeNotifierProvider
- StreamProvider
- FutureProvider
- ValueListenableProvider
- MultiProvider
- ProxyProvider
基本使用
基础 Provider
Provider 无法知道我们更新过了 Model 的属性,也无法告知 Widget 响应改变从而作出更新
class UserModel {
int counter = 0;
Future<void> increment() async {
await Future.delayed(Duration(seconds: 2));
counter++;
print(counter);
}
dispose() {}
}
class DemoWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Provider<UserModel>(
create: (_) => UserModel(),
dispose: (_, v) => v.dispose(),
child: Scaffold(
appBar: AppBar(
title: Text('测试'),
),
body: Consumer<UserModel>(builder: (_, value, child) => Center(
child: FlatButton(
color: Colors.lightBlue,
child: Text('${value.counter}'),
onPressed: () {
value.increment();
},
),
),
),
),
);
}
}
ChangeNotifierProvider
要实现上面的 Model 改变且展现在 Widget 上,使用这个 Provider 就可以,只需要稍加改造:
class UserModel extends ChangeNotifier {
int counter = 0;
increment() {
counter++;
print(counter);
notifyListeners();
}
}
class DemoWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<UserModel>(
create: (_) => UserModel(),
child: Scaffold(
appBar: AppBar(
title: Text('测试'),
),
body: Column(
children: [
Consumer<UserModel>(builder: (_, value, child) => Text('${value.counter}'),),
Builder(builder: (context){
return FlatButton(
color: Colors.lightBlue,
child: Text('点我'),
onPressed: () {
final model = Provider.of<UserModel>(context);
model.increment();
},
);
}),
],
),
),
);
}
}
看起来与前面我们自己做的 Provider 很类似,这里当然也会遇到我们前面遇到的问题,为了避免这样的事情发生,我们可以使用如下的语法让模型不再注册重新构建的监听:
final model = Provider.of<MyModel>(context, listen: false);
StreamProvider
StreamProvider 给人的第一印象是:好像并不那么有必要。毕竟在 Flutter 中,我们可以使用常规的 StreamBuilder 来订阅流信息:
class DemoWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: currentStream,
builder: (_, AsyncSnapshot<UserModel> snapshot) {
return Text('${snapshot.data.counter}');
},
);
}
}
而如果想使用 Provider 来完成,我们可以在根结点通过 StreamProvider 暴露出这个流:
StreamProvider<UserModel>.value(
stream: currentStream,
child: DemoWidget(...),
}
然后在子组件中就可以像其他 Provider 那样使用了:
@override
Widget build(BuildContext context) {
return Consumer<UserModel>(
builder: (context, value, child) => Text(value.counter),
);
}
除了能让组件代码更加清晰,它也可以抽象并过滤掉数据是否是来自于流的这一信息。
FutureProvider
和上面的例子类似,FutureProvider 是在组件中使用 FutureBuilder 的替换方案。这里是一段代码示例:
FutureProvider<UserModel>.value(
value: currentUser,
child: DemoWidget(...),
);
我们可以使用 Consumer 来在子元素中获取到这个值。
ValueListenableProvider
ValueListenable 是 ValueNotifier 类实现的 Dart 接口,它可以在自身接收的参数发生变化的时候通知监听者
class CustomModel {
final ValueNotifier<int> counter = ValueNotifier(0);
}
如果我们使用的是复杂类型的参数,ValueNotifier 将会使用 == 操作符来确认是否参数值变化了。 创建一个基础 Provider 用来容纳主模块,它同时还有一个 Consumer,以及一个用于监听 counter 属性的嵌套的 ValueListenableProvider:
Provider<CustomModel>(
builder: (context) => MyModel(),
child: Consumer<CustomModel>(builder: (context, value, child) {
return ValueListenableProvider<int>.value(
value: value.counter,
child: DemoWidget(...)
}
}
}
注意:嵌套的 provider 的类型是 int。当然你的代码也会有其他可能的类型。如果有多个 Provider 都注册为同一类型,那么 Provider 将会返回最“近”的一个(距离最近的父级组件)。
如下代码可以监听任意子组件的 counter 属性:
class DemoWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<int>(
builder: (context, value, child) {
return Text(value.toString());
},
);
}
}
如下代码可以更新其他组件的 counter 属性。注意:我们首先需要获取原始的 CustomModel 实例。
class OtherWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FlatButton(
child: Text('点我'),
onPressed: () {
final model = Provider.of<CustomModel>(context);
model.counter.value++;
},
);
}
}
-
MultiProvider 如果我们应用了多个 Provider 组件,可以使用 MultiProvider,它允许我们在同一层级声明所有的 provider。但这仅仅是一种语法糖,它们实际上还是嵌套的,但是用起来一目了然,所以我们用的会比较多
MultiProvider( providers: [ Provider<ModelA>.value(value: '1'), Provider<ModelB>.value(value: '2'), Provider<ModelC>.value(value: '3'), ], child: MaterialApp(...), )
- ProxyProvider
当我们需要建立有赖于其他服务的根服务集时,就很需要 ProxyProvider 了,第一个泛型参数是 ProxyProvider 的类型,第二个是它需要返回的类型。
MultiProvider ( providers: [ Provider<ModelA> ( builder: (context) => ModelA(), ), ProxyProvider<ModelA, ModelB>( builder: (context, value, previous) => ModelB(value), ), ], child: App(...), )
同时监听多个 Provider
如果我们想要一个组件同时监听多个 Provider,并且当任意一个被监听的 Provider 发生变化时都要重构组件,那就可以使用 Consumer 组件,最多可以监听 6 个 Provider
Consumer2<UserModel, int>(
builder: (context, value, value2, child) {
//value 是 MyModel 类型
//value2 是 int 类型
},
);