Provider 是 Google I/O 2019 大会上宣布的如今官方推荐的状态管理方式,我在学习和使用以后感受很不错,由于它真的很容易上手,因此就写篇博客记录一下吧!!git
Provider GitHub 地址:github.com/rrousselGit…github
再贴两个油管上的视频(第一个只讲了 ChangeNotifierProvider,没有讲 MultiProvider 的状况):bash
www.youtube.com/watch?v=xcS…less
www.youtube.com/watch?v=d_m…ide
OK,那废话很少说。下面具体看看在 Flutter 中如何使用 Provider 作状态管理。函数
我将经过一个小 demo 来展现如何使用 Provider 作状态管理。分别使用单个 Provider 和 MultiProvider 实现(第一种将两个数据放在同一个类中,第二种是将两个数据拆分到两个不一样的类中)。post
该 demo 的功能很简单,计数 + 切换主题。仅有两个页面,两个页面共享相同的数据。学习
我已经将这个小 demo 的代码上传至 GitHub:github.com/MzoneCL/Flu… (MultiProvider) github.com/MzoneCL/Flu… (Single Provider)ui
看一下下效果图:spa
下面就来分别讲讲单个和多个 Provider 分别是如何实现上面功能的吧。
ChangeNotifierProvider 能够说是 Provider 的一种。使用它来管理只有一个共享数据类的状况比较方便。
在 pubspec.yaml 文件中添加依赖。
provider 包 pub 地址:pub.dev/packages/pr…
class DataInfo with ChangeNotifier {
int _count = 0;
ThemeData _themeData = ThemeData.light();
get count => _count;
get themeData => _themeData;
addCount() {
_count++;
notifyListeners();
}
subCount() {
_count--;
notifyListeners();
}
changeTheme() {
if (_themeData == ThemeData.light()) {
_themeData = ThemeData.dark();
} else {
_themeData = ThemeData.light();
}
notifyListeners();
}
}
复制代码
数据类须要 with ChangeNotifier 以使用 notifyListeners() 函数通知监听者以更新界面。
Provider 获取数据状态有两种方式:
不过这两种方式都须要在顶层套上 ChangeNotifierProvider():
例如,指定主题部分的代码以下:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
var dataInfo = Provider.of<DataInfo>(context);
return MaterialApp(
home: MyHomePage(),
theme: dataInfo.themeData,
);
}
}
复制代码
经过Provider.of<DataInfo>(context) 获取 DataInfo 实例,须要在 of 函数后指明具体的数据类。而后就能够直接经过 getter 访问到 themeData 了。
一样,以指定主题部分代码为例:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<DataInfo>(
builder: (context, dataInfo, _) => MaterialApp(
home: MyHomePage(),
theme: dataInfo.themeData,
),
);
}
}
复制代码
直接用 Consumer 包住须要使用共享数据的 Widget,一样的,Consumer 也要指明类型。
这个能够搭配 Stream 使用。有关 Stream 相关的讲解,这位大佬(@Vadaski)的文章很棒:juejin.im/post/5baa4b…
同上,依然是添加 provider 包。
我这里建立了两个类:CounterBloc 和 ThemeDataBloc 分别用于管理计数值和ThemeData。(从类的名字就能看出,实际上是借鉴了BLoC的思想的)
class CounterBloc {
StreamController<int> _streamController;
Stream<int> _stream;
int _count;
CounterBloc() {
_count = 0;
_streamController = StreamController.broadcast();
_stream = _streamController.stream;
}
Stream<int> get stream => _stream;
int get count => _count;
addCounter() {
_streamController.sink.add(++_count);
}
subCounter() {
_streamController.sink.add(--_count);
}
dispose() {
_streamController.close();
}
}
复制代码
因为 CounterBloc 在两个页面都有监听,因此这里的 _streamController 须要是广播类型的,须要支持多订阅,不然会报错。
class ThemeDataBloc {
StreamController<ThemeData> _streamController;
Stream<ThemeData> _stream;
ThemeData _themeData;
ThemeDataBloc() {
_themeData = ThemeData.light();
_streamController = StreamController();
_stream = _streamController.stream;
}
Stream<ThemeData> get stream => _stream;
changeTheme() {
_themeData = _themeData == ThemeData.light()?ThemeData.dark():ThemeData.light();
_streamController.sink.add(_themeData);
}
dispose() {
_streamController.close();
}
}
复制代码
因为这里使用的 Stream,就不用像上面那样 with ChangeNotifier 了。
MultiProvider 有一个必填的参数:providers,咱们须要给它传入一个 Provider 列表。 这样,咱们就能够在全部子 Widget 访问到相关共享数据了。
main() {
var counterBloc = CounterBloc();
var themeDataBloc = ThemeDataBloc();
runApp(MultiProvider(providers: [
Provider<CounterBloc>.value(value: counterBloc),
Provider<ThemeDataBloc>.value(value: themeDataBloc),
], child: MyApp()));
}
复制代码
使用 Provider.of<T>(context) 获取指定类型的数据。
仍是以指定主题的代码为例:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder(
builder: (context, snapshot) {
return MaterialApp(
home: MyHomePage(),
theme: snapshot.data,
);
},
initialData: ThemeData.light(),
stream: Provider.of<ThemeDataBloc>(context).stream,
);
}
}
复制代码
一样须要在 of 函数后面指明类型才能找到具体的 Provider。
好了,以上就是 Flutter 中 Provider 作状态管理的基本使用啦!