分类 Flutter 下的文章

近来工作中用上了Flutter,并且使用了Provider作为状态管理,确实爽,但是也踩了一下坑。

一 概述

Provider是基于InheritedWidget组件,使用观察者模式 + 生产者消费者模式,实现状态共享,简直就是为了取代StatefulWidget而存在。相关资料:

Provider的Flutter插件网址:
https://pub.dev/packages/provider

Provider的官方中文说明:
https://github.com/rrousselGit/provider/blob/master/resources/translations/zh-CN/README.md

二 总结

  1. Provider可以定义在任意地方,其状态只提供给其子Widget访问。例如,定义在App之上可实现全局的状态共享的状态,定义在页面之上可实现页面内的状态共享。
  2. Provider是利用BuildContext对象传递,实现状态共享,也因此只能实现子级可访问。
  3. Provider的子Widget(即child参数)不能赋予创建好的Widget对象,否则后面的所有孙Widget不能通过BuildContext对象获取其状态。解决方案是使用builder参数,传入构建子Widget的函数,或者child参数设置带有builder函数的Widget,例如Builder对象。简单来说,实现BuildContext对象传递,就要父级创建后,利用创建子级的builder方法,把带有父级Provider状态的BuildContext对象传递给子级。
  4. 数据变化,必然导致重绘。所以不要过于担心是否重绘,而重点关注重绘的点在哪里,如何减少重绘的Widget。重绘Widget,会向上找到最近的builder方法并执行。所以需要重绘的Widget,最好放在其builder方法内。需要变化的StatelessWidget对象,用Builder类的builder方法包裹,是个很好的做法。
  5. Provider是以类型区分数据的。如果是多个相同数据类型(例如int类型)的状态,则需要定义不同的类,且都含有该数据类型(例如int类型)的属性。
  6. 定义多个Provider,可以使用MultiProvider。
  7. 组合多个Provider对象,可以使用ProxyProvider。

三 Provider类型

一般使用ChangeNotifierProvider就可以,更多的Provider类型如下:

类型描述
Provider最基础的 provider 组成,接收一个任意值并暴露它。
ListenableProvider供可监听对象使用的特殊 provider。ListenableProvider 会监听对象,并在监听器被调用时更新依赖此对象的 widgets。
ChangeNotifierProvider为 ChangeNotifier 提供的 ListenableProvider 规范,会在需要时自动调用 ChangeNotifier.dispose。
ValueListenableProvider监听 ValueListenable,并且只暴露出 ValueListenable.value。
StreamProvider监听流,并暴露出当前的最新值。
FutureProvider接收一个 Future,并在其进入 complete 状态时更新依赖它的组件。

四 监听方式

获取Provider的状态,有以下三种方式:

  1. read,即只读。只获取状态,不进行监听。示例代码:
// 使用Provider.of,需要加上参数“listen: false”
T t = Provider.of<T>(context,listen: false));

// 使用context.read方法最简单
T t = context.read<T>();
  1. select,即只监听指定数据。指定数据有变化,才会执行重绘。注意:如果监听对象(包括List对象),只有对象的内存地址变化了,才会执行重绘。对象的属性(包括List对象的元素)变化,不会引起重绘。
// 使用Selector类,可以定义builder方法
Selector<T, R>(
  selector: (_, t) {return t.r;},
  builder: (_, r, __) {return Text('${r}');}
);
    
// 使用context.select方法最简单。如果取出的数据需要重绘,则最好用Builder类包裹一下
R r = context.select<T,R>(R cb(T value));

// Flutter Provider Selector数据更新问题优化 
// [https://blog.csdn.net/Code1994/article/details/124720388][3]
  1. watch,即监听状态的变化。状态有任何变化,都会执行重绘。
// 使用Consumer类,可以定义builder方法
Consumer<T>(
  builder: (_, t, __) {return Text('${t.r}');}
);

// 使用Provider.of方法。如果取出的数据需要重绘,则最好用Builder类包裹一下
T t = Provider.of<T>(context);

// 使用context.watch方法最简单。如果取出的数据需要重绘,则最好用Builder类包裹一下
T t = context.watch<T>();