Flutter笔记:GetX模块中不使用 Get.put 怎么办
作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263
邮箱 :291148484@163.com
本文地址:https://blog.csdn.net/qq_28550263/article/details/134006728
目 录
- 1. 概述
- 2. 关于依赖注入
- 1. 手写依赖注入
- 2. 使用依赖注入容器
- 使用 `get_it` 的示例:
- 3. 注解处理器和代码生成
- 3. 什么是 Get.put
- 4. 有时候我还真的不喜欢过多使用Get.put
- 从简单状态管理说起
- Get.put 和 Get.find
- `Get.put`函数
- `Get.find`函数
- 使用 `GetxController.find`创建单例
- 替代StatefulWidget
- 5. 手动使用单例
- 简介
- 实现过程
- 具体案例
1. 概述
2. 关于依赖注入
依赖注入(Dependency Injection,DI)是一种编程模式,它旨在管理和注入类之间的依赖关系,以提高代码的可维护性、可测试性和可扩展性。
在 Dart 语言中,依赖注入可以通过不同的方式实现。下面大概看一下一些常见的实现依赖注入的方式。
1. 手写依赖注入
手写依赖注入的有以下三个步骤:
- 创建依赖项类:首先创建依赖项的类,这些类包括服务、存储库或其他应用程序组件。
- 创建依赖关系:在应用程序中创建依赖项之间的关系,这可以通过构造函数参数或依赖项属性来实现。
- 注入依赖项:将依赖项注入到需要它们的类中,通常在构造函数中进行注入。
比如:
// 步骤 1:创建依赖项类
class DataService {String fetchData() {return "Data from DataService";}
}class Logger {void log(String message) {print(message);}
}class MyService {final DataService dataService;final Logger logger;// 步骤 2:创建依赖关系MyService(this.dataService, this.logger);void doSomething() {String data = dataService.fetchData();logger.log(data);}
}void main() {// 步骤 3:注入依赖项final dataService = DataService();final logger = Logger();final myService = MyService(dataService, logger);myService.doSomething();
}
这个例子中,MyService
类依赖于 DataService
和 Logger
类,这两个依赖项被通过构造函数注入到 MyService
中。对于大型应用程序,可能需要考虑使用依赖注入容器或注解处理器来更方便地管理依赖项。下面两个小节继续介绍。
2. 使用依赖注入容器
依赖注入容器是管理和注入依赖项的工具,它们可以简化依赖管理过程并提供更高级的功能。在 Dart 中,一些流行的依赖注入容器包括 get_it
、injector
和 kiwi
等。
使用 get_it
的示例:
import 'package:get_it/get_it.dart';class DataService {String fetchData() {return "Data from DataService";}
}class MyService {final DataService dataService;MyService(this.dataService);void doSomething() {String data = dataService.fetchData();print(data);}
}void main() {final getIt = GetIt.instance;getIt.registerSingleton<DataService>(DataService());getIt.registerFactory<MyService>(() => MyService(getIt<DataService>()));final myService = getIt<MyService>();myService.doSomething();
}
在上述示例中,使用 get_it
容器注册并获取依赖项。 get_it
允许您注册单例和工厂方法来创建依赖项,并且能够在整个应用程序中轻松访问这些依赖项。
3. 注解处理器和代码生成
另一种方式是使用注解处理器和代码生成工具,如 inject
或 get_it
的 Generator,来自动生成依赖注入代码。这些工具可以根据您的类和注解自动生成依赖注入的代码,减少手动编写依赖注入的工作。
// 使用 inject 注解
import 'package:inject/inject.dart';
class MyModule {DataService provideDataService() => DataService();
}class DataService {String fetchData() {return "Data from DataService";}
}class MyService {final DataService dataService;MyService(this.dataService);void doSomething() {String data = dataService.fetchData();print(data);}
}void main() {final injector = Injector<MyModule>().injector;final myService = injector.get<MyService>();myService.doSomething();
}
在上述示例中,使用了 inject
注解处理器来生成依赖注入代码,简化了依赖项的注册和获取。
无论您选择手动注入、依赖注入容器还是注解处理器,依赖注入都有助于将应用程序的组件解耦,提高代码的可测试性和可维护性。这种模式在构建大型、复杂的应用程序时特别有用,使代码更易于扩展和维护。
3. 什么是 Get.put
Get.put
是 GetX 状态管理库中的一个方法,它用于将一个控制器(Controller)实例放入 GetX 的依赖注入容器中,使得该控制器可以在整个应用程序中被共享和访问。
-
获取依赖:
Get.put
的主要目的是将一个控制器实例添加到 GetX 的依赖注入系统中。这可以让您的控制器在整个应用程序中被轻松访问和共享。 -
依赖注入: GetX 使用依赖注入来管理应用程序的状态。依赖注入是一种设计模式,它有助于管理对象之间的依赖关系。在这种情况下,
Get.put
用于将一个控制器添加到应用程序的依赖项容器中,以便其他部分可以访问它。 -
单例模式: 默认情况下,
Get.put
创建的控制器是单例的。这意味着无论应用程序的哪个部分使用Get.find
或其他方法获取该控制器,都将获得同一个实例。这对于共享应用程序状态和逻辑非常有用。
使用示例:
class MyController extends GetxController {var count = 0.obs;
}// 在应用程序的某个地方将 MyController 放入依赖注入容器中
Get.put(MyController());// 在其他部分可以轻松获取 MyController 实例
MyController myController = Get.find<MyController>();// 使用 MyController 实例
myController.count.value = 42;
总之,Get.put
允许您在整个应用程序中访问和共享控制器,是 GetX 库的关键部分,用于实现轻量级的状态管理和依赖注入。
4. 有时候我还真的不喜欢过多使用Get.put
从简单状态管理说起
在 GetX 模块中提供了太多的解决方案,其中甚至包括 StatefulWidget 的替代方案。GetX 模块官方文档中就直接说,通过 简单状态管理, 你 不再需要 StatefulWidget。比如,经典的几乎在每一个响应式框架都喜欢给出的计数器例子:
// your/path/count_controller.dartclass Controller extends GetxController {int counter = 0;void increment() {counter++;update(); // 当调用增量时,使用update()来更新用户界面上的计数器变量。}
}
import 'your/path/count_controller.dart'// ...其它代码GetBuilder<Controller>(init: Controller(), // 首次启动builder: (_) => Text('${_.counter}',),
)
这个例子中,使用的就是GetX提供的所谓 简单状态管理 的方式。它的状态保存在 Controller 类中,然后在需要用到的地方使 GetBuilder进行包装。
Get.put 和 Get.find
但是——考虑下面一个问题:
你此定义的 Controller 是一个全局状态,它可能不是在一个类、甚至一个文件中的代码中需要使用。那么,你会每次都创建 Controller 类的实例吗?显然这就不能共享数据状态了。
怎么办呢?对于全局共享的数据,很多情况下我们都是使用 Get.put 和 Get.find 来实现的,它们是 GetX 框架提供了一种方便的方式来管理应用程序状态和访问依赖项。
Get.put
函数
Get.put 函数用于将一个对象注册为单例,以便在整个应用程序中重用。它通常用于注册 控制器、服务、数据存储类 等全局性的依赖项,使它们可以在整个应用程序的生命周期内被访问。
使用Get.put
时,您需要提供一个对象的实例。通常,这将是一个控制器类的实例。比如,下面的例子展示了如何在GetX中使用Get.put
来注册和访问一个控制器:
// 1. 创建一个控制器类
class MyController extends GetxController {// 控制器的逻辑和状态
}// 2. 在应用程序初始化时使用Get.put注册控制器
void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {Widget build(BuildContext context) {// 注册MyController控制器为单例Get.put(MyController());return MaterialApp(home: Scaffold(body: Center(child: ElevatedButton(onPressed: () {// 3. 在应用程序的任何地方使用Get.find获取控制器实例final myController = Get.find<MyController>();// 使用myController来访问控制器的逻辑和状态},child: Text('Get Controller'),),),),);}
}
Get.find
函数
`Get.find 函数用于查找和获取已经注册的单例对象(通常是控制器)的实例。它用于在应用程序的任何地方访问注册的对象,而无需手动传递它们。
使用Get.find
时,需要提供对象的类型,以获取已注册对象的实例。可以在应用程序的任何部分使用Get.find
来获取已注册对象的实例,而无需显式传递它们。例如
// 获取已注册的MyController对象实例
final myController = Get.find<MyController>();// 使用myController来访问控制器的逻辑和状态
使用 GetxController.find
创建单例
依据GetX官方的介绍,如果你需要在许多其他地方使用你的控制器,并且在GetBuilder之外,只需在你的控制器中创建一个get,就可以轻松地拥有它。(或者使用Get.find()) 例如:
class Controller extends GetxController {/// 你不需要这个,我推荐使用它只是为了方便语法。/// 用静态方法:Controller.to.increment()。/// 没有静态方法的情况下:Get.find<Controller>().increment();/// 使用这两种语法在性能上没有区别,也没有任何副作用。一个不需要类型,另一个IDE会自动完成。static Controller get to => Get.find(); // 添加这一行int counter = 0;void increment() {counter++;update();}
}
于是,你可以直接访问你的控制器:
FloatingActionButton(onPressed: () {Controller.to.increment(),} child: Text("${Controller.to.counter}"),
),
哦,看起来似乎“贼简单”,不过接下来就是报错:
错误信息提示你,你需要先使用 Get.put 添加作为依赖添加给GetX框架管理。
—— 对的,我们使用的依然是 Get.find(),还需要见Get.put(); 一下。于是你很不情愿地又在 mian 的一大堆代码中添加了一个导入,创建了一个新的控制器实例,并添加了一条依赖注入。
替代StatefulWidget
既然简单状态管理在很多场景下用于替代StatelessWidget,而一旦遇到想考虑拆分组件避免一个组件写的过大时,就可能遇到简单状态需要在多个拆分后的组件中局部进行共享的问题。
由于,毕竟从需求上来说,这些状态还真的没有其它需要用到的地方了,如果都像 GetService一样去注册,写在 GetMaterialApp 中注册的就会特别多。
一般为了方便查找,我考虑写一个 app/injections.dart 文件,同意管理依赖注入项目,比如:
import 'package:get/get.dart';import '../xxx.dart';class DependencyInjection {static void init() {Get.put<GetConnect>(GetConnect());Get.put<GetProvider>(GetProvider());Get.put<GetProvider>(GetProvider());Get.put(AuthService(GetProvider()));Get.put(RecommendationController());Get.put(CartController());}
}
而在 mian.dart 的应用组件中(如“Myapp”),可以实现一个初始化方法,在这里和其它需要初始化的项目一起完成依赖初始化:
class Myapp extends StatelessWidget {const Myapp({super.key});Future<void> initialization(BuildContext context) async {// ... 其它需要初始化的内容DependencyInjection.init(); // 初始化依赖注入}
}
虽然这已经可以使得代码显得更加整洁清晰。但是,对于一些真的仅仅就是部分代码中共享的状态,我还是不想在 app/injections.dart 注册,我希望仅仅在这些代码中调用的地方才创建实例而不是在应用一启动就有GetX管理单例。
不仅是减少没必要的实例创建,同时我页不用大范围的找文件,尤其是项目变大的时候。那么如果不依赖注入,还有什么办法呢。请看下节。
5. 手动使用单例
简介
这个办法就是在控制器类上做一些手脚,保证外部访问的都是同一实例——实际上使用GetX的Get.put方法时,内部管理的也不就是我们注册的单例。
对于很多真的就是简单的局部共享状态的场景,自己实现单例我个人感觉反而更好。首先,你不需要集中于创建代码初期就从各个模块中导入你的各个控制器,也不需要预先在应用初始化时就创建它们的实例,从而将实例添加到GetX依赖中进行管理。这使得mian文件中的代码更加简洁。如果某个局部状态控制器被移除,你也不需要回到mian文件中来对代码进行改动,只需要删除不用的部分。其次,在Dart语言中,为面向对象的单例实现提供了很方便的支持,仅仅三个小步骤就可以实现严格管理单例。接下来就我们看一下具体该怎么搞。
实现过程
为控制器实现单例可以按照下面的三个步骤进行:
- 供一个私有静态属性用于存储唯一的控制器实例;
- 创建用于内部静态构造的构造器,尽量避免提供外部可访问的构造方法;
- 提供一个外部访问的访问器接口,在该接口中:
- 如果还没有创建过控制器,则内部构建数以该类的唯一构造器实例后返回;
- 如果存储的构造器已经非空,则返回该之前创建过的属于该类的唯一构造器实例。
具体案例
基于以上步骤,一个计数器控制器增加单例控制的面向对象实现如下:
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();}
}
接着,就可以在完全不通过 Get.put函数和Get.get函数情况下,改用 CounterController.to 访问器直接使用单例了。
相关文章:
Flutter笔记:GetX模块中不使用 Get.put 怎么办
Flutter笔记 GetX模块中不使用 Get.put 怎么办 作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263 邮箱 :291148484163.com 本文地址:https://blog.csdn.net/qq_28550263/article/details/13400672…...
2023前端面试整理
1. 介绍一下最近参与的项目,负责那些业务,在开发过程中遇到过问题吗?最后是咋样处理的? 之前负责过大小十几个项目,负责过浙里办的整套上架流程,负责过数据大屏统计,后台管理系统文书生成表单生成等,浙政钉…...

文化融合:TikTok如何弥合跨文化差异
随着全球化的加速和数字媒体的崛起,社交媒体平台已经成为连接世界各地人们的纽带。其中,TikTok作为一个引领者,正在以惊人的速度消除跨文化差异,促进文化融合,使人们更加了解和尊重不同背景和传统。 本文将深入探讨Ti…...
asp.net core获取config和env
配置文件的读取和使用 //读取配置文件直接使用 var configModel configuration.GetSection("DataBaseConfig").Get<DataBaseConfigModel>(); //读取配置文件注入到IOC中 services.Configure<AssemblyConfig>(configuration.GetSection("AssemblyC…...

Git不常用命令(持续更新)
今日鸡汤:当你最满足的时候,通常也最孤独;当你最愤慨的时候,通常也最可怜。 此博文会列出一些平时不常用,但是能提高效率的git命令,后续会出IDEA对应的操作步骤 快看看你是不是都用过... 分支(…...
PostPreSql 数据库的一些用法
1、varchar 类型转换成数字 select sum(CAST(order_num AS NUMERIC)) from ads_port_cli_cons_freq_rpt where yr2023 and mon 08...

小工具推荐:FastGithub的下载及使用
前言:FastGithub是基于dotnet开发的一款开源Github加速器,通过自动获取与GitHub相关的IP地址并更新本地hosts文件来提高资源访问速度,使GitHub的访问畅通无阻。原理(复制过来的): ①修改本机的DNS服务指向…...

硬件信息查看工具 EtreCheckpro mac中文版功能介绍
etrecheckpro mac中文版是一款专业的硬件信息查看工具,它能够快速的检测Mac电脑的软硬件信息,加强用户对自己计算机的了解,EtreCheckPro for Mac下载首先会对电脑的软硬件信息进行扫描收集,之后才会显示出来。EtreCheck Mac版报告…...

宝塔Python3.7安装模块报错ModuleNotFoundError: No module named ‘Crypto‘解决办法
前言 今晚遇到一个问题,宝塔服务器上安装脚本的模块时,出现以下报错,这里找到了解决办法 Traceback (most recent call last):File "/www/wwwroot/unifysign/fuck_chaoxing/fuck_xxt.py", line 4, in <module>from Crypto.…...

优化改进YOLOv5算法:加入ODConv+ConvNeXt提升小目标检测能力——(超详细)
为了提升无人机视角下目标检测效果,基于YOLOv5算法,在YOLOv5主干中实现了Omnidimensional Convolution(ODConv),以在不增加网络宽度和深度的情况下提高精度,还在YOLOv5骨干网中用ConvNeXt块替换了原始的C3块,以加快检测速度。 1 Omni-dimensional dynamic convolution …...

ElasticSearch安装、插件介绍及Kibana的安装与使用详解
ElasticSearch安装、插件介绍及Kibana的安装与使用详解 1.安装 ElasticSearch 1.1 安装 JDK 环境 因为 ElasticSearch 是用 Java 语言编写的,所以必须安装 JDK 的环境,并且是 JDK 1.8 以上,具体操作步骤自行百度 安装完成查看 java 版本 …...

JVM | 命令行诊断与调优 jhsdb jmap jstat jps
目录 jmap 查看堆使用情况 查看类列表,包含实例数、占用内存大小 生成jvm的堆转储快照dump文件 jstat 查看gc的信息,查看gc的次数,及时间 查看VM内存中三代(young/old/perm)对象的使用和占用大小 查看元数据空…...
SQL 表达式
SQL 表达式 表达式是计算值的一个或多个值、运算符和SQL函数的组合。这些SQL表达式类似于公式,它们是用查询语言编写的。 您还可以使用它们查询数据库中的特定数据集。 句法 考虑SELECT语句的基本语法,如下所示: SELECT column1, column2, …...

Unity3D 打包发布时生成文件到打包目录
有时候需要自己创建批处理文件或日志文件,在启动程序的同级目录使用,减少手动操作的时间和错误率。主要使用到的是OnPostprocessBuild方法。 1、在工程中的Editor文件夹下创建脚本 2、将文件放入Plugins的相关目录 3.脚本内容 using System.Collection…...
Elasticsearch中使用join来进行父子关联
在使用关系数据库进行开发的过程中,你可能会经常使用外键来表示父表和子表之间的关联关系,在Elasticsearch中,有哪些方法可以用来让开发者解决索引之间一对多和多对多的关联关系的问题呢 1 使用对象数组存在的问题 你可以很方便地把一个对象…...
提供一个springboot使用h2数据库是无法使用脚本并报错的处理方案
环境描述 springboot 2.6.2 mybatis-plus-boot-starter 3.5.1 mysql-connector-java 8.0.11 查阅了很多博客,说是使用spring.datasource.schema或者spring.sql.init.schema-locations指定脚本也均无效。不使用启动脚本,启动后在h2控制台ÿ…...

【组合计数】CF1866 H
Problem - H - Codeforces 题意 思路 不知道这种trick叫什么,昨天VP刚遇到过 设 f[x] 为恰好有一个最大值为 x 的方案数,我们要求这个,那就设 g[x] 为 至少有一个最大值为 x 的方案数,那么答案就是 f[x] g[x] - g[x - 1] 这里…...

JavaSpringbootmysql农产品销售管理系统47627-计算机毕业设计项目选题推荐(附源码)
摘 要 随着互联网趋势的到来,各行各业都在考虑利用互联网将自己推广出去,最好方式就是建立自己的互联网系统,并对其进行维护和管理。在现实运用中,应用软件的工作规则和开发步骤,采用Java技术建设农产品销售管理系统。…...

一文5000字从0到1使用Jmeter实现轻量级的接口自动化测试(图文并茂)
接口测试虽然作为版本的一环,但是也是有一套完整的体系,有接口的功能测试、性能测试、安全测试;同时,由于接口的特性,接口的自动化低成本高收益的,使用一些开源工具或一些轻量级的方法,在测试用…...

蓝桥杯每日一题0223.10.23
第几天 - 蓝桥云课 (lanqiao.cn) 题目描述 题目分析 简单枚举(用k来记录经过的天数) #include<bits/stdc.h> using namespace std; bool is_ren(int n) {if(n % 400 0 || (n % 4 0 && n % 100 ! 0))return true;return false; } int …...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...

消息队列系统设计与实践全解析
文章目录 🚀 消息队列系统设计与实践全解析🔍 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡💡 权衡决策框架 1.3 运维复杂度评估🔧 运维成本降低策略 🏗️ 二、典型架构设计2.1 分布式事务最终一致…...