Flutter笔记:状态提升、控制器模式、GetX控制器和服务
作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263
邮箱 :291148484@163.com
本文地址:https://blog.csdn.net/qq_28550263/article/details/134350949
【简介】本文聊一聊状态提升、控制器模式,GetX简单状态管理与响应式状态管理、GetX服务的相关思想和使用。
目 录
1. 概述
在Flutter中,状态管理是一个重要的主题,它涉及到如何存储和更新应用的数据以及如何在组件之间共享数据。状态管理的方法有很多种,包括状态提升、控制器模式、响应式编程等。每种方法都有其优点和适用场景。
状态提升是一种简单的状态管理方法,它通过将状态放在组件树的上层来实现状态的共享。但是,状态提升可能会导致组件树过于复杂,而且不适用于全局状态的管理。
控制器模式是一种更加灵活的状态管理方法,它通过将状态封装在控制器对象中,然后通过控制器来管理状态。控制器模式可以有效地管理全局状态,而且可以避免组件树过于复杂。
响应式编程是一种基于数据流的编程模式,它可以使状态的更新变得更加直观和易于理解。在响应式编程中,状态被视为数据流,组件可以监听数据流的变化并根据变化来更新自己。
GetX库提供了一种简单而强大的状态管理方法,它结合了控制器模式和响应式编程的优点。在GetX中,你可以创建一个继承自GetxController或GetxService的类来保存状态,然后在状态改变时调用update()方法或者使用.obs来通知所有监听这个状态的组件。
接下来,本文具体聊一聊状态提升、控制器模式,GetX简单状态管理与响应式状态管理、GetX服务的相关思想和使用。
2. 状态提升模式
2.1 状态提升的基本概念
状态提升(State Lifting) 是一种在 Flutter 中常用的状态管理模式,其基本思想是将状态放在需要这个状态的最小公共祖先组件上。这样,所有需要这个状态的子组件都可以通过祖先组件来访问和修改这个状态。例如,如果两个兄弟组件都需要访问和修改同一个状态,那么这个状态就应该放在它们的父组件上。
详细说来,这种模式通常用于处理以下情况:
-
共享数据: 当多个组件需要访问和共享相同的数据时,将状态提升到这些组件的共同祖先组件中,以便它们可以共享数据。
-
状态同步: 当某个状态需要被多个组件 修改 时,将这个状态提升到共同的父组件,由父组件负责管理和更新状态,然后将状态传递给子组件。
2.2 状态提升的缺陷
虽然状态提升模式在一些简单的场景下工作得很好,但是它也有一些缺陷。
由于每个组件都可以有自己的 内部状态(即局部状态),但当多个组件之间需要共享状态或协同工作时,状态提升就变得非常有用。但是在很多实际开发场景中并不是说你想提升状态就可以提升状态。显而易见的是,如果我们封装一个第三方组件库,不可能在组件发布后去库的使用者的代码里提升状态,但是使用者又有可能需要用到这些状态来控制我们所封装的组件,因此这种情况下状态提升并不是可行的解决方案。
另外一个方面,状态提升在 Flutter 中将很容易导致扩大刷新范围,浪费性能。因此需要一种有效的解决方案来弥补状态提升的不足。这个解决方案就是所谓的 控制器模式。
3. 控制器模式与状态管理
3.1 ChangeNotifier 与 ListerableBuilder(改变通知机制)
顾名思义,ChangeNotifier(改变通知) 可以在状态改变时通知其监听器,是一个可以混入到类中的类。你可以创建一个继承自 ChangeNotifier 的类来保存状态,然后在状态改变时调用 notifyListeners
() 方法来通知所有监听这个状态的组件。
其中需要指出的是,ChangeNotifier 实现了 Listenable 接口,用于提供一个可以发送变化通知的对象:
-
Listenable 接口定义了两个方法:
addListener()
和removeListener()
,这两个方法分别用于添加和移除监听器。任何实现了 Listenable 接口的对象都可以被其他对象监听,当 Listenable 对象的状态发生变化时,它可以通知所有的监听器。(实际上是 发布-订阅模式-见《发布订阅模式原理及其应用》,地址:https://jclee95.blog.csdn.net/article/details/129930814) -
ChangeNotifier 是 Listenable 的一个具体实现,它提供了一个
notifyListeners()
方法,可以在状态改变时调用,以通知所有的监听器。ChangeNotifier 内部维护了一个监听器列表,当你调用addListener()
方法时,监听器会被添加到这个列表中;当你调用removeListener()
方法时,监听器会从这个列表中移除。
例如,你可以创建一个 Counter 类,它继承自 ChangeNotifier,并有一个 count
状态和一个 increment
方法:
class Counter with ChangeNotifier {int _count = 0;int get count => _count;void increment() {_count++;notifyListeners();}
}
然后,你可以使用 ListenableBuilder 来 监听 这个 Counter 对象。ListenableBuilder 函数会在每次 ChangeNotifier 调用 notifyListeners() 以实现 Counter 对象的 状态改变 时被调用。
class CounterWidget extends StatelessWidget {final Counter counter;CounterWidget({ this.counter});Widget build(BuildContext context) {return ListenableBuilder(listenable: counter,builder: (context, _) {return Text('Count: ${counter.count}');},);}
}
其中:
-
当你创建一个 ListenableBuilder 并传入一个 Listenable 对象(ChangeNotifier 是 Listenable 的实现)时,ListenableBuilder 会将自己添加到 Listenable 的监听器列表中。
-
当 Listenable 对象的状态改变并调用
notifyListeners()
方法时,所有的监听器(包括ListenableBuilder)都会收到通知。 -
当 ListenableBuilder 收到通知时,它会调用其builder函数来重建子组件。builder函数会接收到当前的 BuildContext、Listenable 对象,以及一个可选的
child
参数,然后返回一个新的 Widget。 -
ListenableBuilder 会将 builder 函数返回的 新 Widget 显示在屏幕上,从而更新UI的效果。
功能上 ListenableBuilder 和 AnimationBuilder 是一样的。
因此在封装组件时,经常使用控制器来命名这个基于 改变通知(发布订阅) 的类:
/// 计数器控制器类
///
/// - 存储计数器状态;
/// - 提供改变状态的方法作为外部改变状态的接口。
class CounterController with ChangeNotifier {int _count = 0;int get count => _count;void increment() {_count++;notifyListeners();}
}
3.2 控制器模式是如何工作的
控制器模式是一种更加灵活的状态管理模式。在这种模式下,状态被保存在一个或多个控制器对象中,而不是直接保存在组件中。组件可以创建和管理这些控制器对象,也可以通过它们来访问和修改状态。
当状态改变时,控制器会通知所有监听这个状态的组件,这样这些组件就可以根据新的状态来更新自己。因为状态被保存在控制器中,所以它可以被任何可以访问到这个控制器的组件共享,这使得状态管理变得更加灵活和高效。
状态提升时,我们仅仅时把状态放在了层级更高的组件,但是控制器模式将状态放在一个独立的类中,这个类不仅用于存储状态,也提供相应的改变方法。实际上,上一节混入了 ChangeNotifier 的 Counter 类就是一个控制器。
为什么控制器要混入或继承于ChangeNotifier?
因为我们的目标是状态改变后能够及时的更新UI。
在 Flutter 中,提供 ChangeNotifier - ListenableBuilder 机制:
前者用于控制器类——因为控制器类是状态改变的源:
- 所有改变状态变量的操作被封装在控制器类中,以接口的形式暴露给外部使用;
- 在修改的接口方法中,每当数据更改数据后调用
notifyListeners()
方法完成通知监听器。
后者用于 build 方法的某个局部需要依赖于数据更新的UI中:
- ListenableBuilder 的 builder 方法在监听器被通知后使用新的数据进行重构;
4. GetxController 与控制器模式
4.1 简单状态管理
在 GetX 库中,你可以创建一个继承自 GetxController 的类来保存状态,然后在状态改变时调用 update() 方法来通知所有监听这个状态的组件。
class CounterController extends GetxController {int count = 0;void increment() {count++;update();}
}
然后,你可以在UI类中使用 GetBuilder 来监听这个 CounterController 对象。当 CounterController 对象的状态改变时,GetBuilder 会自动重建,从而更新UI。
class CounterWidget extends StatelessWidget {Widget build(BuildContext context) {final counterController = Get.put(CounterController());return Scaffold(appBar: AppBar(title: Text('Counter')),body: Center(child: GetBuilder<CounterController>(builder: (controller) => Text('Count: ${controller.count}'),),),floatingActionButton: FloatingActionButton(child: Icon(Icons.add),onPressed: counterController.increment,),);}
}
仅仅从使用的角度上看:
- 在控制器类中:
- 从继承于 ChangeNotifier 变成了继承于 GetxController;
- 从使用
notifyListeners();
函数通知更新UI变成了 使用update();
函数更新UI。
- 在 UI 的build函数的需要更新处:
- 从使用 ListenableBuilder 类包括需要依赖数据更新部分改成了使用 GetBuilder 类。
4.2 响应式状态管理
GetX的响应式状态管理提供了一种用起来更加方便地方式——不再需要使用 update()
方法。但是这意味着需要在每一个变量上做些手脚——添加.obs
变成“响应式变量”。
在GetX库内部,.obs
是一个扩展方法,它可以用于将普通的Dart值(如String、int、double等)转换为可观察的 Rx 对象。——这是因为在 Dart 语言中,你可以通过 扩展(extension) 语法来为已有的类型添加新的方法或属性。GetX库就使用了这个特性,为Dart的基本类型添加了 .obs
扩展方法。
所以,当你在一个String、int、double等值后面调用.obs时,你实际上是在创建一个新的Rx对象,这个对象的初始值就是这个值。例如,var count = 0.obs;就等价于var count = Rx(0);。
Rx对象是可观察的,你可以使用value属性来获取或设置它的值,也可以使用addListener()方法来添加监听器。当Rx对象的值改变时,所有的监听器都会收到通知。这就是GetX的响应式状态管理的基础。
基于响应式转台管理,控制器类调整为:
// 控制器类
class CounterController extends GetxController {RxInt count = 0.obs;void increment() {count++;}
}
而在UI部分,也不再使用 ChangeNotifier 的ListenableBuilder或者简单状态管理的GetBuilder ,而是由 Obx进行包裹,例如:
// 界面组件
class CounterView extends StatelessWidget {final CounterController controller = Get.put(CounterController());Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Counter App with GetX'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Obx(() => Text('Count: ${controller.count}')),ElevatedButton(onPressed: () => controller.increment(),child: Text('Increment'),),],),),);}
}
5. GetxService 与全局状态管理
实际上 GetxService 和 的功能是很像的,我们可以将控制器类改为一个服务类,比如计数器的例子:
class CounterService extends GetxService {var count = 0.obs;void increment() {count.value++;}
}
然后,你可以在UI类中使用Obx来监听这个CounterService对象。当CounterService对象的状态改变时,Obx会自动重建,从而更新UI:
class CounterWidget extends StatelessWidget {Widget build(BuildContext context) {final counterService = Get.put(CounterService());return Scaffold(appBar: AppBar(title: Text('Counter')),body: Center(child: Obx(() => Text('Count: ${counterService.count.value}')),),floatingActionButton: FloatingActionButton(child: Icon(Icons.add),onPressed: counterService.increment,),);}
}
从使用上看没有什么不同。但是GetxService是一个长生命周期的类,一旦被创建,就会一直存在,直到应用被关闭或者你手动调用Get.reset()。
6. GetxController 和 GetxService的比较
GetxController 和 GetxService 都是 GetX 库中的核心组件,它们都有生命周期方法(onInit
(), onReady
(), onClose
()),并且都可以用于 管理状态和依赖。但是,它们的主要区别在于它们的存活时间和用途不一样。
6.1 GetxController 的应用场景
GetxController 是一个用于状态管理的类,它的实例可以通过 Get.put
(), Get.lazyPut
(), Get.putAsync
(), Get.create
() 等方法创建并绑定到一个生命周期。当与绑定的页面不再需要时, GetX会自动删除GetxController的实例 以释放内存。因此,GetxController通常用于页面和小部件的 局部状态管理,例如用户界面的交互、表单状态、主题颜色等。
6.2 GetxService 的应用场景
GetxService 是一个长期存活在应用中的类,它的实例一旦被创建,就不会被自动删除,除非你手动调用Get.reset
()。因此,GetxService 通常用于需要全局访问和长期存在的服务,例如用户认证、数据库操作、网络请求等。
最常见的就是认证和权限,者往往是整个应用生命周期都需要的,因此我们经常定义各异认证服务,用于处理应用中与认证相关的状态:
class AuthService extends GetxService {Future<AuthService> init() async {// Initialize your class}
}
在GetX中,Get.reset()方法用于清除所有的依赖项,包括GetxController和GetxService的实例。这个方法通常在你需要完全重置应用状态时使用,例如用户注销登录时。
然而,如果你有一个场景需要重置CounterService的状态,例如用户注销登录时,你可以调用Get.reset()。例如:
void userLogout() {// ...其他的注销逻辑...// 重置应用状态Get.reset();
}
需要指出的是,Get.reset()会清除所有的依赖项,包括所有的GetxController和GetxService的实例。如果你只想重置CounterService的状态,你可以在CounterService中添加一个重置状态的方法,然后在需要的地方调用这个方法。比如:
void userLogout() {// ...其他的注销逻辑...// 重置CounterService的状态Get.find<CounterService>().reset();
}
相关文章:
Flutter笔记:状态提升、控制器模式、GetX控制器和服务
Flutter笔记 状态提升、控制器模式、GetX控制器和服务 作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263 邮箱 :291148484163.com 本文地址:https://blog.csdn.net/qq_28550263/article/details/1…...

9.spark自适应查询-AQE之动态调整Join策略
目录 概述动态调整Join策略原理实战 动态优化倾斜的 Join原理实战 概述 broadcast hash join 类似于 Spark 共享变量中的广播变量,Spark join 如果能采取这种策略,那join 的性能是最好的 自适应查询AQE(Adaptive Query Execution) 动态调整Join策略 原…...

CentOs7 NAT模式连接网络
1.配置动态网络 1.1 检查主机网卡配置 检查主机的网络设置 进入控制面板,找到网络共享中心 查看适配器是否都已经开启 1.2 设置虚拟机的网络配置 打开虚拟机网络配置设置,对网卡VMnet8 进行设置 记住网关 全部选择应用,确定 1.3 设置…...

linux安装git
目录 声明 前言 正文 (1)下载git压缩包 (2)git压缩包解压 (3)解压完成后需要进行源码的编译操作 a.首先进去到解压后的文件目录中: b.执行: 编译的过程中可能遇到的问题&am…...

thinkphp6 起步
1、安装 composer create-project topthink/think6.0 tp62、使用多应用模式,你需要安装多应用模式扩展think-multi-app composer require topthink/think-multi-app3、config/app.php中,将 ‘auto_multi_app’ > flase, 改为true; 需要自…...

会员题-力扣408-有效单词缩写
有效单词缩写 字符串可以用 缩写 进行表示,缩写 的方法是将任意数量的 不相邻 的子字符串替换为相应子串的长度。例如,字符串 “substitution” 可以缩写为(不止这几种方法): “s10n” (“s ubstitutio n”) “sub4…...

spring-cloud-stream
系列文章目录 第一章 Java线程池技术应用 第二章 CountDownLatch和Semaphone的应用 第三章 Spring Cloud 简介 第四章 Spring Cloud Netflix 之 Eureka 第五章 Spring Cloud Netflix 之 Ribbon 第六章 Spring Cloud 之 OpenFeign 第七章 Spring Cloud 之 GateWay 第八章 Sprin…...

2.0 熟悉CheatEngine修改器
Cheat Engine 一般简称为CE,它是一款功能强大的开源内存修改工具,其主要功能包括、内存扫描、十六进制编辑器、动态调试功能于一体,且该工具自身附带了脚本工具,可以用它很方便的生成自己的脚本窗体,CE工具可以帮助用户…...

微信小程序数据交互和缓存
目录 前言: 数据交互 1. 发起网络请求 2. WebSocket 2.1实时数据库 3. 微信支付 数据缓存 1. 页面级缓存 2. 内存级缓存 3. 数据缓存策略 优化用户体验 总结 前言: 在开发微信小程序时,数据交互和缓存是非常重要的方面。本文将介…...

kubernetes集群编排——k8s认证授权
pod绑定sa [rootk8s2 ~]# kubectl create sa admin [rootk8s2 secret]# vim pod5.yaml apiVersion: v1 kind: Pod metadata:name: mypod spec:serviceAccountName: admincontainers:- name: nginximage: nginxkubectl apply -f pod5.yamlkubectl get pod -o yaml 认证 [rootk8s…...

rabbitmq下载安装教程
1.首先需要下载erlang和rabbitmq安装包: 官网下载比较慢,通过网盘下载: 链接:https://pan.baidu.com/s/1fM2BrJqefyzUDZD4tfZLIg 提取码:5hsu 2.安装,傻瓜式安装就可以,可以自定义自己要安装的目…...

数据分析实战 | SVM算法——病例自动诊断分析
目录 一、数据分析及对象 二、目的及分析任务 三、方法及工具 四、数据读入 五、数据理解 六、数据准备 七、模型训练 八、模型应用及评价 一、数据分析及对象 CSV文件——“bc_data.csv” 数据集链接:https://download.csdn.net/download/m0_70452407/88…...
Splunk Connect for Kafka – Connecting Apache Kafka with Splunk
1: 背景: 1: splunk 有时要去拉取kafka 上的数据: 下面要用的有用的插件:Splunk Connect for Kafka 先说一下这个Splunk connect for kafka 是什么: What is Splunk Connect for Kafka? Spunk Connect for Kafka is a “sink connector” built on the Kafka Connect…...

Unity | Shader(着色器)和material(材质)的关系
一、前言 在上一篇文章中 【精选】Unity | Shader基础知识(什么是shader)_unity shader_菌菌巧乐兹的博客-CSDN博客 我们讲了什么是shader,今天我们讲一下shder和material的关系 二、在unity中shader的本质 unity中,shader就…...

Leetcode—69.x的平方根【简单】
2023每日刷题(二十七) Leetcode—69.x的平方根 直接法实现代码 int mySqrt(int x) {long long i 0;while(i * i < x) {i;}if(i * i > x) {return i - 1;}return i; }运行结果 二分法实现代码 int mySqrt(int x) {long long left 0, right (l…...
再探单例模式
再探单例模式 一:故事背景二:单例重点三:总结提升 一:故事背景 最近在进行单例模式的复习,今天进行一下对应的总结,分析一下各个设计模式。今天从最简单的单例模式开始。 二:单例重点 概念 一…...

Postman使用json提取器和正则表达式实现接口的关联
近期在复习Postman的基础知识,在小破站上跟着百里老师系统复习了一遍,也做了一些笔记,希望可以给大家一点点启发。 一)使用json提取器实现接口关联 实际项目场景,在财务信息页面,需要上传一个营业执照&…...
【11.10】现代密码学1——密码学发展史:密码学概述、安全服务、香农理论、现代密码学
密码学发展史 写在最前面密码学概述现代密码学量子密码学基本术语加解密的通信模型对称加密PKI通信工作流程 古典密码与分析古代密码的加密古典密码的分析 安全服务香农理论现代密码学乘积密码方案代换-置换网络安全性概念可证明安全性——规约(*规约证明的方案——…...

时间序列预测实战(九)PyTorch实现LSTM-ARIMA融合移动平均进行长期预测
一、本文介绍 本文带来的是利用传统时间序列预测模型ARIMA(注意:ARIMA模型不属于机器学习)和利用PyTorch实现深度学习模型LSTM进行融合进行预测,主要思想是->先利用ARIMA先和移动平均结合处理数据的线性部分(例如趋势和季节性)…...
由日期计算当天是星期几
题目 输入:一个合法的公历日期,格式为“XXXXXXXX”,分别代表年(4 位)、月(2 位)、日(2 位)。 输出:当日对应星期几的英语缩写(3 个字母ÿ…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...
Linux安全加固:从攻防视角构建系统免疫
Linux安全加固:从攻防视角构建系统免疫 构建坚不可摧的数字堡垒 引言:攻防对抗的新纪元 在日益复杂的网络威胁环境中,Linux系统安全已从被动防御转向主动免疫。2023年全球网络安全报告显示,高级持续性威胁(APT)攻击同比增长65%,平均入侵停留时间缩短至48小时。本章将从…...