Flutter State Management状态管理全面分析(二)

简介: Flutter State Management状态管理全面分析
+关注继续查看

看了一下日志输出如图:

image.png

有没有发现一个问题?当MyInheritedWidgetState.addItem,导致setState被调用,然后就触发了WidgetA、WidgetB的build的方法,而WidgetA根本不需要重新build,这不是浪费吗?那么我们如何优化呢?

static MyInheritedWidgetState of([BuildContext context, bool rebuild = true]){
    return (rebuild ? context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited
                    : context.ancestorWidgetOfExactType(_MyInherited) as _MyInherited).data;
  }

通过抽象rebuild属性来控制是否需要重新build

final MyInheritedWidgetState state = MyInheritedWidget.of(context,false);

然后用的时候加以参数控制,改完代码,再看下日志:

image.png

看,已经生效了。你现在是不是对InheritedWidget有了更清晰的认识了呢?但说到它就不得不提InheritedModel,它是InheritedWidget的子类,InheritedModel可以做到部分数据改变的时候才会重建,你可以修改上面例子

class _MyInheritedWidget extends InheritedModel {
  static MyInheritedWidgetState of(BuildContext context, String aspect) {
     return InheritedModel.inheritFrom<_MyInheritedWidget>(context, aspect: aspect).data;
   }
   @override
   bool updateShouldNotifyDependent(_MyInheritedWidget old, Set aspects) {
     return aspects.contains('true');
   }
 }

调用修改为:

///不允许重新build
 final MyInheritedWidgetState state = MyInheritedWidget.of(context,"false");
///允许重新build
final MyInheritedWidgetState state = MyInheritedWidget.of(context,"true");

推荐阅读

inheritedmodel-vs-inheritedwidget

juju.one/inheritedwi…

widget-state-context-inheritedwidget/

InheritedWidget 缺点

通过上面的分析,我们来看下它的缺点

  • 容易造成不必要的刷新
  • 不支持跨页面(route)的状态,意思是跨树,如果不在一个树中,我们无法获取
  • 数据是不可变的,必须结合StatefulWidget、ChangeNotifier或者Steam使用

InheritedWidget 小结

经过一系列的举例和验证,你也基本的掌握了InheritedWidget了吧,这个组件特别适合在同一树型Widget中,抽象出公有状态,每一个子Widget或者孙Widget都可以获取该状态,我们还可以通过手段控制rebuild的粒度来优化重绘逻辑,但它更适合从上往下传递,如果是从下往上传递,我们如何做到呢?请往下看,马上给你解答

Notification


它是Flutter中跨层数据共享的一种机制,注意,它不是widget,它提供了dispatch方法,来让我们沿着context对应的Element节点向上逐层发送通知

具个简单例子看下

class TestNotification extends Notification {
  final int test;
  TestNotification(this.test);
}
var a = 0;
// ignore: must_be_immutable
class WidgetNotification extends StatelessWidget {
  final String btnText;
  WidgetNotification({Key key, this.btnText}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Container(
      child: RaisedButton(
        child: Text(btnText),
        onPressed: () {
          var b = ++a;
          debugPrint(b.toString());
          TestNotification(b).dispatch(context);
        },
      ),
    );
  }
}
class WidgetListener extends StatefulWidget {
  @override
  _WidgetListenerState createState() => _WidgetListenerState();
}
class _WidgetListenerState extends State<WidgetListener> {
  int _test = 1;
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: <Widget>[
          NotificationListener<TestNotification>(
            child: Column(
              children: <Widget>[
                Text("监听$_test"),
                WidgetNotification(btnText: "子Widget",)
              ],
            ),
            onNotification: (TestNotification notification) {
              setState(() {
                _test = notification.test;
              });
              return true;
            },
          ),
          WidgetNotification(btnText: "非子Widget",)
        ],
      ),
    );
  }
}
  • 定义TestNotification通知的实现
  • WidgetNotification 负责通知结果,通过RaisedButton的点击事件,将数据a传递出去,通过Notification提供的dispatch方法向上传递
  • WidgetListener通过Widget NotificationListener来监听数据变化,最终通过setState变更数据
  • WidgetNotification 实例化了两次,一次在NotificationListener的树内部,一个在NotificationListener的外部,经过测试发现,在外部的WidgetNotification并不能通知到内容变化。

所以说在使用Notification的时候要注意,如果遇到无法收到通知的情况,考虑是否是Notification 未在NotificationListener的内部发出通知,这个一定要注意。

同样的思路,我想看下Notification是如何刷新Ui的 在代码里加入了跟通知无关紧要的WidgetC

image.png

image.png

这么看来,你以为是Notification导致的吗?我把这个注释掉,如图

image.png

再运行看下,连续点击了八次

image.png

原来是State的原因,那么这种情况我们如何优化呢?这就用到了Stream了,请接着往下继续看哦。

推荐阅读

flutter-notifications-bubble-up-and-values-go-down

notification

Notification缺点

  • 不支持跨页面(route)的状态,准备的说不支持NotificationListener同级或者父级Widget的状态通知
  • 本身不支持刷新UI,需要结合State使用
  • 如果结合State,会导致整个UI的重绘,效率底下不科学

Notification小结

使用起来很简单,但在刷新UI方面需要注意,如果页面复杂度很高,导致无关紧要的组件跟着刷新,得不偿失,还需要另找蹊径,躲开这些坑,下面我来介绍如何完美躲闪,重磅来袭Stream。

Stream


它其实是纯Dart的实现,跟Flutter没什么关系,扯上关系的就是用StreamBuilder来构建一个Stream通道的Widget,像知名的rxdart、BloC、flutter_redux全都用到了Stream的api。所以学习它才是我们掌握状态管理的一个关键

推荐阅读我自己写的StreamBuilder源码分析大神写的Stream全面分析

Stream 缺点

  • api生涩,不好理解
  • 需要定制化,才能满足更复杂的场景

缺点恰恰是它的优点,保证了足够灵活,你更可基于它做一个好的设计,满足当下业务的设计。

小结


通过对State、InheritedWidget、Notification、Stream的学习,你是不是觉得,Flutter的状态管理也就这些了呢?不一定哈,我也在不断的学习,如果碰到新的技术,继续分享给你们哦。难道这就完了吗?没有,其实我们只是学了第一步,是什么,如何用,还没有讨论怎么用好呢?需要什么标准吗,当然有,下面通过我的项目实战经验来提出一个基本原则,超过这个原则你就是在破坏平衡,请往下看。

状态管理的使用原则


局部管理优于全局

这个原则来源于,Flutter的性能优化,局部刷新肯定比全局刷新要好很多,那么我们在管理状态的同时,也要考虑该状态到底是局部还是全局,从而编写正确的逻辑。

保持数据安全性

用“_”私有化状态,因为当开发人员众多,当别人看到你的变量的时候,第一反应可能不是找你提供的方法,而是直接对变量操作,那就有可能出现想不到的后果,如果他只能调用你提供的方法,那他就要遵循你方法的逻辑,避免数据被处理错误。

考虑页面重新build带来的影响

很多时候页面的重建都会调用build函数,也就是说,在一个生命周期内,build函数是多次被调用的,所以你就要考虑数据的初始化或者刷新怎么样才能合理。

使用成熟状态管理库弊端


  • 增加代码复杂性
  • 框架bug修复需要时间等待
  • 不理解框架原理导致使用方式不对,反而带来更多问题
  • 选型错误导致不符合应用要求
  • 与团队风格冲突不适用 通过了解它们的弊端来规避一些风险,综合考虑,选框架不易,且行且珍惜。

选型原则


  • 侵入性
  • 扩展性
  • 高性能
  • 安全性
  • 驾驭性
  • 易用性
  • 范围性

所有的框架都有侵入性,你同意吗?不同意请左转,前面有个坑,你可以跳过去。目前侵入性比较高的代表ScopedModel,为啥?因为它是用extend实现的,需要继承实现的基本不是什么好实现,你同意吗?同上。 扩展性就不用说了,如果你选择的框架只能使用它提供的几个入口,那么请你放弃使用它。高性能也是很重要的,这个需要明白它的原理,看它到底如何做的管理。安全性也很重要,看他数据管理通道是否安全稳定。驾驭性,你说你都不理解你就敢用,出了问题找谁?如果驾驭不了也不要用。易用性大家应该都明白,如果用它一个框架需要N多配置,N多实现,放弃吧,不合适。简单才是硬道理。

范围性这个特点是flutter中比较明显的,框架选型一定要考虑框架的适用范围,到底是适合做局部管理,还是适合全局管理,要做一个实际的考量。

推荐用法


如果是初期,建议多使用Stream、State、Notification来自行处理,顺便学习源码,多理解,多实践。有架构能力的就可以着手封装了,提供更简单的使用方式 如果是后期,当然也是在前面的基础之上,再去考虑使用Provider、redux等复杂的框架,原则上要吃透源码,否则不建议使用。

注意


你以为使用框架就能万事大吉了?性能优化是一个不变的话题,包括Provider在内的,如果你使用不当,照样出现页面的性能损耗严重,所以你又回到了为啥会这样,请你学习上面的底层逻辑,谢谢🙏

总结


通过这期分享,你是不是对Flutter的状态管理有了一个重新的认识呢?如果对你有帮住,请点一下下面的赞哦。谢谢🙏。

目录
相关文章
|
11月前
|
算法 前端开发 JavaScript
Flutter State Management状态管理全面分析(一)
Flutter State Management状态管理全面分析
425 0
Flutter State Management状态管理全面分析(一)
|
12月前
|
Dart IDE 开发工具
Flutter框架对热重载在项目里的深度运用,状态热重新加载以及静态字段被延迟初始化【Flutter】
Flutter框架对热重载在项目里的深度运用,状态热重新加载以及静态字段被延迟初始化【Flutter】
【Flutter】当项目中使用交互并且使用小工具管理其状态widget的state的详解
【Flutter】当项目中使用交互并且使用小工具管理其状态widget的state的详解
|
Java Android开发
Flutter(八)——状态管理(二)
Flutter(八)——状态管理(二)
191 0
Flutter(八)——状态管理(二)
|
JavaScript 前端开发 Java
Flutter(八)——状态管理(一)
Flutter(八)——状态管理(一)
452 0
Flutter(八)——状态管理(一)
|
前端开发 JavaScript Android开发
Flutter | 关于状态管理,别再被吓着了
本篇是带大家了解并明白 Flutter 中状态管理相关,着眼与实际应用与通俗(说人话)解释,杜绝概念连篇 ❎ 。
106 0
flutter系列之:用来管理复杂状态的State详解
Flutter的基础是widget,根据是否需要跟用户进行交互,widget则可以分为StatelessWidget和StatefulWidget。StatelessWidget只能根据传入的状态进行简单的初始化widget,如果要实现跟用户交互这种复杂的功能,则需要用到StatefulWidget。 但是对于StatefulWidget本身来说,它并不存储任何状态,所有的状态都是存放在和StatefulWidget关联的State中的。
|
JavaScript 前端开发 搜索推荐
Flutter状态State管理
状态管理是声明式编程非常重要的一个概念,我们在前面介绍过Flutter是声明式编程的,也区分声明式编程和命令式编程的区别。 这里,我们就来系统的学习一下Flutter声明式编程中非常重要的状态管理
79 0
Flutter状态State管理
|
存储 Dart 监控
Flutter(二)之有状态的StatefulWidget
在开发中,某些Widget情况下我们展示的数据并不是一层不变的: 比如Flutter默认程序中的计数器案例,点击了+号按钮后,显示的数字需要+1; 比如在开发中,我们会进行下拉刷新、上拉加载更多,这时数据也会发生变化; 而StatelessWidget通常用来展示哪些数据固定不变的,如果数据会发生改变,我们使用StatefulWidget;
242 1
Flutter(二)之有状态的StatefulWidget
|
JavaScript 前端开发 搜索推荐
Day11 - Flutter - 状态State管理
Day11 - Flutter - 状态State管理
133 0
Day11 - Flutter - 状态State管理