provider的使用以及优化心得
provider基于观察者(发布-订阅)模式。
provider 的组成以及作用:
Listenable
作为发布者的角色,需要和Provider配合使用,相关组件有:
- Listenable 抽象类,需要自己继承,实现addListener removeListener 函数 以及 维护listeners列表。
- ChangeNotifier 继承于Listenable 内部提供实现好的发布者功能,常于ChangeNotifierProvider相结合使用。
- ValueNotifier 继承于ChangeNotifier,和ValueListenableProvider配合使用, 区别在于,内部提供了单一变量的状态变化自动通知,不需要自行调用notifyListeners()。缺点在于牺牲了灵活性,仅对于内部value的set方法做了监听。
Consumer ConsumerX
作为订阅者的角色,也是一个便捷构造器,内部帮我们调用Provider.of(context)
来获取ChangeNotifier
,以及将将自身加入订阅者中。
也可以不使用Consumer,直接在widget中使用Provder.of(conext) 是一样的效果。
Provider
Provider 基于一个DelegateWidget来实现,它提供一对creat和dispose,来让我们管理数据的生命周期,所以通过Provider我们不用担心内存释放以及数据请求等问题(这种场景下完全可以替代StatefulWidget)。
除此之外,Provider还提供了不同的子类来适应各种场景:
- Provider :不仅提供creat和dispose,还拥有Inherited特性,它继承于InheritedProvider,其内部基于InheritedWidget实现。但是Provider并不具备刷新子部件的功能,仅能用于提供固定数据。
- ListenableProvider :一个偏底层且拥有刷新子部件功能的Provider,需要提供一个继承于Listenable的发布者进行消息发布。
- ChangeNotifierProvider : 继承于ListenableProvider,需要和ChangeNotifier结合使用:
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<CustomChangeNotifier>(
create: (BuildContext context) {
return CustomChangeNotifier();
},
child: Consumer<CustomChangeNotifier>(
builder: (builder: (context, notifier, _) {
return new Column(
children: <Widget>[
new Expanded(child: new Center(child: new Text(notifier.count.toString()))),
new Center(
child: new FlatButton(
onPressed: () {
notifier.add();
},
color: Colors.blue,
child: new Text("+")),
)
],
);
}
- FutureProvider:支持异步,需要提供一个Future,等待Future完成后,自动刷新子部件。
- StreamProvider:支持异步,需要提供一个Steam充当发布者,当有流输入后,刷新子部件。
- ValueListenableProvider : 支持单一变量,需要提供一个ValueNotifier对数据进行包装,
- MultiProvider 针对Provider进行组合:
void main() {
final counter = CounterModel();
final textSize = 48;
runApp(
MultiProvider(
providers: [
Provider.value(value: textSize),
ChangeNotifierProvider.value(value: counter)
],
child: MyApp(),
),
);
}
- ProxyProvider ProxyProviderX :结合MultiProvider可以相应其他Provider的更新:
比如一个图片上传,我们可以通过一个ChangeNotifierProvider进行图片上传获取URL,然后通过ProxyProvider拦截上传后的URL,把上传到服务器中。
/// 官方示例
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Counter()),
ProxyProvider<Counter, Translations>(
update: (_, counter, __) => Translations(counter.value),
),
],
child: Foo(),
);
}
class Translations {
const Translations(this._value);
final int _value;
String get title => 'You clicked $_value times';
}
Selector
在业务层的细粒度刷新下,光靠Provider控制是远远不够的,这时候,就需要Selector。 Selector和Consumer扮演一样的角色—订阅者,使用起来也和Consummer差不多,只不过多一个selector。
实现原理:
Selector继承于SingleChildStatefulWidget,为了防止重复构建,重写了buildWithChild并对widget进行了缓存,通过selector选择器传回来的数据进行equals判断,当然还有_shouldRebuild可以进行强制构建。
如何使用:
Selector接受两个泛型变量 Selector<T, S>,通过Provider.of(context),获取发布者,同时S会作为视图是否需要重新构建的数据类型。
Selector<Translations, String>(
selector: (context, provider) {
return provider.title; // 将改变后的数据返回
},
builder: (context, value, child) {
print("刷新了");
return Text("$value"); // 重新构建
},
),
优化心得:
局部刷新控制
Provider具有非常好的生命周期控制,控制好刷新范围,尽量支持最小化原则,这样不仅对于刷新速度有所提升,在数据控制方面例如内存的申请和释放频次,也有更好的优化。
善用懒加载
这里的懒加载是指的是create的延迟调用,通常create中还会进行网络请求、数据初始化等操作,且在provider的整个生命周期中,只调用一次,如果想要关闭懒加载,需要将lazy置为false。
Dispose释放
provider对于内存控制很看重,除了各类Provider的dispose支持,ChangeNotifier也提供了dispose,
转载自:https://juejin.cn/post/6944982006618914823