Flutter 状态管理一锅端:第一章 Provider


序言

任何人事物都有状态,水结冰、天起大雾、人生气这些未尝不是状态呢?那么对于程序而言,对于语言来讲,他们的状态又是什么呢?咱们知道前端有那么几个大的框架Vue.js,React.js,包括但不限于小程序语法、移动端,他们都会有本身活跃的生态环境,同时也会衍生出本身的状态管理工具,像Vuex,像Vue的男友同样,如影随行,像Mobx Redux,好像他们无处不在,为何说状态管理是那么的重要呢,有时候不用状态管理,简简单单、单单纯纯不就挺好的。虽然话是这么说,但是真正在企业项目的时候,你们又都说组件化开发,组件化开发,那就形成了一个问题,这个数据,这个值怎么办,传来传去不怕传丢了,那这段路让咱们一块儿来聊聊状态管理在Flutter中又是什么的一个角色,谁和谁又是“情敌”呢 写分享第一点要考虑的就是起名字,就是一段视频 ,封面到底怎么样才可以吸引人,文章也不例外,一个你们 都乐于去阅读的可能是那些名字听起来就炸天的,好比状态管理看这篇就够了Flutter状态管理终极秘籍等等,固然我们也不肯去作震惊派,因此暂且称为Flutter 状态管理一锅端 灵感是来自以前看过的一篇文章,毕竟人家写的也是真的很好,但愿能趁上这个名字。闲扯了那么多,仍是没有一点技术性在,那开始吧……前端

  • 文章字数 3143
  • 阅读建议 能够跟着敲一敲效果更佳

第一章 走进官方推荐的解决方案Provider

在开始写以前,我决定从官方文档找切入,虽然一些中文的文档写的也很好,我们从flutter.dev/看会不会发现什么蛛丝马迹 git

120601.png
我么发现官网还真的有关于状态的描述

120602.png

映入眼帘的即是这个小动画,大体意思讲的是添加购物车的demo,也就是说探索Flutter时,有时须要跨应用程序在屏幕之间共享应用程序状态。固然咱们也必须考虑不少问题 github

120603.gif
那对于从事Ios 或者安卓的开发者来讲,真的须要重新的角度去看问题嘛,对于前端开发者来讲早已司空见惯。 能够从头开始重建UI的某些部分,而不用对其进行修改。Flutter的速度足够快,即便须要时也能够在每一帧上执行。还有说道 UI = F(state)

1.1 了解状态

短时 (ephemeral) 状态

不须要使用状态管理架构(例如 ScopedModel, Redux)去管理这种状态。你须要用的只是一个 StatefulWidget。 在下方你能够看到一个底部导航栏中当前被选中的项目是如何被被保存在 _MyHomepageState 类的 _index 变量中。小程序

class MyHomepage extends StatefulWidget {
  @override
  _MyHomepageState createState() => _MyHomepageState();
}

class _MyHomepageState extends State<MyHomepage> {
  int _index = 0; // 这个index即是短时的,别的地方也不须要去访问它不是吗

  @override
  Widget build(BuildContext context) {
    return BottomNavigationBar(
      currentIndex: _index,
      onTap: (newIndex) {
        setState(() {
          _index = newIndex;
        });
      },
      // ... items ...
    );
  }
}
复制代码

应用状态

若是你想在你的应用中的多个部分之间共享一个非短时的状态,而且在用户会话期间保留这个状态,咱们称之为应用状态(有时也称共享状态)。什么意思呢,就是说一个状态在A页面也要用在B页面也要使用,就像一个渣男同样,和谁都有染 api

03.png

也就是说哪位女士都有点状态上的联系 在这里缓存

Use React for ephemeral state that doesn't matter to the app globally and doesn't mutate in complex ways. For example, a toggle in some UI element, a form input state. Use Redux for state that matters globally or is mutated in complex ways. For example, cached users, or a post draft.markdown

Sometimes you'll want to move from Redux state to React state (when storing something in Redux gets awkward) or the other way around (when more components need to have access to some state that used to be local).架构

The rule of thumb is: do whatever is less awkward.app

翻译过来就是框架

将React用于短暂状态,该状态对应用程序全局可有可无,而且不会以复杂的方式进行更改。 例如,在某些UI元素中切换,即表单输入状态。 将Redux用于全局重要的状态或以复杂方式突变的状态。 例如,缓存的用户或后期草稿。

有时,您可能但愿从Redux状态转换为React状态(当在Redux中存储某些内容变得笨拙时),或者反过来(当更多组件须要访问某些曾经是本地的状态时)。

经验法则是:作些不太尴尬的事情。

大体就是这些意思

2 开始实践

从官方看到,这也印证了一点,它是官方建议使用的状态管理工具 首先我们先创建几个互不相干的文件来一下

  • MyPageA
import 'package:flutter/material.dart';

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

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

class _MyPageAState extends State<MyPageA> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        child: Center(
          child: Text('我是A页面'),
        ),
      ),
    );
  }
}

复制代码
  • MyPageB
  • MyPageC 后两个一样的道理。那么在首页呢我们写三个按钮,分别能够跳向不一样的按钮,在这里为了演示案例,我们就不作代码的封装等问题。
child: Center(
          child: Column(
            children: <Widget>[
              RaisedButton(
                onPressed: () {},
                child: Text('我是跳转到A页面的按钮'),
              ),
              RaisedButton(
                onPressed: () {},
                child: Text('我是跳转到B页面的按钮'),
              ),
              RaisedButton(
                onPressed: () {},
                child: Text('我是跳转到C页面的按钮'),
              )
            ],
          ),
        ),
复制代码

如今指望是这样子的咱们在页面A作测试

child: Column(
                children: <Widget>[
                  Container(
                    child: RaisedButton(
                      onPressed: () {},
                      child: Text('+'),
                    ),
                  ),
                  Container(
                    child: Text('如今的值是'),
                  ),
                ],
              )),
复制代码

想必已经知道要作个什么效果了就是没点击一次的时候,默认值就加一

04.png

Container(
                    child: RaisedButton(
                      onPressed: () {
                        setState(() {
                          count = count + 1;
                        });
                      },
                      child: Text('+'),
                    ),
                  ),
                  Container(
                    child: Text('如今的值是$count'),
                  ),
复制代码

如今已经能够实现了,那么咱们要在首页也显示这个值怎么办呢,这至关因而他的父亲也想看的值,就像是,儿子过年的时候收了一大笔压岁钱,此时长辈指望知道咱收了多少压岁钱而后,没收好伐 这样子吧,父亲给个方法过来,也就是给个袋子过来,我把压岁钱装进去

void callback() {
    print('我是首页的方法');
  }
复制代码

在跳转的时候,把方法传过去,Flutter 中能够任意的传递他们

Navigator.push(
                    context,
                    new MaterialPageRoute(
                        builder: (context) => MyPageA(myCallBack)),
                  );
复制代码

但是会有一个问题,可是对于全局应用状态来讲你须要在不一样的地方进行修改,可能须要大量传递回调函数 写到这儿,突然有个颇有意思的想法,那就是我们脑海中有个这个想法,

05.png

  • 父亲:父组件便是main.dart 文件
  • 大儿子:子组件一也就是MyPageA 文件
  • 二女儿:子组件二MyPageB文件
  • 老少:子组件三MyPageC文件 目前是这样的一个状况
void myCallBack(val) {
    print('我是父亲,这是个人收钱袋子,从孩子哪里没收的压岁钱是$val');
  }
复制代码
onPressed: () {
                        setState(() {
                          count = count + 1;
                        });
                        // 调用回调
                        widget.callback(count);
                      },
复制代码

那这样就会有一个问题,在开发的过程当中,就会出现大量的回调函数,这里就引用官方推荐的一种方式,provider package 须要理解3个概念

  • ChangeNotifier 这个是Flutter SDK 里用来向你们发送通知的一个类,相似于村里的喇叭 用来通知村民一些事情的变化等 那么就像如上提到的压碎钱的案例,怎们才能把压碎钱在父亲和几个孩子之间进行来回的传递呢,接下来我打算用一下这个相似于广播的东西。 在lib下新建一个文件夹命名provider lib/provider/money_provider.dart
import 'package:flutter/material.dart';

// MoneyProvider 这个类继承自发布者
class MoneyProvider extends ChangeNotifier {
  /// 这里就不说是数据了我们暂且称为私有数据,并_开头命名
  num _money = 0;
  // 把数据get 一下,相似于暴露
  num get money => _money;
  // 定义一个没有返回值的方法,主要是用来增长本身的压岁钱并展现给其余家里人
  void addMoneyAndShowOthers() {
    _money = _money + 1;
    // 该调用告诉正在侦听此模型的小部件进行重建。
    notifyListeners();
  }
}

复制代码
  • ChangeNotifierProvider ChangeNotifierProvider widget 能够向其子孙节点暴露一个 ChangeNotifier 实例。它属于 provider package。 那咱们喇叭已经搞好了,如今就是要高高挂起。放在全部村民都可以听的地方 这也就是说hangeNotifierProvider 要放在使用它的部件之上,可是又不能够放的太上,在这个案例中,咱们暂且放在 Remove Link
    121401.png
    这里须要注意的是provider 3.2.0
  • 提供者不同意使用的“构建器”,而同意“建立”
  • 不同意使用代理提供程序的“ builder” /“ initialBuilder”,而分别建议使用“ create”和“ update” 也就是说builder已是不建议使用了,那么这时候就须要使用新的api
void main() => runApp(
      ChangeNotifierProvider(
        // builder: (context) => MoneyProvider(),
        create: (context) => MoneyProvider(),
        child: MyApp(),
      ),
    );

复制代码

固然了,当咱们尝试须要多个共享的状态的时候呢,好比压岁钱状态、孩子有没有男女友状态等 就可使用这种方式

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider( create: (context) => MoneyProvider()),
        Provider( create: (context) => MoneyProvider(),),
      ],
      child: MyApp(),
    ),
  );
}
复制代码
  • Consumer 如上村长大喇叭都已经就绪了,接下来就是村民应该作的事儿了,就是一条通知发出,广大的村民该怎么接收呢,这里就须要一个部件Consumer咱们能够把它想象成一个Container是同样的道理,只不过它不仅仅的提供布局功能
return Consumer<MoneyProvider>(
  builder: (context, state, child) {
    return Text("${state}是什么呢?");
  },
);
复制代码

那这个时候,我们就去二女儿页面去监听一下试试,那这个时候可能须要我们把有状态的部件更改成无状态的部件即StatelessWidget

return Container(
      child: Consumer<MoneyProvider>(
        builder: (context, state, child) {
          return Text("");
        },
      ),
    );
复制代码

咱们简单的来看一下这个代码段

  • MoneyProvider 咱们必须指定要访问的模型类型(是哪一个provider类)。在这个示例中,咱们要访问 MoneyProvider 那么就写上 Consumer
  • builder 当 ChangeNotifier 发生变化的时候会调用 builder 这个函数
  • context 在每一个 build 方法中都能找到这个参数。
  • state 也就是第2个参数 ChangeNotifier 的实例
  • child 用于优化目的
    121402.png

显然咱们是成功了的,目前已经能够显示状态数据0

  • Provider.of

有时候就像这样,咱们多是单独的想读出来状态的数据,还不想让压岁钱加1呢,也就是说有的时候你不须要模型中的 数据 来改变 UI,可是你可能仍是须要访问该数据。 这个时候

Provider.of<MoneyProvider>(context, listen: false)
复制代码

就像这个样子

Container(
            child: Column(
          children: <Widget>[
            Text('$moneyFromState'),
            Consumer<MoneyProvider>(
              builder: (context, state, child) {
                return Column(
                  children: <Widget>[
                    RaisedButton(
                      onPressed: () {
                        state.addMoneyAndShowOthers();
                      },
                      child: Text('我是一个按钮'),
                    ),
                    Text('${state.money}')
                  ],
                );
              },
            ),
          ],
        ))
复制代码

最后看一下完整的效果吧

121402.png

3 本章总结

这篇provider相关的分享实际上是在1206号左右就开始写了,因为种种缘由到今天才写了差很少,在工做中也有用到状态管理,方案也是用的provider,整体使用下来尤为是和后台通讯结合起来我的并没以为很方便,反而是麻烦了点,多是项目比较小的缘由,因此说仍是那句话不近视的话呢,先能够不要戴眼镜。因为平时开发任务也比较重,加上本身也在维护一个全栈的项目。因此这个系列

仍是会不断的更新,目前github也有几颗星了,下一篇章打算写一下Model 类的转换,但愿看到这儿的你可以多多给个鼓励,但愿你也有点收获,盼望每一个人都能过上儿时梦想搬的生活 这篇分享的相关代码会同步到githubFlutter 项目开发中不用它就用它的状态管理一锅端


-- End but thank you --

相关文章
相关标签/搜索