Flutter笔记:getX库中的GetView中间件

Flutter笔记
getX库中的GetView中间件

作者李俊才 (jcLee95):https://blog.csdn.net/qq_28550263
邮箱 :291148484@163.com
本文地址:https://blog.csdn.net/qq_28550263/article/details/134301825


如果你对一些原生 Flutter 和 GetX 中的概念不熟悉,如状态提升、控制器、改变通知、GetX控制器、GetX服务,可以在阅读本文前先阅读《状态模式、控制器模式、GetX控制器和服务
》。本文假设你已经有这些基础的情况下,进而介绍针对于 GetX 状态实例采用单例模式的实践,以及 依赖注入 在基于 GetXFlutter 项目中的应用。

另请参考 《状态模式、控制器模式、GetX控制器和服务》,地址:https://blog.csdn.net/qq_28550263/article/details/134350949。


1. GetPage简介

GetViewGetX 库中的一个用于构建视图的组件。它与一个注册的 Controller 关联,并通过 getter 方法提供对该 Controller 的访问。用人话说就是,GetView 简化了 GetX 中对 控制器的访问。

2. 控制器模式思想的简要回顾

2.1 状态提升模式的缺陷

当我们做状态管理的时候,一个常用的手段就是 状态提升(State Lifting)。状态提升是一种在 ReactFlutter 等前端和跨端框架中都十分常见的设计模式,用于管理组件之间共享状态的方法。这种模式通常用于处理以下情况:

  • 共享数据: 当多个组件需要访问和共享相同的数据时,将状态提升到这些组件的共同祖先组件中,以便它们可以共享数据。

  • 状态同步: 当某个状态需要被多个组件 修改 时,将这个状态提升到共同的父组件,由父组件负责管理和更新状态,然后将状态传递给子组件。

首先,如果一个状态需要被很多组件访问,那么这个状态就需要被提升到很高的层级,这会使得组件树变得复杂。其次,状态提升模式使得状态和UI紧密耦合,这可能会导致代码难以维护和测试。另外一个方面,状态提升在Flutter中将很容易导致扩大刷新范围,浪费性能。因此需要一种有效的解决方案来弥补状态提升的不足。这个解决方案就是所谓的 控制器模式

2.2 控制器模式

在原生Flutter中,控制器模式的基本思想是通过创建一个独立的控制器类,将与业务逻辑相关的状态和方法都封装在这个控制器中。控制器类负责管理状态、处理逻辑,而界面组件则负责展示UI,并通过控制器来获取或更新状态。

  1. 独立控制器类: 创建一个独立的控制器类,用于管理相关的状态和逻辑。

  2. 状态封装: 将与业务逻辑相关的状态封装在控制器中,使得控制器成为状态的唯一管理者。

  3. 界面组件简化: 界面组件专注于展示UI,通过控制器获取状态和调用方法,避免在组件内部处理过多的业务逻辑。

  4. 解耦和复用: 通过使用控制器,实现组件的状态和行为的解耦,提高代码的复用性和可维护性。

3. 使用单例模式控制GetX实例数量

3.1 不使用 工厂构造方法 单例模式

基于 Dart 对于面向对象语法中相关功能的支持,为控制器或者服务实现单例可以按照下面的三个步骤进行:

  1. 供一个私有静态属性用于存储唯一的控制器实例;
  2. 创建用于内部静态构造的构造器,尽量避免提供外部可访问的构造方法;
  3. 提供一个外部访问的访问器接口,在该接口中:
  • 如果还没有创建过控制器,则内部构建数以该类的唯一构造器实例后返回;
  • 如果存储的构造器已经非空,则返回该之前创建过的属于该类的唯一构造器实例。

基于以上步骤,一个计数器控制器增加单例控制的面向对象实现如下:

import 'package:get/get.dart';/// 计数器控制器类
class CounterController extends GetxController {// 提供一个私有静态属性用于存储唯一的控制器实例static CounterController? _instance;// 仅提供一个私有构造器防止外部创建实例CounterController._();// 提供一个外部访问的访问器接口static CounterController? get to {// 表示仅仅当 _instance 为 null 时,内部构造该控制器实例_instance ??= CounterController._();return _instance;}// 下面表示一些状态变量个状态相关的内容...int counter = 0;void increment() {counter++;update();}
}

3.2 使用 工厂构造方法 实现单例模式

在Dart中,我们可以使用工厂构造函数来实现单例模式。工厂构造函数与普通构造函数不同,它不一定每次都会创建一个新的实例。相反,工厂构造函数可以从缓存中返回一个已存在的实例,或者返回一个子类型的实例。

以下是如何使用工厂构造函数来实现CounterController的单例:

import 'package:get/get.dart';class CounterController extends GetxController {// 提供一个私有静态属性用于存储唯一的控制器实例static CounterController? _instance;// 使用工厂构造函数来实现单例factory CounterController() {if (_instance == null) {_instance = CounterController._internal();}return _instance!;}// 私有构造函数CounterController._internal();// 下面表示一些状态变量个状态相关的内容...int counter = 0;void increment() {counter++;update();}
}

在这个例子中,我们首先定义了一个私有的静态属性_instance来存储单例对象。然后,我们定义了一个工厂构造函数CounterController(),它会检查_instance是否为null,如果为null,就创建一个新的CounterController实例并赋值给_instance,否则就直接返回_instance。这样,我们就可以确保CounterController的单例性。

然后,我们定义了一个私有的构造函数CounterController._internal(),这个构造函数只能在类的内部被调用,这样可以防止外部代码创建CounterController的新实例。

最后,我们定义了一些状态变量和状态相关的方法,这些与单例模式无关,只是CounterController的业务逻辑。

4. GetX 依赖注入

4.1 依赖注入的基本概念

依赖注入(Dependency Injection,简称DI)是一种设计模式,它的主要目标是实现松耦合。在依赖注入中,一个对象不需要直接创建或管理它的依赖项,而是通过外部的方式(例如构造函数、属性或方法)来接收这些依赖项。这样,对象就可以专注于自己的业务逻辑,而不需要关心如何创建和管理依赖项。

依赖注入的主要优点是提高了代码的可测试性和可重用性。由于对象不再直接创建和管理依赖项,所以我们可以在测试时轻松地替换依赖项,例如使用模拟对象(Mock Object)来代替真实的依赖项。此外,由于依赖项是通过外部的方式来提供的,所以我们可以在不同的场景下使用不同的依赖项,这增加了代码的可重用性。

4.2 GetX中的依赖注入原理

GetX 的依赖注入原理主要基于 Dart 的映射(Map)数据结构和 GetX 的依赖管理容器。在 GetX 中,依赖管理容器是一个全局的映射表,键是依赖项的类型,值是依赖项的实例。

当你调用 Get.put()Get.lazyPut()Get.putAsync()Get.create() 等方法时,GetX 会首先检查依赖管理容器中是否已经存在相同类型的依赖项。如果不存在,GetX 会创建一个新的依赖项,并将它的类型和实例添加到依赖管理容器中。如果已经存在,GetX 会根据具体的方法来决定是否替换已存在的依赖项。

当你调用 Get.find() 方法时,GetX 会根据指定的类型从依赖管理容器中查找对应的依赖项。如果找到了,就返回这个依赖项;如果没有找到,就抛出一个异常。

当你调用 Get.delete() 方法时,GetX 会根据指定的类型从依赖管理容器中删除对应的依赖项。如果这个依赖项是一个GetxControllerGetxServiceGetX还会调用它的 onClose()方法。

通过这种方式,GetX 可以实现依赖项的自动管理,包括创建、查找、删除和生命周期管理。这大大简化了依赖管理的复杂性,使得开发者可以更专注于业务逻辑的实现。

4.3 基于GetX依赖注入使用单例

注入依赖

基于GetX依赖注入使用单例时,需要调用 Get.put()、Get.lazyPut()、Get.putAsync()、Get.create() 创建一个新的依赖项,然后将它注册到依赖管理容器中。每个依赖项都有一个对应的类型,GetX会根据这个类型来管理和查找依赖项。

例如:

void main() {Get.put(CounterController());runApp(MyApp());
}

由于往往需要创建使用多个依赖,这种情况下,我个人习惯于在项目中创建一个 app_injections.dart 文件用于存放这些依赖项。例如:

class DependencyInjection {static void init() {Get.lazyPut(() => AuthService(AuthProvider())); Get.lazyPut(() => ChatService()); // 继续添加其它依赖项}
}

另外,使用 GetX 时,则顶层组件使用的是其包装的 GetMaterialApp 以替代 MaterialApp。你可以选择在 onInit 中完成一些初始化工作, 比如:

GetMaterialApp(//...省略其它内容onInit: () async {await initialization(context); // 等待初始化完成},
)

其中,为了代码简洁,可以单独写一个 initialization 方法:

Future<void> initialization(BuildContext context) async {// 可以在这里完成依赖项初始化DependencyInjection.init();
}

获取依赖

你调用Get.find()方法时,GetX会根据指定的类型从依赖管理容器中查找对应的依赖项。如果找到了,就返回这个依赖项;如果没有找到,就抛出一个异常,因此请确保你的依赖被正确注入。

final counterController = Get.find<CounterController>();

删除依赖项

当你调用Get.delete()方法时,GetX会根据指定的类型从依赖管理容器中删除对应的依赖项。如果这个依赖项是一个GetxController或GetxService,GetX还会调用它的onClose()方法。如:

Get.delete<CounterController>();

生命周期管理

如果一个依赖项是一个 GetxControllerGetX 会自动管理它的生命周期。当这个依赖项被创建时,GetX会调用它的 onInit() 和 onReady()方法;当这个依赖项被删除时,GetX 会调用它的 onClose() 方法。

相比之下GetxService 的实例在整个应用生命周期中都是可用的,它们不会被自动回收。这是因为GetxService通常用于提供全局的、需要长期存在的服务,例如用户认证、数据库操作、网络请求等。

作用域管理

GetX支持作用域管理,你可以使用Get.createScope()和Get.deleteScope()方法来创建和删除作用域。每个作用域都有一个唯一的标识符,你可以在创建和获取依赖项时指定作用域。

Get.createScope('myScope');
Get.put(CounterController(), tag: 'myScope');
final counterController = Get.find<CounterController>(tag: 'myScope');
Get.deleteScope('myScope');

4.4 GetX 控制器内置的一种简化的单例获取方法

在GetX中,除了使用Get.find()方法来获取控制器的单例实例外,还可以使用.to的方式来获取。这是GetX为我们提供的一种简化的获取单例的方式。

当你创建一个继承自GetxController的类时,你可以在类的内部定义一个静态的to属性,这个属性会自动获取这个类的单例实例。这样,你就可以直接通过.to的方式来获取这个类的单例实例,而不需要每次都调用Get.find()方法。

例如:

class CounterController extends GetxController {static CounterController get to => Get.find();var count = 0.obs;void increment() {count.value++;}
}

在这个例子中,我们定义了一个静态的to属性,它会自动调用Get.find()方法来获取CounterController的单例实例。然后,我们就可以在其他地方通过CounterController.to的方式来获取这个单例实例:

CounterController.to.increment();

显而易见的是,这种方式的好处是,它可以让代码看起来更简洁,而且更符合面向对象的设计原则。你心动了吗?赶紧试一试修改你大量的 find 吧。

5. GetView中间件

在你想着要修改 find 使用 4.4 小节 的方式时,有时候可能使用更简单的 GetView 就可以完成你的需求。

由于有时候在 Flutter 中,我们经常需要在不同的组件中访问和操作同一个控制器实例。为了避免在每个组件中都写一遍 Get.find()GetX 库提供了一个 GetView 中间件。

GetView 是一个抽象类,它继承自 StatelessWidget,并提供了一个 controller 属性,这个属性会自动获取对应类型的控制器实例,因此相比于在 StatelessWidget 中自己获取单例, 使用起GetView 来会更加地方便。

GetView 的主要功能是简化控制器的获取。当你创建一个继承自 GetView 的组件时,你只需要指定控制器的类型,然后你就可以在组件中直接使用 controller 属性来访问控制器实例。

例如,还是哪个计数器地例子,我们再一次进行改进。我们创建一个 CounterController 类,它继承自 GetxController 并有一个 count 状态和一个 increment 方法:

class CounterController extends GetxController {var count = 0.obs;void increment() {count.value++;}
}

然后,我们创建一个继承自GetView的组件:

class CounterPage extends GetView<CounterController> {Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Counter')),body: Center(child: Obx(() => Text('Count: ${controller.count.value}')),),floatingActionButton: FloatingActionButton(child: Icon(Icons.add),onPressed: controller.increment,),);}
}

在这个例子中,GetView 会自动获取一个 CounterController 的实例,并将它赋值给 controller 属性。然后,我们就可以在组件中直接使用 controller 属性来访问和操作 CounterController 的实例。这样,我们就不需要在每个组件中都写一遍 Get.find() 了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/143894.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第3章:搜索与图论【AcWing】

文章目录 图的概念图的概念图的分类有向图和无向图 连通性连通块重边和自环稠密图和稀疏图参考资料 图的存储方式邻接表代码 邻接矩阵 DFS全排列问题题目描述思路回溯标记剪枝代码时间复杂度 [N 皇后问题](https://www.luogu.com.cn/problem/P1219)题目描述全排列思路 O ( n ! …

聚观早报 |英伟达发布H200;夸克发布自研大模型

【聚观365】11月15日消息 英伟达发布H200 夸克发布自研大模型 iQOO 12系列开启销售 红魔9 Pro配置细节 禾赛科技第三季度营收4.5亿元 英伟达发布H200 全球市值最高的芯片制造商英伟达公司&#xff0c;正在升级其H100人工智能处理器&#xff0c;为这款产品增加更多功能&am…

操作符——C语言初阶

一.算数操作符&#xff1a; - * / % 、-、*、/这四个运算符均可用于整数及浮点数的运算。 当使用/运算符时&#xff0c;如果两个操作数均为整型&#xff0c;那么执行整数除法&#xff0c;运算结果也为整型&#xff1b;如果两个操作数至少一个为浮…

点云从入门到精通技术详解100篇-双传感器模式的非结构化环境检测与识别(续)

目录 3.3 KD-Tree算法点云特征提取 3.3.1 KD-Tree算法的基本原理 3.3.2边沿点提取 3.4道路边沿线拟合验证

macOS 13.6 及后续系统安装 Asahi Linux 将破坏引导

导读Asahi Linux 是一个致力于为 Apple Silicon 设备带来 Linux 支持的项目&#xff0c;日前有用户反馈称&#xff0c;若在相关设备上安装了 macOS 13.6-14&#xff0c;再安装 Asahi Linux &#xff0c;就会导致系统引导失败&#xff0c;出现“黑屏”情况。 目前 Asahi Linux 项…

并发线程特性-可见性和有序性

2 可见性 2.1 什么是可见性 可见性问题是基于CPU位置出现的&#xff0c;CPU处理速度非常快&#xff0c;相对CPU来说&#xff0c;去主内存获取数据这个事情太慢了&#xff0c;CPU就提供了 L1&#xff0c;L2&#xff0c;L3的三级缓存&#xff0c;每次去主内存拿完数据后&#x…

对产品实现汇率换算服务(将两个CompletableFuture对象整合起来,无论它们是否存在依赖)

需求 有一家商店提供的价格是以欧元&#xff08;EUR&#xff09;计价的&#xff0c;但是你希望以美元的方式提供给你的客户。你可以用异步的方式向商店查询指定商品的价格&#xff0c;同时从远程的汇率服务那里查到欧元和美元之间的汇率。当二者都结束时&#xff0c;再将这两个…

单片机实验(一)

前言 实验一&#xff1a;用单片机控制多只数码管(屏)分别左、右滚动显示自己完整的学号&#xff1b; 实验二&#xff1a;用单片机控制LED1616点阵交替正序、逆序显示自己的中文姓名。 参考链接&#xff1a; LED数码管的静态显示与动态显示&#xff08;KeilProteus&#xff0…

Android网络模块基本实现步骤

Android网络模块主要是用于访问网络和获取数据&#xff0c;下面是网络模块的基本实现步骤&#xff1a; 选择网络框架&#xff1a;Android中常用的网络框架有HttpURLConnection、OkHttp、Volley和Retrofit等。最新的版本已经支持使用Kotlin协程完成网络请求&#xff0c;可以根据…

关于对Java中volatile关键字的理解与简述

【版权声明】未经博主同意&#xff0c;谢绝转载&#xff01;&#xff08;请尊重原创&#xff0c;博主保留追究权&#xff09; https://blog.csdn.net/m0_69908381/article/details/134430096 出自【进步*于辰的博客】 启发之作&#xff1a;Java volatile关键字最全总结&#xf…

RK3588平台开发系列讲解(项目篇)嵌入式AI的学习步骤

文章目录 一、嵌入式AI的学习步骤1.1、入门Linux1.2、入门AI 二、瑞芯微嵌入式AI2.1、瑞芯微的嵌入式AI关键词2.2、AI模型部署流程 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; &#x1f4e2; 本篇将给大家介绍什么是嵌入式AI。 一、嵌入…

C++二分查找算法:132 模式解法二枚举2

题目及解法一&#xff1a; https://blog.csdn.net/he_zhidan/article/details/134362273 分析 第一步&#xff0c;选择各3对应的1&#xff0c;如果有多个符合对应最小的1&#xff0c;记录num[0,j)中的最小值iMin&#xff0c;如果nums[j]大于iMin&#xff0c;则m3To1 [nums[j…

基于平衡优化器算法优化概率神经网络PNN的分类预测 - 附代码

基于平衡优化器算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于平衡优化器算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于平衡优化器优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针…

STM32与ZigBee技术在智能家居无线通信中的应用研究

一、引言 智能家居系统是利用物联网技术将家庭各种设备进行互联互通&#xff0c;实现智能化控制和管理的系统。在智能家居系统中&#xff0c;无线通信技术起着至关重要的作用&#xff0c;而STM32微控制器和ZigBee技术则是实现智能家居无线通信的关键技术。本文将对STM32与ZigB…

Mysql Innodb Cluster集群搭建 - docker

Mysql Innodb Cluster集群搭建 - docker 背景搭建环境架构图3台机器如下:修改三台机器的ip域名映射如下,并重启网络使其生效部署mysql server实例通过docker启动三台mysql server实例,需要映射数据请自行更改配置加入-v启动第一台mysql-server启动第二台mysql-server启动第三…

图像分类:弥合像素和理解之间的差距

一、介绍 在人工智能的广阔领域中&#xff0c;图像分类作为一种关键应用脱颖而出&#xff0c;它无缝地融合了计算机视觉和机器学习的复杂性。图像分类的核心是训练机器对数字图像中的对象或场景进行识别和分类。这项技术有着广泛的应用&#xff0c;从自动驾驶汽车和医疗诊断到社…

计算机网络中的面向连接与无连接

目录 面向连接和无连接在计算机网络中是如何理解的面向连接和无连接的通信在路由选择上有哪些区别 面向连接和无连接在计算机网络中是如何理解的 在计算机网络中&#xff0c;面向连接和无连接是两种核心的网络通信方式&#xff0c;它们决定了数据包如何传输和接收。 面向连接&…

CPU vs GPU:谁更适合进行图像处理?

CPU 和 GPU 到底谁更适合进行图像处理呢&#xff1f;相信很多人在日常生活中都会接触到图像处理&#xff0c;比如修图、视频编辑等。那么&#xff0c;让我们一起来看看&#xff0c;在这方面&#xff0c;CPU 和 GPU 到底有什么不同&#xff0c;哪个更胜一筹呢&#xff1f; 一、C…

C# new 和 override 的区别

在C#中子类继承抽象类的时候&#xff0c;new 和override都可以用来修饰子类方法&#xff0c;但两者之间是有区别的。 相同点&#xff1a; 它们都是子类在覆写基类方法时&#xff0c;修饰子类同名方法用的&#xff0c;都是为了隐藏基类的同名方法在实例化子类对象的时候&#…

Hive使用max case when over partition by 实现单个窗口取两个窗口的值(单个开窗函数,实际取两个窗口)

一、Hive开窗函数根据特定条件取上一条最接近时间的数据&#xff08;单个开窗函数&#xff0c;实际取两个窗口&#xff09; 针对于就诊业务&#xff0c;一次就诊&#xff0c;多个处方&#xff0c;处方结算时间可能不一致&#xff0c;然后会有多个AI助手推荐用药&#xff0c;会…