Flutter 狀態管理之 Provider

前言:

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 分別是如何實現上面功能的吧。

一. 單個 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 獲取數據狀態有兩種方式:

  1. 使用 Provider.of<T>(context)
  2. 使用 Consumer

不過這兩種方式都須要在頂層套上 ChangeNotifierProvider():

1. 使用 Provider.of<T>(context)

例如,指定主題部分的代碼以下:

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 了。

2.使用 Consumer

一樣,以指定主題部分代碼爲例:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<DataInfo>(
      builder: (context, dataInfo, _) => MaterialApp(
            home: MyHomePage(),
            theme: dataInfo.themeData,
          ),
    );
  }
}
複製代碼

直接用 Consumer 包住須要使用共享數據的 Widget,一樣的,Consumer 也要指明類型。

二. 使用 MultiProvider 管理多個共享數據類

這個能夠搭配 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

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

相關文章
相關標籤/搜索