Flutter状态管理Provider(一) 简易使用

前言

若是对Provider的使用已经很熟练,能够跳过这部份内容,直接看过程分析部分 Flutter状态管理Provider(二)过程分析html

Demo代码仓库 入口:main_provider.dartgit

简介

官网介绍

[Provider](https://github.com/rrousselGit/provider)

简单翻译来讲,Provider:依赖注入与状态管理的结合,而且对外提供Widget使用。使用widget代替纯Dart对象,好比说Stream。由于widget简单,强大且可扩展。使用Provider,能够保证:可维护性(强制的单向数据流),可测试性,健壮性等等。

A mixture between dependency injection (DI) and state management, built with widgets for widgets.It purposefully uses widgets for DI/state management instead of dart-only classes like Stream.

The reason is, widgets are very simple yet robust and scalable.

By using widgets for state management, provider can guarantee:
复制代码
  • maintainability, through a forced uni-directional data-flow
  • testability/composability, since it is always possible to mock/override a value
  • robustness, as it is harder to forget to handle the update scenario of a model/widget

什么是状态?什么是状态管理?

若是说页面是静态,拿到数据,渲染完就完事了.压根就不须要状态管理。 不幸的是,页面不可能全是静态。页面须要响应数据变化(网络数据?用户操做产生的数据?),更新UI。同时,数据变化的影响,不单单是组件内,还有多是页面内其余组件,甚至于应用内其余页面github

数据即为状态。从数据变化到通知界面更新的过程,咱们称之为状态管理 状态管理要尽量的把这个过程独立出来,让动态界面如同静态页面通常简单。redux

有哪些状态管理的方式/库

- setState
- FutureBuilder/StreamBuilder/BLoc
- Provider/ScopedModel
- redux/Fish-redux
复制代码

简易版Provider实现

有了状态管理的介绍,咱们能够参考Provider,经过手上现有的组件,实现一个简易版的Provider。要用到的系统组件:bash

  • setState:StatefulWidget现成的,刷新UI的办法
  • InheritedWidget:一个基础widget,让widget树具有从上而下传递数据的能力。同时数据变化能够引发依赖它的widget从新构建。
  • ChangeNotifier:观察者模式的代码模型。

setState

状态管理最基础的一个实现markdown

class _SetStateDemoWidgetState extends State<SetStateDemoWidget> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(setStateDemoTitle),),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text("计数:$count"),
            RaisedButton(
              child: Text("increment"),
              onPressed: () => setState(() => count++),
            )
          ],
        ),
      ),
    );
  }
}
复制代码

InheritedWidget

用CountProvider继承InheritedWidget来保存数据。 经过context.getElementForInheritedWidgetOfExactType拿到CountProvider中的数据。 这个context必定要注意,只能用CountProvider子widget的BuildContext。CountProvider的查找是经过context往上的。 注意这个地方,咱们只是单纯的拿数据,尚未用到InheritedWidget能够控制子widget从新构建的功能。网络

///首先咱们要有个地方存放咱们的数据
class CountProvider extends InheritedWidget {
  final int count;

  CountProvider({Key key, this.count, Widget child})
      : super(key: key, child: child);

  @override
  bool updateShouldNotify(CountProvider old) {
    return true;
  }
}

class ProviderDemoWidget1 extends StatefulWidget {
  ProviderDemoWidget1({Key key}) : super(key: key);

  @override
  _ProviderDemoWidget1State createState() => _ProviderDemoWidget1State();
}

class _ProviderDemoWidget1State extends State<ProviderDemoWidget1> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(providerDemoTitle1),
      ),
      body: CountProvider(
        count: _count,
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Builder(
                builder: (context2) {
                  
                  CountProvider provider = context2
                      .getElementForInheritedWidgetOfExactType<CountProvider>()
                      .widget;
                  return Text("计数:${provider.count}");
                },
              ),

              /// 读取和显示计数
              RaisedButton(
                child: Text("increment"),
                onPressed: () => setState(() => _count++),
              ),
              Text(providerDemoIntroduction1),
            ],
          ),
        ),
      ),
    );
  }
}
复制代码

Provider(InheritedWidget + ChangeNotifier)

咱们先看效果图,咱们看到有三种场景。只有依赖的组件更新了UI。app

  • CountModel封装示例1中的count,继承ChangeNotifier,具有notify能力。
class CountModel extends ChangeNotifier {
  int count;

  CountModel(this.count);

  void increment() {
    count++;
    notifyListeners();
  }
}
复制代码
  • Provider继承InheritedWidget,封装两种访问: context.dependOnInheritedWidgetOfExactType和context.getElementForInheritedWidgetOfExactType. 当InheritedWidget从新构建是,前者widget会从新构建,后者则不会。
class Provider<T extends ChangeNotifier> extends InheritedWidget {
  final T model;

  Provider({Key key, this.model, Widget child}) : super(key: key, child: child);

  static T of<T extends ChangeNotifier>(BuildContext context, bool depend) {
    if (depend) {
      return context.dependOnInheritedWidgetOfExactType<Provider>().model;
    } else {
      Provider provider =
          context.getElementForInheritedWidgetOfExactType<Provider>().widget;
      return provider.model;
    }
  }

  @override
  bool updateShouldNotify(Provider old) {
    return true;
  }
}
复制代码
  • ChangeNotifierProvider经过监听ChangeNotifier,将setState封装在其内部。总之,把状态管理(数据修改和Widget刷新)封装在自定义的对象内。外部控件不须要再关心状态管理的细节了。
class ChangeNotifierProvider<T extends ChangeNotifier> extends StatefulWidget {
  final Widget child;

  final T model;

  ChangeNotifierProvider({this.child, this.model});

  @override
  _ChangeNotifierProviderState createState() => _ChangeNotifierProviderState();
}

class _ChangeNotifierProviderState extends State<ChangeNotifierProvider> {
  _ChangeNotifierProviderState();

  _update() {
    setState(() => {});
  }

  @override
  void initState() {
    super.initState();
    widget.model.addListener(_update);
  }

  @override
  void dispose() {
    super.dispose();
    widget.model.removeListener(_update);
  }

  @override
  Widget build(BuildContext context) {
    return Provider(
      model: widget.model,
      child: widget.child,
    );
  }
}
复制代码
  • 有了 CountModel,Provider,ChangeNotifierProvider,简易版状态管理Provider库也就写好了,下面用起来:
class ProviderDemoWidget3 extends StatefulWidget {
  ProviderDemoWidget3({Key key}) : super(key: key);
  
  @override
  _ProviderDemoWidget3State createState() => _ProviderDemoWidget3State();
}

class _ProviderDemoWidget3State extends State<ProviderDemoWidget3> {
  CountModel _countModel = CountModel(0);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(providerDemoTitle3), ),
      body: ChangeNotifierProvider<CountModel>(
        model: _countModel,
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Builder(builder: (context1) {
                return Text(
                    "计数:${Provider.of<CountModel>(context1, true).count}(有依赖状况)");
              }),
              Builder(builder: (context2) {
                return Text(
                    "计数:${Provider.of<CountModel>(context2, false).count}(无依赖状况)");
              }),
              RaisedButton(
                  child: Text("increment"),
                  onPressed: () => _countModel.increment()),
              Text(providerDemoIntroduction3),
            ],
          ),
        ),
      ),
    );
  }
}
复制代码

基于上个版本的通用泛型封装

上一个示例中CountModel不具备通用性,因此咱们写一个泛型版本ide

class Provider<T extends ChangeNotifier> extends InheritedWidget {
  final T model;

  Provider({Key key, this.model, Widget child}) : super(key: key, child: child);

  static T of<T extends ChangeNotifier>(BuildContext context, bool depend) {
    if (depend) {
      return context.dependOnInheritedWidgetOfExactType<Provider>().model;
    } else {
      Provider provider =
          context.getElementForInheritedWidgetOfExactType<Provider>().widget;
      return provider.model;
    }
  }

  @override
  bool updateShouldNotify(Provider old) {
    return true;
  }
}
复制代码
class ChangeNotifierProvider<T extends ChangeNotifier> extends StatefulWidget {
  final Widget child;

  final T model;

  ChangeNotifierProvider({this.child, this.model});

  @override
  _ChangeNotifierProviderState createState() => _ChangeNotifierProviderState();
}

class _ChangeNotifierProviderState extends State<ChangeNotifierProvider> {
  _ChangeNotifierProviderState();

  _update() {
    setState(() => {});
  }

  @override
  void initState() {
    super.initState();
    widget.model.addListener(_update);
  }

  @override
  void dispose() {
    super.dispose();
    widget.model.removeListener(_update);
  }

  @override
  Widget build(BuildContext context) {
    return Provider(
      model: widget.model,
      child: widget.child,
    );
  }
}
复制代码
class CountModel extends ChangeNotifier {
  int count;

  CountModel(this.count);

  void increment() {
    count++;
    notifyListeners();
  }
}
class ProviderDemoWidget3 extends StatefulWidget {
  ProviderDemoWidget3({Key key}) : super(key: key);

  @override
  _ProviderDemoWidget3State createState() => _ProviderDemoWidget3State();
}

class _ProviderDemoWidget3State extends State<ProviderDemoWidget3> {
  CountModel _countModel = CountModel(0);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(providerDemoTitle3),
      ),
      body: ChangeNotifierProvider<CountModel>(
        model: _countModel,
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Builder(builder: (context1) {
                return Text(
                    "计数:${Provider.of<CountModel>(context1, true).count}(有依赖状况)");
              }),
              Builder(builder: (context2) {
                return Text(
                    "计数:${Provider.of<CountModel>(context2, false).count}(无依赖状况)");
              }),
              RaisedButton(
                  child: Text("increment"),
                  onPressed: () => _countModel.increment()),
              Text(providerDemoIntroduction3),
            ],
          ),
        ),
      ),
    );
  }
}
复制代码

真正Provider的用法

咱们发现与上一个示例的简易版Provider上的使用方法是一致的。 正如介绍所描述的很是简单。固然了Provider库,可维护性,可测试性,可扩展性远比咱们所写的强大。oop

class ProviderDemoWidget4 extends StatefulWidget {
  ProviderDemoWidget4({Key key}) : super(key: key);

  @override
  _ProviderDemoWidget4State createState() => _ProviderDemoWidget4State();
}

class _ProviderDemoWidget4State extends State<ProviderDemoWidget4> {
  CountModel _countModel = CountModel(0);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(providerDemoTitle4),
      ),
      body: ChangeNotifierProvider.value(
        value: _countModel,
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Consumer<CountModel>(
                builder: (contextC, model, child) {
                  return Text("计数:${model.count}(有依赖状况)");
                },
              ),
              Builder(builder: (context2) {
                return Text(
                    "计数:${Provider.of<CountModel>(context2, listen: false).count}(无依赖状况)");
              }),
              RaisedButton(
                  child: Text("increment"),
                  onPressed: () => _countModel.increment()),
              Text(providerDemoIntroduction4),
            ],
          ),
        ),
      ),
    );
  }
}

class CountModel extends ChangeNotifier {
  int count;

  CountModel(this.count);

  void increment() {
    count++;
    notifyListeners();
  }
}
复制代码

到此为止,咱们也就了解了Provider的基本原理和基本使用。 可咱们的征程才开始。知其然,更要知其因此然。接下来,咱们从setState开始一步步分析Provider状态管理的过程。

参考资料

Flutter实战