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

相关文章
相关标签/搜索