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 作狀態管理的基本使用啦!