flutte状态管理

这个转自我自己的有道云 想看图片去那里
文档:Day3_23 状态管理.md
链接:http://note.youdao.com/noteshare?id=81a9d9832a1baa68995b45110b101900&sub=82EF5749864544B7B0782C8163EF971E

状态管理

  • 这个东西是这个里面非常重要的东西

为什么需要状态管理

状态管理是声明式编程里面非常重要的东西

这个东西是更接近原理的东西 它理解它对我们写代码

Flutter, vue, React 声明式编程

OC => swffit 整个前端都在向声明式编程迈近

  • 这个就是官方给的一幅图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cv9Wp8sU-1586077223760)(4C28770B9BAA46E18583B645B85D5A8B)]

状态就是数据 我们在构建flutter的时候我们就需要一些状态

这个官方的图就很好的表示了flutter的情况

我们应用里面有这些状态 我们build方法里面依赖这些状态 最后就会生成我们的页面结构

  • 调用setState

一般情况下 如果状态改变我们可以 调用setState 然后他就会根据最新的状态来展示我们的页面的

状态的分类

状态被分成两种 短时状态 和 App State 应用状态

  1. 短时状态

某些状态我们只需要在自己的wiget里面使用就可以了

  • 比如我们之前做的计数器conuter 官方的demo
  • PageView里面的组件记录的状态
  • 一个动画记录当前的进度 controller
  • 比如一个BottomNavigationBar中当前被选中的tab _currentIndex

这个就是当前页面会用到的时候它就是一个短时状态

这个状态管理只需要使用StatefulWidget对应的State 来使用就可以了

但是它有一个缺点 就是如果想要在其他的地方访问这个状态其实是并不好访问的

虽然GlobalKey可以做到差不多的事情但是还是希望有一个更方便的形式来使用这个全局状态

  1. 应用状态

有些状态就希望是能在全局进行共享的

  • 购物车
  • 用户个性化选项 我们希望知道你在全局过滤掉了什么东西
  • 登录状态
  • 一个新闻的已读未读信息

这个状态虽然可以在Widget中传递来传递去 虽然可以

但是会造成耦合度过高的问题 如果中间去掉一个 那就会造成很麻烦的问题 你全部结构都要改

如何选择将一个状态设置为应用状态还是一个短时状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Np0O3zHz-1586077223763)(5CF3B0AF406E49429B2D1E2BFC56ACBB)]

如何选择应用状态 或者 短时状态

针对React使用setState还是Redux中的Store来管理状态哪个更好的问题,Redux的issue上,Redux的作者Dan Abramov

他这样回答的:

The rule of thumb is: Do whatever is less awkward

经验原则就是:选择能够减少麻烦的方式。

  • 怎么简单怎么来

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l7S1wR85-1586077223765)(B22647F6BAB4405480941CDEA6E97D1E)]

本来这些工具就是为了 使用简单

两种状态管理

我们主要学习两种状态管理

  • InheritedWidget
  • Provider

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EUCMpeME-1586077223766)(D83564DE3B1D4396AAE1F02FEB4C5969)]

如果以前我们要做这个我们可能会

将这个状态传递到需要使用的地方 路由的话也是将他作为参数传递过去

  • 然后就是第三方的状态管理
    • 像咸鱼的状态管理 他是在官方没有出的时候做的状态管理
    • 还有就是官方的状态管理它会不断的修改它的bug 不会某一天突然不更新

这个东西是可以用来共享Widget的

InheritedWidget

创建一个状态

  • 创建一个Widget他继承至 InheritedWidget
  • 实现一个bool updateShouldNotify(InheritedWidget oldWidget)方法
  • 状态共享 (设置状态)
  • 使用of拿到对象 (搞一个静态的方法拿到这个对象)
class HYCounterWidget extends InheritedWidget {

}

它这里有一个抽象方法我们还必须实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n2a1OrLM-1586077223767)(DDB3EA4C823D43489D95EAA7441C88E3)]

我们设置一个状态同时实现这个方法

class HYCounterWidget extends InheritedWidget {
  final int counter = 100;
  
  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return null;
  }
}

同时我们搞一个静态方法

class HYCounterWidget extends InheritedWidget {
  final int counter = 100;

  static HYCounterWidget of(BuildContext context) {
    return null;
  }
  
  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return null;
  }
}

到这里我们的这个InheritedWidget就创建完成了

我们现在就是小组件希望拿到这个widget

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ux6eKX66-1586077223767)(28DF986DCBD54333A6E9BFC907013B57)]

现在我们就创建对应的小组件

然后在小组件的外层包裹一个HYCounterWidget

import "package:flutter/material.dart";

main() => runApp(MyApp());

class HYCounterWidget extends InheritedWidget {
  final int counter = 100;

  HYCounterWidget({Widget child}): super(child: child);

  static HYCounterWidget of(BuildContext context) {
    return null;
  }

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return null;
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      theme: ThemeData(
        primarySwatch: Colors.blue,
        splashColor: Colors.transparent,
      ),
      home: HYHomePage(),
    );
  }
}

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: Center(
        child: HYCounterWidget (
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              HYShowData01(),
              HYShowData02()
            ],
          ),
        ),
      ),
    );
  }
}

class HYShowData01 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Card(
      color: Colors.red,
      child: Text("当前计数: 100")
    );
  }
}

class HYShowData02 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.blue,
      child: Text("当前计数: 100"),
    );
  }
}


所以如果我们要拿到这个couterWidget我们就要

class HYShowData01 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int counter = HYCounterWidget.of(context);

    return Card(
      color: Colors.red,
      child: Text("当前计数: ")
    );
  }
}

这个context就是Element 他会记录着Widget所在的位置

就像下面这样

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jsmVsktf-1586077223769)(92021157421C448A943494BAAF295C3C)]

所以我们调用这个HYCounterWidget.of(context); 它就开始沿着这个树结构往上找

离我们最近的HYCounterWidget这个对象 而这个HYCounterWidget里面是有一个counter属性的

这个时候我们就可以使用这个couter对象的了

其他地方想用我们也这样把这个参数拿到就可以了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F8Nt3PW4-1586077223770)(F05E29734104475988E8896F84704844)]

class HYShowData01 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int counter = HYCounterWidget.of(context).counter;

    return Card(
      color: Colors.red,
      child: Text("当前计数: ")
    );
  }
}

class HYShowData02 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int counter = HYCounterWidget.of(context).counter;

    return Container(
      color: Colors.blue,
      child: Text("当前计数: 100"),
    );
  }
}

当然现在肯定是不行的 因为我们当前of返回的是一个null

所以我们要

class HYCounterWidget extends InheritedWidget {
  final int counter = 100;

  HYCounterWidget({Widget child}): super(child: child);

  static HYCounterWidget of(BuildContext context) {
//    沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
    return context.dependOnInheritedWidgetOfExactType();
  }

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return null;
  }
}

你看这里它是返回的有一个泛型 所以那就是它了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s3DlVojY-1586077223771)(74ED7F9ECB40465280E27BA665CC158C)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uvVLcf1J-1586077223772)(D9161B5B4A6545B1A58F142E17A2990D)]

他会往上找找到对应的对象 找到这个counter

import "package:flutter/material.dart";

main() => runApp(MyApp());

class HYCounterWidget extends InheritedWidget {
  final int counter = 100;

  HYCounterWidget({Widget child}): super(child: child);

  static HYCounterWidget of(BuildContext context) {
//    沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
    return context.dependOnInheritedWidgetOfExactType();
  }

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return null;
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      theme: ThemeData(
        primarySwatch: Colors.blue,
        splashColor: Colors.transparent,
      ),
      home: HYHomePage(),
    );
  }
}

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: Center(
        child: HYCounterWidget (
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              HYShowData01(),
              HYShowData02()
            ],
          ),
        ),
      ),
    );
  }
}

class HYShowData01 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int counter = HYCounterWidget.of(context).counter;

    return Card(
      color: Colors.red,
      child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
    );
  }
}

class HYShowData02 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int counter = HYCounterWidget.of(context).counter;

    return Container(
      color: Colors.blue,
      child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
    );
  }
}


然后运行一下 结果报错了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SQkQnnhw-1586077223773)(43473A6E808343F481BC1A98B93E917E)]

原因就是下面这个 这个东西它是要你返回一个bool值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y7rSjssR-1586077223774)(4AAFBC8455E3410DB54B3CF4CEEA88E9)]

class HYCounterWidget extends InheritedWidget {
  final int counter = 100;

  HYCounterWidget({Widget child}): super(child: child);

  static HYCounterWidget of(BuildContext context) {
//    沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
    return context.dependOnInheritedWidgetOfExactType();
  }

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return true;
  }
}

但是这个返回什么用一会再说

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bPxsnqbz-1586077223775)(EABE9034FB50469FBC9330FD9322A393)]

遇到错误我们来读一下就可以了

但是现在我想要一个需求

我们这里有一个按钮然后点击以后 + 1

但是这里注意

  • 这个HYCounterWidget它是一个widget
  • 我们这个这个widget 里面所有的属性都是不可变的
  • 所以我们只能重新创建这个widget

我们将这个counter作为参数传递过来

class HYCounterWidget extends InheritedWidget {
  final int counter;

  HYCounterWidget({this.counter, Widget child}): super(child: child);

  static HYCounterWidget of(BuildContext context) {
//    沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
    return context.dependOnInheritedWidgetOfExactType();
  }

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return true;
  }
}

然后在创建的里面弄一个 100 的变量

同样我们的这个数字是要变的所以不能使用statelessWidget

class HYHomePage extends StatefulWidget {
  @override
  _HYHomePageState createState() => _HYHomePageState();
}

class _HYHomePageState extends State<HYHomePage> {
  int _counter = 100;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: Center(
        child: HYCounterWidget (
          counter: _counter,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              HYShowData01(),
              HYShowData02()
            ],
          ),
        ),
      ),
    );
  }
}

整个按钮

class HYHomePage extends StatefulWidget {
  @override
  _HYHomePageState createState() => _HYHomePageState();
}

class _HYHomePageState extends State<HYHomePage> {
  int _counter = 100;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: Center(
        child: HYCounterWidget (
          counter: _counter,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              HYShowData01(),
              HYShowData02()
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.play_arrow),
        onPressed: () {
          setState(() {
            _counter++;
          });
        },
      ),
    );
  }
}

这样setState的时候这个build的东西都会重新执行

那这个HYCounterWidget 自然就会重新创建

我们点击之后counter改变了 我们保存的_counter就改变

后面的这个就重新构建了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bDQiXJiZ-1586077223776)(40CA46C888F041CF954F92BC9B3402B6)]

这个东西就是我们InheritedWidget最基本的使用

import "package:flutter/material.dart";

main() => runApp(MyApp());

class HYCounterWidget extends InheritedWidget {
  final int counter;

  HYCounterWidget({this.counter, Widget child}): super(child: child);

  static HYCounterWidget of(BuildContext context) {
//    沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
    return context.dependOnInheritedWidgetOfExactType();
  }

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return true;
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      theme: ThemeData(
        primarySwatch: Colors.blue,
        splashColor: Colors.transparent,
      ),
      home: HYHomePage(),
    );
  }
}

class HYHomePage extends StatefulWidget {
  @override
  _HYHomePageState createState() => _HYHomePageState();
}

class _HYHomePageState extends State<HYHomePage> {
  int _counter = 100;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: Center(
        child: HYCounterWidget (
          counter: _counter,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              HYShowData01(),
              HYShowData02()
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.play_arrow),
        onPressed: () {
          setState(() {
            _counter++;
          });
        },
      ),
    );
  }
}

class HYShowData01 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int counter = HYCounterWidget.of(context).counter;

    return Card(
      color: Colors.red,
      child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
    );
  }
}

class HYShowData02 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int counter = HYCounterWidget.of(context).counter;

    return Container(
      color: Colors.blue,
      child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
    );
  }
}


还有一个问题就是undataShouldNotify 这个函数要求返回一个bool 即true或者false

这个区别是什么呢

可以看到它的名字 当我们更新的时候要不要做一个通知 返回true表示通知 返回false的话 表示不通知

我们改成false看看会有什么不同吗

class HYCounterWidget extends InheritedWidget {
  final int counter;

  HYCounterWidget({this.counter, Widget child}): super(child: child);

  static HYCounterWidget of(BuildContext context) {
//    沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
    return context.dependOnInheritedWidgetOfExactType();
  }

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return false;
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-US7zHuas-1586077223777)(33F880FA7CD641D1B2E49576717B97E0)]

其实这个东西是如果你的使用的widget是fulWidget 这个为true还是为false会影响你是否会调用这个didChangeDependencies

我们之前讲过这个东西 什么时候会回调呢 它依赖我们的某一个InheritedWidget改变的时候它就会执行

import "package:flutter/material.dart";

main() => runApp(MyApp());

class HYCounterWidget extends InheritedWidget {
  final int counter;

  HYCounterWidget({this.counter, Widget child}): super(child: child);

  static HYCounterWidget of(BuildContext context) {
//    沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
    return context.dependOnInheritedWidgetOfExactType();
  }

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return true;
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      theme: ThemeData(
        primarySwatch: Colors.blue,
        splashColor: Colors.transparent,
      ),
      home: HYHomePage(),
    );
  }
}

class HYHomePage extends StatefulWidget {
  @override
  _HYHomePageState createState() => _HYHomePageState();
}

class _HYHomePageState extends State<HYHomePage> {
  int _counter = 100;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: Center(
        child: HYCounterWidget (
          counter: _counter,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              HYShowData01(),
              HYShowData02()
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.play_arrow),
        onPressed: () {
          setState(() {
            _counter++;
          });
        },
      ),
    );
  }
}

class HYShowData01 extends StatefulWidget {
  @override
  _HYShowData01State createState() => _HYShowData01State();
}

class _HYShowData01State extends State<HYShowData01> {

  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    print("是否打印  void didChangeDependencies");
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    int counter = HYCounterWidget.of(context).counter;

    return Card(
      color: Colors.red,
      child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
    );
  }
}

class HYShowData02 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int counter = HYCounterWidget.of(context).counter;

    return Container(
      color: Colors.blue,
      child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
    );
  }
}


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jLirPgTy-1586077223778)(5CD766496A754D6BB1A100B16FBC4078)]

然后改成false它就不会执行了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2DvZXNIU-1586077223780)(DDCD6D96D1BB4FBE9BAB7149E7ABA6FA)]

那我们到底是返回true还是false呢

我们可以比较原来的数据和现在是否是一样的 如果一样我们就返回true

class HYCounterWidget extends InheritedWidget {
  final int counter;

  HYCounterWidget({this.counter, Widget child}): super(child: child);

  static HYCounterWidget of(BuildContext context) {
//    沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
    return context.dependOnInheritedWidgetOfExactType();
  }

  @override
  bool updateShouldNotify(HYCounterWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return oldWidget.counter != counter;
  }
}

所以我们来总结一下

我们使用InheritedWidget

  • 设置共享的数据
  • 定义构造方法
  • 获取组件最近的InheritedWidget
  • 决定要不要回State对象中的didchangeDependenices方法

所以一般情况下我们的InheritedWidget里面就需要做这些事情

然后我们就要看一下这个

context.dependOnInheritedWidgetOfExactType();

我们这里掉了这么一个方法 这个方法最主要的就是

沿着Element树找最近的HYCounterElement这个对象然后把它返回

然后从Element中取出widget对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yYvnK0PM-1586077223781)(308798937B6542C5A198C2214BBD745E)]

我们点到这个方法里面 它是一个抽象的方法

如果你想看子类的实现的话 ctrl + alt + b

如果有多个实现它会叫你选择 但是这里只有一个 它就会直接调过来

我们来看看它怎么找

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-khpBBubp-1586077223782)(D31C4A67F93D4BA5B717B534A8D3466B)]

它这里有一个 _inheritedWidgets 我们点过去看看

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I2Itzu3z-1586077223782)(7B907A02E6A749809192E163E20EC8A9)]

ctrl + 左键 点过来看看 它这里是一个映射

我说我们找到的就是InheritedElement

然后再看过来

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NoDtRS7l-1586077223783)(9902E06923CC4ACEACECD12D59E8BBFF)]

我们这里看它是否为空 然后这里给你放入一个T

这个T是一个泛型

这个地方我们找的是HYCounterWidget 所以这里就意味着这个地方是一个HYCounterWidget这样的一个类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uhi1sgDF-1586077223784)(80FE44F2350647CD8088DDD752855101)]

然后找到之后它就把这个东西赋值给ancester(祖先)

然后它就拿到这个祖先了

他就判断一下你祖先是否为空 就返回空 如果不为空它就给你返回这个dependOnInheritedElement 这个方法

那么我们点进去

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-15btoOKt-1586077223784)(186C36CF915849BD837901D94D524BD5)]

然后这里有一个_dependencies 这个东西的作用就是 到时候数据刷新的时候 要不要执行这个didChangeDependencies

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KmdA9s3p-1586077223785)(5BF54FA6CB2749D4AE67D8D17C06A52B)]

我们这里最主要的还是

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VqJJSDGP-1586077223785)(10CCF606511E445BBE162C85C09205AC)]

这里从祖先拿到了widget 它这里就是把它的widget给返回了

所以我们这里的调用的这个函数 就是去取出我们的widget然后把它返回了

  • 但是注意我们的这个InheritedWidget一般只是用来共享数据 一般不用来 状态管理那种要修改的数据
  • 要修改数据的话我们最主要的是Provider

Provider

这个虽然是一个第三方的 插件 但是它是由社区作者和flutter团队共同编写的

他们现在也是在共同维护 所以可以放心使用 不用担心它有一天不维护了

这个完全不用担心 只要还有在用flutter 因该就会继续维护下去

因为这个玩意它属于第三方的东西所以我们在使用的 需要去安装它

pub.dev

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pnrDRhsG-1586077223786)(29AE6BFC698F4937B19692923CE3CDEE)]

dependencies:
  event_bus: ^1.1.1
  provider: ^4.0.4
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  dio: ^3.0.9

然后pub get

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-flPlkyiT-1586077223787)(9E76AAF4467941A68D2AF45E0F6ABFE0)]

  • 刚刚我们在做数据共享的时候我们用的是InheritedWidget
  • 现在我们就要用Provider

我们什么使用使用

像刚刚我们使用InheritedWidget的时候 我们一般是管理一个状态

但是现在这里我们 如果希望是 管理多个状态 我们就需要使用Provider

还有就是我们在使用provider的时候它也是需要嵌套的

那为了不要考虑这个问题 我们一般是直接来到最顶层来嵌套这个provider

看到最上面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jlbulCQM-1586077223787)(5BF3D22E4F91403A8E7FDD11C884A8CC)]

这个MyApp就是最顶层的Widget

现在我们写成 箭头函数就不太好了展开

void main() {
  runApp(
    MyApp()
  );
}

我们一般是使用的ChangeNotifyProvider

void main() {
  runApp(
    ChangeNotifierProvider(
      child: MyApp()
    )
  );
}

但是还没完 这里还有一个属性create

void main() {
  runApp(
    ChangeNotifierProvider(
      create: ,
      child: MyApp()
    )
  );
}

而这个create就是放到我们以后要共享的数据

怎么使用呢

  • 如何使用Provider
  1. 先创建我们需要共享的数据
  2. 在应用程序的顶层 ChangeNotifierProvider
  3. 在其他的位置使用这个共享的数据
  • 这里我们将我们的代码放到一个文件夹里面 不然到时候抽取的时候我们还要改路径

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-smUUy3tr-1586077223788)(A0921E05D226475DBD24A6BC415F12D7)]

  1. 创建需要共享的数据

一般我们是在文件夹里面 建立一个文件夹viewmodel

我们建立的这个文件夹 不是model

但是这个东西不是纯粹的model

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-loDTu0sJ-1586077223789)(742885F4022745D4B5590AC6D292279D)]

这样我们就是做的MVVM结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DjyqXJQU-1586077223790)(77BDD97955D34C758DE476B5EAA2B9BB)]

这样做一个东西 然后里面就由东西需要共享

然后这个数据有一天改变的时候我们是不是要通知我们的界面发生一个刷新

所以我们会然他继承至ChangeNotifier

如果我们不想要混入的话我们就可以使用混入

同样使用混入的话我们就会有这个类里面的所有相关的方法

但是这个地方 我们的变量是私有的 外面是访问不了的

所以我们要给它gs方法

我们可以使用 alt + insert 快速生成对应的方法

mac 是 commond + n

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fSlJoZdu-1586077223791)(936294F6098949B1A525A54292F4E160)]

同时为了能通知我们里面的东西进行刷新我们也需要 在set的位置调用对应的方法

import 'package:flutter/material.dart';

class HYCounterViewModel with ChangeNotifier{
  int _counter;

  int get counter => _counter;

  set counter(int value) {
    _counter = value;
  }

}

调用notifyListeners, 通知所有的监听者 刷新状态

import 'package:flutter/material.dart';

class HYCounterViewModel with ChangeNotifier{
  int _counter;

  int get counter => _counter;

  set counter(int value) {
    _counter = value;
    notifyListeners();
  }

}

然后我们来到引用程序顶层

void main() {
  runApp(
    ChangeNotifierProvider(
      child: MyApp()
    )
  );
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jpe7edkg-1586077223792)(5BB1A3DD20154C5A8A83285B905B5A1C)]

我们看到这个create是一个必传的参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9V0gUwBZ-1586077223793)(2976C2A1EDB94B71857E558AC6FC8206)]

它长成这样 还有一个返回值这个返回的值就是我们刚刚写的HYCunterViewModel的对象

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (BuildContext ctx) {
        return HYCounterViewModel();
      },
      child: MyApp()
    )
  );
}

那么我们怎么在后面使用这个东西呢

    Provider.of(context);

通过这个东西我们就可以取到对应的这个ViewMdeol对象

但是我们怎么才能取到这个ViewModel呢

这个地方的 其实就相当于一个 key

class HYShowData01 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int counter = Provider.of<HYCounterViewModel>(context).counter;
    return Container(
      color: Colors.red,
      child: Text("当前计数: 100", style: TextStyle(fontSize: 30)),
    );
  }
}

然后展示

import "package:flutter/material.dart";
import 'package:learn_flutter02/day09Protice/viewmodel/counter_view_model.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (BuildContext ctx) {
        return HYCounterViewModel();
      },
      child: MyApp()
    )
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      theme: ThemeData(
        primarySwatch: Colors.blue,
        splashColor: Colors.transparent,
      ),
      home: HYHomePage(),
    );
  }
}

class HYHomePage extends StatefulWidget {
  @override
  _HYHomePageState createState() => _HYHomePageState();
}

class _HYHomePageState extends State<HYHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              HYShowData01(),
              HYShowData02()
            ],
          ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.play_arrow),
        onPressed: () {
        },
      ),
    );
  }
}

class HYShowData01 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int counter = Provider.of<HYCounterViewModel>(context).counter;
    return Container(
      color: Colors.red,
      child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
    );
  }
}

class HYShowData02 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int counter = Provider.of<HYCounterViewModel>(context).counter;
    return Container(
      color: Colors.blue,
      child: Text("当前计数:$counter", style: TextStyle(fontSize: 30)),
    );
  }
}


但是这样它就是null 因为我们没有赋初值

import 'package:flutter/material.dart';

class HYCounterViewModel with ChangeNotifier{
  int _counter = 100;

  int get counter => _counter;

  set counter(int value) {
    _counter = value;
    notifyListeners();
  }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LaAhUSFb-1586077223794)(BEDDA5E2BEDC454B93E396B3FF200D2A)]

所以 这个看着和InheritedWidget非常相似

其实这个Provider它的底层就是依赖这个InheritedWidget的

但是它做很多的优化

然后我们同样希望能够在

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A8Oz3QJB-1586077223796)(6F8CBF06B67C42A4AD20C445A524788C)]

改变这个数据

你要想对它做一个修改 你必须这样来做

那我们要怎么来用呢

      floatingActionButton: Consumer(
        
      ),

我们在对应的地方放一个Consumer 这个东西是消费者的意思

可能这个东西涉及到锁问题吧

它有一个必须实现的方法builder

      floatingActionButton: Consumer<HYCounterViewModel>(
        builder: 
      ),

你可以看到它的builder里面有三个参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Y6mLwLu-1586077223797)(48EF4FBD2A8F42D291E584207A8BF2E8)]

一般我们的这个builder都是会有一个context的 Element嘛在树结构中的位置

这个value就是我们的HYCounterViewModel

  floatingActionButton: Consumer<HYCounterViewModel>(
        builder: (context, counterVM, child) {
          return FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {
              
            },
          );
        }
      ),

我们可以通过counterVM取到对应的数据

        floatingActionButton: Consumer<HYCounterViewModel>(
        builder: (context, counterVM, child) {
          return FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {
              counterVM.counter += 1;
            },
          );
        }
      ),

我们一旦设置这个之后其他的使用的地方就会做监听

HYShowdata01 02

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xYW0HhQC-1586077223798)(4E8C844B36254504B81081622E59DDBF)]

所以我们来总结一下使用

  1. 先创建我们需要共享的数据
  2. 在应用程序的顶层 ChangeNotifierProvider
  3. 在其他的位置使用这个共享的数据
    • Provider.of
    • Consumer
  • 创建ViewModel 和 需要共享的数据
  • 在应用程序的顶层挂载
  • 在其他的位置 Provider.of(context).counter; 拿到并使用
  • 或者使用Consumer 用builder 回调函数创建出对应的组件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r31Zy8Af-1586077223799)(87F7A65B9D8E47C6B30BEF8FB4CF9740)]

为什么说这个是一个MVVM

因为我们出了数据模型意外还多了一个ViewModel

MVVM 就是 Model View ViewModel 的简写

虽然这里没有专门的model对象不过感觉 差不多

我们来到HYShowData02

class HYShowData02 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int counter = Provider.of<HYCounterViewModel>(context).counter;
    return Container(
      color: Colors.blue,
      child: Consumer<HYCounterViewModel>(
        builder: (ctx, counterVM, child) {
          return Text("当前计数:${counterVM.counter}", style: TextStyle(fontSize: 30));
        },
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pakMRuaf-1586077223799)(63F3C5B064134065877EAF8F481C9EE6)]

但是从实现的难度上来说 还是直接使用Provider.of(context)简单一点

但是开发中还是Consumer还是用的更多一点

因为我们在使用Provider.of(context).counter 的时候

然后我们来看看

class HYShowData01 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("data01的build方法");
    int counter = Provider.of<HYCounterViewModel>(context).counter;
    return Container(
      color: Colors.red,
      child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
    );
  }
}

class HYShowData02 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("data02的build方法");
    return Container(
      color: Colors.blue,
      child: Consumer<HYCounterViewModel>(
        builder: (ctx, counterVM, child) {
          print("data02的builder方法");
          return Text("当前计数:${counterVM.counter}", style: TextStyle(fontSize: 30));
        },
      ),
    );
  }
}


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H2VUbBMS-1586077223800)(DD9516DB5BDE48A2825544D185B909FF)]

因为consumer里面只会执行builder内部的代码

但是外部的不会执行 所以它的执行效率高

  1. 先创建我们需要共享的数据
  2. 在应用程序的顶层 ChangeNotifierProvider
  3. 在其他的位置使用这个共享的数据
    • Provider.of: 当Provider中的数据发生变化的时候, Provider.of所在的widget整个build都会重新执行
    • Consumer(相对推荐): 当Provider数据发生改变的时候 只会执行Consumer当中的builder

builder方法是会被重新执行的

那么问一个问题我们的整个Provider在那些地方被依赖了
































答案是: 三个

但是有些地方我们只是改变了数据 但是我们并不希望它重新执行builder方法

import "package:flutter/material.dart";
import 'package:learn_flutter02/day09Protice/viewmodel/counter_view_model.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (BuildContext ctx) {
        return HYCounterViewModel();
      },
      child: MyApp()
    )
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      theme: ThemeData(
        primarySwatch: Colors.blue,
        splashColor: Colors.transparent,
      ),
      home: HYHomePage(),
    );
  }
}

class HYHomePage extends StatefulWidget {
  @override
  _HYHomePageState createState() => _HYHomePageState();
}

class _HYHomePageState extends State<HYHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              HYShowData01(),
              HYShowData02()
            ],
          ),
      ),
      floatingActionButton: Consumer<HYCounterViewModel>(
        builder: (context, counterVM, child) {
          print("Consumer 中的builder");
          return FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {
              counterVM.counter += 1;
            },
          );
        }
      ),
    );
  }
}

class HYShowData01 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("data01的build方法");
    int counter = Provider.of<HYCounterViewModel>(context).counter;
    return Container(
      color: Colors.red,
      child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
    );
  }
}

class HYShowData02 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("data02的build方法");
    return Container(
      color: Colors.blue,
      child: Consumer<HYCounterViewModel>(
        builder: (ctx, counterVM, child) {
          print("data02的builder方法");
          return Text("当前计数:${counterVM.counter}", style: TextStyle(fontSize: 30));
        },
      ),
    );
  }
}


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DVXqtZDj-1586077223800)(F494D11B89854035ABEBD179737CCC8B)]

我们的这个FloatingActionButton 没有必要构建

同时我们的Icon也没有必要构建

          FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {
              counterVM.counter += 1;
            },
          );

我们这里可以有两层优化

  1. 我们可以使用child Icon优化
      floatingActionButton: Consumer<HYCounterViewModel>(
        builder: (context, counterVM, child) {
          print("Consumer 中的builder");
          return FloatingActionButton(
            child: child,
            onPressed: () {
              counterVM.counter += 1;
            },
          );
        },
        child: Icon(Icons.add),
      ),

这样我们的这个Icon它就不在builder里面了

所以Icon它就不会重新构建了

如果你这里十分的复杂的话它也不会重新构建了

但是它就没有办法使用这个ViewModel里面的数据了

第二部的优化

刚刚的那种优化一般用在HYShowdata01 02 的位置 那些地方有很多不希望重新构建的地方

但是针对我们的floatActionButton 当我们的数据发生变化的时候 floatActionButton也是不需要重新构建的

  1. 先创建我们需要共享的数据

  2. 在应用程序的顶层 ChangeNotifierProvider

  3. 在其他的位置使用这个共享的数据

    • Provider.of: 当Provider中的数据发生变化的时候, Provider.of所在的widget整个build都会重新执行
    • Consumer(相对推荐): 当Provider数据发生改变的时候 只会执行Consumer当中的builder
    • Selector:
      • selector方法 对原有的数据进行转化
      • shouldRebuild
  4. selector

  • 对原有的类型做一个转化
  • shouldRebuild返回bool是否需要重新构建
      floatingActionButton: Selector<HYCounterViewModel, HYCounterViewModel>(
        
      )

使用的时候你发现它传一个泛型还不够

它要传两个 这两个怎么用我们一会再说

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L5VLLLTT-1586077223801)(31CEFA30182547C8A8DB7A870CDE7035)]

我们看到他的selector

它传过来一个S返回一个A我们可以通过它 去做ViewModel和Model的转化

但是我们这里共享的是一个普通的数值 所以没有必要使用这个东西

但是还是可以用一下

class _HYHomePageState extends State<HYHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              HYShowData01(),
              HYShowData02()
            ],
          ),
      ),
      floatingActionButton: Selector<HYCounterViewModel, int>(
        selector: (ctx, counterVM) => counterVM.counter,
        builder: (ctx, counter, child) {
          return FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {
              counter += 1;
            },
          );
        },
      )
    );
  }
}

但是这样没有办法通知到对应的数据了

所以我们还是改回来

class _HYHomePageState extends State<HYHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              HYShowData01(),
              HYShowData02()
            ],
          ),
      ),
      floatingActionButton: Selector<HYCounterViewModel, HYCounterViewModel>(
        selector: (ctx, counterVM) => counterVM,
        builder: (ctx, counterVM, child) {
          return FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {
              counterVM.counter += 1;
            },
          );
        },
      )
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GrHes8OY-1586077223801)(6CFE7847B7AF43F3924210826900CC19)]

还是可以用的

  • shouldRebuild: 这个东西的作用就是判断它是否需要 重新执行builder方法
      floatingActionButton: Selector<HYCounterViewModel, HYCounterViewModel>(
        selector: (ctx, counterVM) => counterVM,
        shouldRebuild: (prev, next) => false,
        builder: (ctx, counterVM, child) {
          return FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {
              counterVM.counter += 1;
            },
          );
        },
      )
  1. 先创建我们需要共享的数据
  2. 在应用程序的顶层 ChangeNotifierProvider
  3. 在其他的位置使用这个共享的数据
    • Provider.of: 当Provider中的数据发生变化的时候, Provider.of所在的widget整个build都会重新执行
    • Consumer(相对推荐): 当Provider数据发生改变的时候 只会执行Consumer当中的builder
    • Selector:
      • selector方法 对原有的数据进行转化
      • shouldRebuild

Consumer 和 Selector 是搭配使用的

如果有对数据的依赖的话 我们可以使用Consumer

但是没有对数据的依赖或者想要 过滤数据或者改变数据的话我们就可以使用这个Selector

共享多个ViewModel

这里我们有一个弊端就是我们如果想要共享多个ViewModel

的话是不能实现的

我们这里共享数据是只能共享一个

但是肯定是想要共享多个 我们这里来创建这样的两个 文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BK8IpWaC-1586077223802)(379732F293EB4EE9BB1466F85823C941)]

user.dart

class UserInfo {
  String nickName;
  String imageUrl;
  int level;
}

user_view_model.dart

import "package:flutter/material.dart";
import 'package:learn_flutter02/day09Protice/model/user.dart';

class UserViewModel extends ChangeNotifier {
  UserInfo _user;

  UserInfo get user => _user;

  set user(UserInfo user) {
    _user = user;
    notifyListeners();
  }
}

现在我们就有两个ViewModel文件了

那你如何进行共享呢

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (BuildContext ctx) {
        return HYCounterViewModel();
      },
      child: MyApp()
    )
  );
}

其实我们是可以

这样嵌套

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (BuildContext ctx) {
        return HYCounterViewModel();
      },
      child: ChangeNotifierProvider(
          create: (BuildContext ctx) {
            return HYUserViewModel()
          },
        child: MyApp(),
      )
    )
  );
}

但是很明显这样嵌套不好

我们可以这样使用

void main() {
  runApp(MultiProvider(
    providers: [],
    child: MyApp(),
  ));
}

这样我们就不需要使用这个ChangeNotifierProvider的嵌套了

void main() {
  runApp(MultiProvider(
    providers: [
      ChangeNotifierProvider(
        create: (ctx) => HYCounterViewModel(),
      ),
      ChangeNotifierProvider(
        create: (ctx) => HYUserViewModel(UserInfo()),
      )
    ],
    child: MyApp(),
  ));
}

但是这样 这个provider里面的东西就太多了

同样我们老师的尿性就是这样

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IBgzlPti-1586077223802)(09F0BA41EAE64C97BF608675FC37CA4F)]

创建了一个新的dart 文件

import "package:provider/provider.dart";
import "package:provider/single_child_widget.dart";

import "counter_view_model.dart";
import "user_view_model.dart";
import "../model/user.dart";

List<SingleChildWidget> providers = [
  ChangeNotifierProvider(
    create: (ctx) => HYCounterViewModel(),
  ),
  ChangeNotifierProvider(
    create: (ctx) => HYUserViewModel(UserInfo()),
  )
];

这样的话我们的这个东西我们就是在这个里面进行维护了

之后如果我们想要添加任何一个东西的 我们就只需要在这里 添加就可以了

所以如果我们有很多Provider我们就可以来这里维护

能不能使用呢

当然能了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JO8R61uT-1586077223803)(61C1FF14E3084C6B9D0FEAC334541BAA)]

import "package:flutter/material.dart";
import 'package:learn_flutter02/day09Protice/viewmodel/counter_view_model.dart';
import 'package:learn_flutter02/day09Protice/viewmodel/initialize_provider.dart';
import 'package:learn_flutter02/day09Protice/viewmodel/user_view_model.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(MultiProvider(
    providers: providers,
    child: MyApp(),
  ));
//  runApp(
//    ChangeNotifierProvider(
//      create: (BuildContext ctx) {
//        return HYCounterViewModel();
//      },
//      child: ChangeNotifierProvider(
//          create: (BuildContext ctx) {
//            return HYUserViewModel(UserInfo());
//          },
//        child: MyApp(),
//      )
//    )
//  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      theme: ThemeData(
        primarySwatch: Colors.blue,
        splashColor: Colors.transparent,
      ),
      home: HYHomePage(),
    );
  }
}

class HYHomePage extends StatefulWidget {
  @override
  _HYHomePageState createState() => _HYHomePageState();
}

class _HYHomePageState extends State<HYHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              HYShowData01(),
              HYShowData02(),
              HYShowData03()
            ],
          ),
      ),
      floatingActionButton: Selector<HYCounterViewModel, HYCounterViewModel>(
        selector: (ctx, counterVM) => counterVM,
        shouldRebuild: (prev, next) => false,
        builder: (ctx, counterVM, child) {
          return FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {
              counterVM.counter += 1;
            },
          );
        },
      )
    );
  }
}

class HYShowData01 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("data01的build方法");
    int counter = Provider.of<HYCounterViewModel>(context).counter;
    return Container(
      color: Colors.red,
      child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
    );
  }
}

class HYShowData02 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("data02的build方法");
    return Container(
      color: Colors.blue,
      child: Consumer<HYCounterViewModel>(
        builder: (ctx, counterVM, child) {
          print("data02的builder方法");
          return Text("当前计数:${counterVM.counter}", style: TextStyle(fontSize: 30));
        },
      ),
    );
  }
}

class HYShowData03 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.yellow,
      child: Consumer<HYUserViewModel>(
        builder: (ctx, userVM, child) {
          return Text("user对象的展示${userVM.user.nickName}");
        },
      ),
    );
  }
}

  • 还有一个问题就是如果我们要展示 超过两个ViewModel里面的东西那我们怎么办呢

很容易想到嵌套

但是这个不好

我们知道现在我们的Provider已经有三种用法了

  • Provider.of
  • Consumer
  • Selector

但是现在又要多一些了

  • Consumer23456

我们只说一下2讲了2其他的你就知道了

这个Consumer有两个泛型 分别代表两个ViewModel

同时builder的参数也多了一个

class HYShowData03 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
//    Provider.of Consumer Selector Consumer23456
    return Container(
      color: Colors.yellow,
      child: Consumer2<HYUserViewModel, HYCounterViewModel>(
        builder: (ctx, userVM, counterVM, child) {
          return Text(
           "user对象的展示${userVM.user.nickName} counter的展示 ${counterVM.counter}"
            , style: TextStyle(fontSize: 30),);
        },
      ),
    );
  }
}

  • 同理我们的selector也有23456但是这个用的比较少

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ReXYdWJ6-1586077223803)(2215A52075634235A1FEFB6E82F9C7A7)]

这个东西就是关于我们的Provider的使用

  • 我们这里请求的数据可以在网络请求过来以后放到 Provider里面准确来说是塞到ViewModel里面
  • 还有一种就是写到ViewModel的构造器里面 请求到数据以后 调用notifyListeners

用的时候如果有不懂因该马上去看需要什么东西

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值