Flutter 状态管理的实现
作者:叫我丶华仔 发布时间:2023-08-21 02:38:33
一、什么是状态管理
大到整个app的状态,用户使用app是登录状态,还是游客状态;小到一个按钮的状态,按钮是点击选中状态还是未点击状态等等,这些都是状态管理。
二、命令式编程和声明式编程状态管理的区别
iOS是如何管理状态的,一般都是获取这个控件然后设置你想要的状态 当你的 Flutter 应用的状态发生改变时(例如,用户在设置界面中点击了一个开关选项)你改变了状态,这将会触发用户界面的重绘。去改变用户界面本身是没有必要的(例如 widget.setText )—你改变了状态,那么用户界面将重新构建。
三、状态管理中的声明式编程思维
Flutter 应用是 声明式 的,这也就意味着 Flutter 构建的用户界面就是应用的当前状态。
一旦你的界面状态发生改变,就会触发界面的重新绘制,绘制出你想要的界面,而不是像iOS的OC语言那样去获取需要改变状态的控件,然后修改它
四、短时 (ephemeral) 和应用 (app) 状态的区别
Flutter中的状态管理又分为短时状态和应用状态。
短时状态,就是在单个页面需要保持的状态,比如页面数据加载到了第几页,关注按钮是已关注还是未关注等,都是在单个页面需要保持的状态。widget树中其他部分不需要访问这种状态。不需要去序列化这种状态,这种状态也不会以复杂的方式改变。换句话说,不需要使用状态管理架构(例如 ScopedModel, Redux)去管理这种状态。你需要用的只是一个 StatefulWidget。
在下方你可以看到一个底部导航栏中当前被选中的项目是如何被被保存在 _MyHomepageState 类的 _index 变量中。在这个例子中,_index 是一个短时状态。
class MyHomepage extends StatefulWidget {
@override
_MyHomepageState createState() => _MyHomepageState();
}
class _MyHomepageState extends State<MyHomepage> {
int _index = 0;
@override
Widget build(BuildContext context) {
return BottomNavigationBar(
currentIndex: _index,
onTap: (newIndex) {
setState(() {
_index = newIndex;
});
},
// ... items ...
);
}
}
在这里,使用 setState() 和一个变量就能达到管理状态的目的。你的 app 中的其他部分不需要访问 _index。这个变量只会在 MyHomepage widget 中改变。而且,如果用户关闭并重启这个 app,_index会被重置而不会继续保持原来的状态。
应用状态,如果你想在你的应用中的多个部分之间共享一个非短时的状态,并且在用户会话期间保留这个状态,我们称之为应用状态(有时也称共享状态)。 应用状态的一些例子:
1、用户选项
2、登录信息
3、一个社交应用中的通知
4、一个电商应用中的购物车
5、一个新闻应用中的文章已读/未读状态
五、共享状态管理
在 Flutter 中,一般是将存储状态的对象置于 widget 树中对应 widget 的上层,当它发生改变的时候,它对应的widget会从上层开始重构。因为这个机制,所以 widget 无需考虑生命周期的问题—它只需要针对 上层存储数据的对象 声明所需显示内容即可。当内容发生改变的时候,旧的 widget 就会消失,完全被新的 widget 替代。 Flutter原生提供了两个方法来管理共享状态:
5.1 --InheritedWidget
class ADCounterWidget extends InheritedWidget {
// 1. 共享的数据
final int counter;
// 2. 定义构造方法
ADCounterWidget({this.counter, Widget child}): super(child: child);
// 3. 找到当前Widget树中最近的InheritedWidget
static ADCounterWidget of(BuildContext context) {
// 沿着Element树, 去找到最近的ADCounterElement, 从Element中取出Widget对象
return context.dependOnInheritedWidgetOfExactType();
}
// 4. 要不要回调State中的didChangeDependencies方法
@override
bool updateShouldNotify(ADCounterWidget oldWidget) {
return oldWidget.counter != counter;
}
}
上面定义了一个of方法,该方法通过context开始去查找父级的HYDataWidget
updateShouldNotify方法是对比新旧HYDataWidget,是否需要对更新相关依赖的Widget
class HYHomePage extends StatefulWidget {
@override
_HYHomePageState createState() => _HYHomePageState();
}
class _HYHomePageState extends State<HYHomePage> {
int data = 100;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("InheritedWidget"),
),
body: HYDataWidget(
counter: data,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
HYShowData()
],
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
setState(() {
data++;
});
},
),
);
}
}
创建HYDataWidget,并且传入数据(这里点击按钮会修改数据,并且出发重新build)
5.2 --Provider
Provider库有三个主要用到的类:
ChangeNotifier:真正数据(状态)存放的地方
ChangeNotifierProvider:Widget树中提供数据(状态)的地方,会在其中创建对应的ChangeNotifier
Consumer:Widget树中需要使用数据(状态)的地方
第一步 在程序的最顶层创建自己的ChangeNotifier
将ChangeNotifierProvider放到了顶层,这样方便在整个应用的任何地方可以使用CounterProvider
在ChangeNotifier中创建一个私有的_counter,并且提供了getter和setter
在setter中我们监听到_counter的改变,就调用notifyListeners方法,通知所有的Consumer进行更新
void main() {
runApp(ChangeNotifierProvider(
create: (context) => CounterProvider(),
child: MyApp(),
));
}
class CounterProvider extends ChangeNotifier {
int _counter = 100;
intget counter {
return _counter;
}
set counter(int value) {
_counter = value;
notifyListeners();
}
}
第二步 在首页中使用Consumer引入和修改状态
在body中使用Consumer,Consumer需要传入一个builder回调函数,当数据发生变化时,就会通知依赖数据的Consumer重新调用builder方法来构建
在floatingActionButton中使用Consumer,当点击按钮时,修改CounterNotifier中的counter数据
class HYHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("列表测试"),
),
body: Center(
child: Consumer<CounterProvider>(
builder: (ctx, counterPro, child) {
return Text("当前计数:${counterPro.counter}", style: TextStyle(fontSize: 20, color: Colors.red),);
}
),
),
floatingActionButton: Consumer<CounterProvider>(
builder: (ctx, counterPro, child) {
return FloatingActionButton(
child: child,
onPressed: () {
counterPro.counter += 1;
},
);
},
child: Icon(Icons.add),
),
);
}
}
Consumer的builder方法有三个参数:
context,每个build方法都会有上下文,目的是知道当前树的位置
ChangeNotifier对应的实例,也是我们在builder函数中主要使用的对象
child,目的是进行优化,如果builder下面有一颗庞大的子树,当模型发生改变的时候,我们并不希望重新build这颗子树,那么就可以将这颗子树放到Consumer的child中,在这里直接引入即可(注意我案例中的Icon所放的位置)
第四步 创建一个新的页面,在新的页面中修改数据
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("第二个页面"),
),
floatingActionButton: Consumer<CounterProvider>(
builder: (ctx, counterPro, child) {
return FloatingActionButton(
child: child,
onPressed: () {
counterPro.counter += 1;
},
);
},
child: Icon(Icons.add),
),
);
}
}
来源:https://juejin.im/post/5ee31fd26fb9a047de53dc9d


猜你喜欢
- 解决方法有如下两种:第一种如果你 repo sync 了 android 的整个源码,那么可以直接把你的 app 放到 /packages/
- 因为mybatis好使,所以几乎需要操作数据库的时候,我都会使用mybatis,而且在一个正式的项目中,同时存在BS和CS的程序,都使用的M
- 简介由于最近的项目需求,需要在把配置类导入到容器中,通过查询,使用@Import注解就能实现这个功能,@Import注解能够帮我们吧普通配置
- 使用AS创建ADIL文件时AS会在main文件夹下给我们生成一个aidl文件夹和一个相同包名的包,通常我们会把所有和ADIL相关的类或文件放
- 综述在Android系统中,出于对性能优化的考虑,对于Android的UI操作并不是线程安全的。也就是说若是有多个线程来操作UI组件,就会有
- 最近在研究android自定义控件属性,学到了TypedArray以及attrs。大家也可以结合《理解Android中的自定义属性》这篇文章
- 很多学习Android程序设计的人都会发现每个人对代码的写法都有不同的偏好,比较明显的就是对控件响应事件的写法的不同。因此本文就把这些写法总
- 本文实例讲述了Hibernate批量处理海量数据的方法。分享给大家供大家参考,具体如下:Hibernate批量处理海量其实从性能上考虑,它是
- 摘要:想必大家做开发的时候都会用到下拉刷新的控件,现在各种第三方的下拉刷新控件不胜枚举。当然最NB的还是XListView。其他也有针对Gr
- 在正式的进入主题之前,我们先来了解下深拷贝和前拷贝的概念:浅拷贝:会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝,如果属性是基本
- 背景后台系统需要接入 企业微信登入,满足企业员工快速登入系统流程图简单代码说明自定义一套 springsecurity 认证逻辑主要就是 根
- 最近项目中要做一个带进度条的上传文件的功能,学习了AsyncTask,使用起来比较方便,将几个方法实现就行,另外做了一个很简单的demo,希
- 一直使用Eclipse环境开发Android,也尝鲜使用过Android Studio去开发,各种IDE配合Android SDK及SDK原
- SpringBoot集成Mybatis时mybatis.mapper-locations和@MapperScan的作用1、mybatis.m
- package cn.response;import java.awt.Color;import java.awt.Font;import
- 1、原理事务的概念想必大家都很清楚,其ACID特性在开发过程中占有重要的地位。同时在并发过程中会出现一些一致性问题,为了解决一致性问题,也出
- SpringCloud 整合ribbon的时候出现了这个问题java.lang.IllegalStateException: No inst
- timer和timertask是jdk自带的定时任务实现,无需导入第三方jar包来完成1、指定多久之后执行此任务,注意:只会执行一次publ
- 这是Hadoop学习全程记录第1篇,在这篇里我将介绍一下如何在Linux下安装Hadoop1.x。先说明一下我的开发环境:虚拟机:VMwar
- 前言虽然从学java的第一个程序——helloworld至今,已经有好几个年头了。当时自己找资料,看视频,学习了java的输入输出流,多线程