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 个字母ÿ…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...

【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...

在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...