原文地址:https://juejin.cn/post/6844904115428917262

    简单翻译来说,Provider:依赖注入与状态管理的结合,并且对外提供Widget使用。使用widget代替纯Dart对象,比如说Stream。因为widget简单,强大且可扩展。使用Provider,可以保证:可维护性(强制的单向数据流),可测试性,健壮性等等。
    复制代码
    什么是状态?什么是状态管理?

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

    UI=f(state)。从状态变化到UI更新的过程,我们称之为状态管理。状态管理要尽可能的把这个过程独立出来,让动态界面如同静态页面一般简单。
    有哪些状态管理的方式/库

    • setState
    • FutureBuilder/StreamBuilder/BLoc
    • Provider/ScopedModel
    • redux/Fish-redux
      复制代码
      简易版Provider实现
      有了状态管理的介绍,我们可以参考Provider,通过手上现有的组件,实现一个简易版的Provider。要用到的系统组件:

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

    setState
    状态管理最基础的一个实现
    class _SetStateDemoWidgetState extends State {
    int count = 0;

    @override
    Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar(title: Text(setStateDemoTitle),),
    body: Center(
    child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
    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;

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

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

    class ProviderDemoWidget1 extends StatefulWidget {

    }

    class _ProviderDemoWidget1State extends State {
    int _count = 0;
    @override
    Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar(
    title: const Text(providerDemoTitle1),
    ),
    body: CountProvider(
    count: _count,
    child: Center(
    child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
    Builder(
    builder: (context2) {
    /// 读取和显示计数
    CountProvider provider = (context2
    .getElementForInheritedWidgetOfExactType()
    ?.widget) as CountProvider;
    return Text(“计数:${provider.count}”);
    },
    ),
    ElevatedButton(
    child: const Text(“increment”),
    onPressed: () => setState(() => _count++),
    ),
    const Text(providerDemoIntroduction1),
    ],
    ),
    ),
    ),
    );
    }
    }
    复制代码
    Provider(InheritedWidget + ChangeNotifier)
    我们先看效果图,我们看到有三种场景。只有依赖的组件更新了UI。

    代码入口
    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 CountProvider extends InheritedWidget {
    final CountModel model;

    const CountProvider({Key? key, required this.model, required Widget child}) : super(key: key, child: child);

    static CountModel of(BuildContext context, bool depend) {
    var provider = depend
    ? context.dependOnInheritedWidgetOfExactType()
    : context.getElementForInheritedWidgetOfExactType()?.widget;
    return (provider as CountProvider).model;
    }

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

    final CountModel model;

    ChangeNotifierProvider({Key? key, required this.child, required this.model}) : super(key: key);

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

    class _ChangeNotifierProviderState extends State {
    _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 CountProvider(
    model: widget.model,
    child: widget.child,
    );
    }
    }
    复制代码
    有了 CountModel,Provider,ChangeNotifierProvider,简易版状态管理Provider库也就写好了,下面用起来:
    class ProviderDemoWidget2 extends StatefulWidget {

    }

    class _ProviderDemoWidget2State extends State {
    CountModel _countModel = CountModel(0);

    @override
    Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar(title: const Text(providerDemoTitle2)),
    body: ChangeNotifierProvider(
    model: _countModel,
    child: Center(
    child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
    Text(“计数:写一个自己的简易provider - 图1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20Builder(builder%3A%20(context1)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20Text(%22%E8%AE%A1%E6%95%B0%3A#card=math&code=%7B_countModel.count%7D%22%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20Builder%28builder%3A%20%28context1%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20Text%28%22%E8%AE%A1%E6%95%B0%3A&id=JJc2P){CountProvider.of(context1, true).count}(有依赖情况)”);
    }),
    Builder(builder: (context2) {
    return Text(“计数:${CountProvider.of(context2, false).count}(无依赖情况)”);
    }),
    ElevatedButton(child: const Text(“increment”), onPressed: () => _countModel.increment()),
    const Text(providerDemoIntroduction2),
    ],
    ),
    ),
    ),
    );
    }
    }
    复制代码
    基于上个版本的通用泛型封装
    上一个示例中CountModel不具有通用性,所以我们写一个泛型版本
    代码入口
    class Provider extends InheritedWidget {
    final T model;

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

    static T of(BuildContext context, bool depend) {
    var provider = depend
    ? context.dependOnInheritedWidgetOfExactType()
    : context.getElementForInheritedWidgetOfExactType()?.widget;
    return (provider as Provider).model as T;
    }

    @override
    bool updateShouldNotify(Provider old) {
    return true;
    }
    }
    class ChangeNotifierProvider extends StatefulWidget {
    final Widget child;

    final T model;

    ChangeNotifierProvider({Key? key, required this.child, required this.model}) : super(key: key);

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

    class _ChangeNotifierProviderState extends State {
    _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,
    );
    }
    }
    复制代码
    这样一个简单的ChangeNotifierProvider就实现了。
    demo使用
    class ProviderDemoWidget3 extends StatefulWidget {

    }

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

    @override
    Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar( title: const Text(providerDemoTitle3)),
    body: ChangeNotifierProvider(
    model: _countModel,
    child: Center(
    child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
    Builder(builder: (context1) {
    return Text(“计数:写一个自己的简易provider - 图2.count%7D%EF%BC%88%E6%9C%89%E4%BE%9D%E8%B5%96%E6%83%85%E5%86%B5)%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20Builder(builder%3A%20(context2)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20Text(%22%E8%AE%A1%E6%95%B0%3A#card=math&code=%7BProvider.of%3CCountModel%3E%28context1%2C%20true%29.count%7D%EF%BC%88%E6%9C%89%E4%BE%9D%E8%B5%96%E6%83%85%E5%86%B5%29%22%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20Builder%28builder%3A%20%28context2%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20Text%28%22%E8%AE%A1%E6%95%B0%3A&id=AlFDH){Provider.of(context2, false).count}(无依赖情况)”);
    }),
    ElevatedButton(child: const Text(“increment”), onPressed: () => _countModel.increment()),
    const Text(providerDemoIntroduction3),
    ],
    ),
    ),
    ),
    );
    }
    }
    class ProviderDemoWidget3 extends StatefulWidget {

    }

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

    @override
    Widget build(BuildContext context) {
    …仅修改访问部分,增加泛型支持
    Builder(builder: (context1) {
    return Text(“计数:写一个自己的简易provider - 图3.count%7D%EF%BC%88%E6%9C%89%E4%BE%9D%E8%B5%96%E6%83%85%E5%86%B5)%22)%3B%0A%20%20%20%20%7D)%2C%0A%20%20%20%20Builder(builder%3A%20(context2)%20%7B%0A%20%20%20%20%20%20%20%20return%20Text(%22%E8%AE%A1%E6%95%B0%3A#card=math&code=%7BProvider.of%3CCountModel%3E%28context1%2C%20true%29.count%7D%EF%BC%88%E6%9C%89%E4%BE%9D%E8%B5%96%E6%83%85%E5%86%B5%29%22%29%3B%0A%20%20%20%20%7D%29%2C%0A%20%20%20%20Builder%28builder%3A%20%28context2%29%20%7B%0A%20%20%20%20%20%20%20%20return%20Text%28%22%E8%AE%A1%E6%95%B0%3A&id=p53Lj){Provider.of(context2, false).count}(无依赖情况)”);
    }),

    }
    }
    复制代码
    真正Provider的用法
    举例Provider库中ChangeNotifierProvider使用。

    其中使用到了Consumer,通过Provider.of(context)封装对model的访问

    正如介绍所描述的非常简单。当然了Provider库,可维护性,可测试性,可扩展性远比我们所写的强大。
    class ProviderDemoWidget4 extends StatefulWidget {

    }

    class _ProviderDemoWidget4State extends State {
    final CountModel _countModel = CountModel(0);

    @override
    Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar(title: const Text(providerDemoTitle4)),
    body: ChangeNotifierProvider.value(
    value: _countModel,
    child: Center(
    child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
    Consumer(
    builder: (contextC, model, child) {
    return Text(“计数:写一个自己的简易provider - 图4%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20Builder(builder%3A%20(context2)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20Text(%22%E8%AE%A1%E6%95%B0%3A#card=math&code=%7Bmodel.count%7D%EF%BC%88%E6%9C%89%E4%BE%9D%E8%B5%96%E6%83%85%E5%86%B5%29%22%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20Builder%28builder%3A%20%28context2%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20Text%28%22%E8%AE%A1%E6%95%B0%3A&id=I4che){Provider.of(context2, listen: false).count}(无依赖情况)”);
    }),
    ElevatedButton(child: const Text(“increment”), onPressed: () => _countModel.increment()),
    const Text(providerDemoIntroduction4),
    ],
    ),
    ),
    ),
    );
    }
    }
    复制代码
    到此为止,我们也就了解了Provider的基本原理和基本使用。
    可我们的征程才开始。知其然,更要知其所以然。接下来,我们从setState开始一步步分析Provider状态管理的过程。

    作者:mwq30123
    链接:https://juejin.cn/post/6844904115428917262
    来源:稀土掘金
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。