likes
comments
collection
share

provider的使用以及优化心得

作者站长头像
站长
· 阅读数 4

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
评论
请登录