GetX:依赖注入(Dependency Injection)

什么是依赖注入?

依赖:就是类与类之间的关系,比如 Controller 依赖一个 Service

注入:就是把某个对象的实例交给一个容器(GetX 提供的全局管理器)来保存,需要的时候再从容器里拿,而不是自己手动 new

好处

  • 避免到处 new 对象,生命周期不好管理。
  • 统一管理依赖,方便复用、懒加载、释放内存。
  • 解耦,代码更清晰

GetX的注入方式

Get.put()

final controller = Get.put(CountController());
  • 立即创建并注入到内存。
  • 下次需要时直接从容器里拿,而不是重新创建。
  • 默认情况下,它会在 Controller 不再使用时销毁(除非设为 permanent: true)。

使用场景:你确定马上要用到的对象。

Get.lazyPut()

Get.lazyPut<CountController>(() => CountController());
  • 懒加载:只有在第一次 Get.find() 的时候才会创建对象。
  • 适合比较“重”的对象,不需要一开始就创建。

使用场景:可能要用,但不一定马上用的对象。

Get.putAsync()

putAsync的函数声明

Future<S> putAsync<S>(AsyncInstanceBuilderCallback<S> builder,
       {String? tag, bool permanent = false}) async =>
  GetInstance().putAsync<S>(builder, tag: tag, permanent: permanent);

这个方法如果不要箭头函数简化:

Future<S> putAsync<S>(AsyncInstanceBuilderCallback<S> builder,
{String? tag, bool permanent = false}) async {
  return GetInstance().putAsync<S>(builder, tag: tag, permanent: permanent);
}

这里的 async 其实是 可以省略 的,因为:

  • 这个函数里面没有用 await
  • 直接返回的就是一个 Future<S>
Future<S> putAsync<S>(AsyncInstanceBuilderCallback<S> builder,
    {String? tag, bool permanent = false}) {
  return GetInstance().putAsync<S>(builder, tag: tag, permanent: permanent);
}

putAsync:这实际上是一个异步的方法

  • Future<S>: 方法返回值

  • putAsync: 方法名

  • <S>:声明的泛型

  • putAsync 这个异步方法的参数:(builder,{String? tag, bool permanent = false})

    • 其中:AsyncInstanceBuilderCallback<S> builder 是一个异步回调函数。回调函数是在Get.putAsync() 时执行。
    • {String? tag, bool permanent = false} 是可选参数。

案例

class DatabaseService {
  Future<DatabaseService> init() async {
    // 模拟初始化耗时
    await Future.delayed(const Duration(seconds: 2));
    print("DatabaseService 初始化完成");
    return this;
  }
}

class ApiService {
  Future<ApiService> setup() async {
    // 模拟初始化耗时
    await Future.delayed(const Duration(seconds: 3));
    print("ApiService 初始化完成");
    return this;
  }
}

class CacheService {
  Future<CacheService> init() async {
    // 模拟缓存服务初始化
    await Future.delayed(const Duration(seconds: 1));
    print("CacheService 初始化完成");
    return this;
  }
}

Future<void> main() async {
  print("应用启动中...");

  // 并行启动三个服务,而不是一个一个等
  final dbFuture = Get.putAsync<DatabaseService>(
    () async => DatabaseService().init(),
  );
  final apiFuture = Get.putAsync<ApiService>(() async => ApiService().setup());
  final cacheFuture = Get.putAsync<CacheService>(
    () async => CacheService().init(),
  );

  // 这期间你还能做同步的事情,比如注册一个不用异步的配置服务
  Get.put<String>("AppConfig 已注册");
  print("AppConfig 已注册"); // 这个是同步的

  // 等待所有服务准备好
  await Future.wait([dbFuture, apiFuture, cacheFuture]);

  print("所有异步服务已准备就绪 ✅");

  // 现在就能安全拿到实例
  final db = Get.find<DatabaseService>();
  final api = Get.find<ApiService>();
  final cache = Get.find<CacheService>();

  print("拿到数据库实例: $db");
  print("拿到API实例: $api");
  print("拿到缓存实例: $cache");

  print("应用启动完成!");
}

日志:

I/flutter ( 5091): 应用启动中...
I/flutter ( 5091): AppConfig 已注册
[GETX] Instance "String" has been created
D/ProfileInstaller( 5091): Installing profile for com.example.getx_quickstart_learn
I/flutter ( 5091): CacheService 初始化完成
[GETX] Instance "CacheService" has been created
I/flutter ( 5091): DatabaseService 初始化完成
[GETX] Instance "DatabaseService" has been created
I/flutter ( 5091): ApiService 初始化完成
[GETX] Instance "ApiService" has been created
I/flutter ( 5091): 所有异步服务已准备就绪 ✅
I/flutter ( 5091): 拿到数据库实例: Instance of 'DatabaseService'
I/flutter ( 5091): 拿到API实例: Instance of 'ApiService'
I/flutter ( 5091): 拿到缓存实例: Instance of 'CacheService'
I/flutter ( 5091): 应用启动完成!

回调函数:() async => await DatabaseService().init(),。这是一个匿名函数。完整的函数是:

() async {
  final service = DatabaseService().init();  
  return service;  
}

当你调用 Get.putAsync() 时,GetX 并不会立刻就有一个现成的对象,而是会马上执行你传入的 builder 回调,并等待它返回一个实例(Future<S>)。

DatabaseService().init():创建一个DatabaseService()的实例,并执行init()方法。注意init()方法里面把DatabaseService()这个实例又返回给Gex,让它可以注册到容器中。

class DatabaseService {
  Future<DatabaseService> init() async {
    // 模拟初始化耗时
    await Future.delayed(const Duration(seconds: 2));
    print("DatabaseService 初始化完成");
    return this;
  }
}

Get.putAsync() 与其他注入方式最大的不同就是它返回的是一个Future<S>。所有它可以异步处理很多事情。

然后再统一一个时间点等待

// 等待所有服务准备好
await Future.wait([dbFuture, apiFuture, cacheFuture]);

使用场景:

依赖需要异步初始化。

  • 数据库(如 SQLite)初始化
  • SharedPreferences / Hive 的异步加载
  • 需要网络请求才能构造的对象

Get.create()

Get.create<CountController>(() => CountController());
final controller1 = Get.find<CountController>();
final controller2 = Get.find<CountController>();

controller1controller2 不是同一个对象,因为 Get.find 每次都会 new 一个。

Get.create 都是“工厂模式”,每次 Get.find 都会新建实例

tag的作用

GetX 依赖注入里,tag 就是 “名字标签”,用来区分 相同类型 的不同实例。

为什么需要 tag

  • 如果你在容器里注册了多个 同类型 的对象(比如两个 ApiService),
  • 但你又想分别获取它们,就必须用 tag 来区分。
  • 否则 Get.find<ApiService>() 就会报错:“找到多个实例,不知道要返回哪个”

对于Get.create 的行为,不管有没有 tagGet.create 都是“工厂模式”,每次 Get.find 都会新建实例。因为tag 只是为了区分多个不同的注册(防止冲突),不会改变它“每次都创建新对象”的机制

class MyService {
  final String name;
  MyService(this.name);
}

class MyService2 {
  final String name;
  MyService2(this.name);
}

class MyService3 {
  final String name;
  MyService3(this.name);
}

class MyService4 {
  final String name;
  MyService4(this.name);
}

void main() {
  // ------------------ 1. Get.put 单例 ------------------
  // 注册一个单例
  Get.put<MyService>(MyService("Singleton Service"));

  // 多次获取,看看 hashCode 是否相同
  final s1 = Get.find<MyService>();
  final s2 = Get.find<MyService>();

  print("=== Get.put 单例 ===");
  // s1: Singleton Service, hashCode: 670782972
  print("s1: ${s1.name}, hashCode: ${s1.hashCode}");
  // s2: Singleton Service, hashCode: 670782972
  print("s2: ${s2.name}, hashCode: ${s2.hashCode}");
  // 是否是同一个实例: true
  print("是否是同一个实例: ${identical(s1, s2)}");
  print("");

  // ------------------ 2. Get.create 工厂模式 ------------------
  // 注册一个工厂,每次都会创建新的对象
  Get.create<MyService2>(() => MyService2("Factory Service"));

  final c1 = Get.find<MyService2>();
  final c2 = Get.find<MyService2>();

  print("=== Get.create 工厂模式 ===");
  // c1: Factory Service, hashCode: 506914086
  print("c1: ${c1.name}, hashCode: ${c1.hashCode}");
  // c2: Factory Service, hashCode: 885138364
  print("c2: ${c2.name}, hashCode: ${c2.hashCode}");
  // 是否是同一个实例: false
  print("是否是同一个实例: ${identical(c1, c2)}");

  // ------------------ 3. Get.create 工厂模式  带 tag------------------
  Get.create<MyService3>(() => MyService3("Factory Service"), tag: "myTag");

  final c3 = Get.find<MyService3>(tag: "myTag");
  final c4 = Get.find<MyService3>(tag: "myTag");

  print("=== Get.create 工厂模式 带 tag ===");
  // c3: Factory Service, hashCode: 992340179
  print("c3: ${c3.name}, hashCode: ${c3.hashCode}");
  // c4: Factory Service, hashCode: 451596084
  print("c4: ${c4.name}, hashCode: ${c4.hashCode}");
  // 是否是同一个实例: false
  print("是否是同一个实例: ${identical(c3, c4)}");

  // ------------------ 4. Get.put 单例 带 tag ------------------
  Get.put<MyService4>(MyService4("Singleton Service"), tag: "getTag1");
  Get.put<MyService4>(MyService4("Singleton Service"), tag: "getTag2");

  final s3 = Get.find<MyService4>(tag: "getTag1");
  final s4 = Get.find<MyService4>(tag: "getTag2");
  final s5 = Get.find<MyService4>(tag: "getTag2");

  print("=== Get.put 单例 带 tag ===");
  // s3: Singleton Service, hashCode: 385559188
  print("s3: ${s3.name}, hashCode: ${s3.hashCode}");
  // s4: Singleton Service, hashCode: 693402719
  print("s4: ${s4.name}, hashCode: ${s4.hashCode}");
  // s5: Singleton Service, hashCode: 693402719
  print("s5: ${s5.name}, hashCode: ${s5.hashCode}");
  // 不同tag是不是同一个实例: false
  print("不同tag是不是同一个实例: ${identical(s3, s4)}");
  // 相同tag是不是同一个实例: true
  print("相同tag是不是同一个实例: ${identical(s4, s5)}");

  // 如果在单例模式下,创建了多个实例,但是不用tag去取,那么就会报错
  // final s6 = Get.find<MyService4>();
  // "MyService4" not found. You need to call "Get.put(MyService4())" or "Get.lazyPut(()=>MyService4())"
}

查找依赖

使用 Get.find<T>() 来获取注入的对象。如果没找到,就会抛异常

final controller = Get.find<CountController>();

Get.isRegistered<T>():判断某个类型是否已经注册。

if (Get.isRegistered<HomeController>()) {
  print("已注册");
}

Get.isPrepared<T>():判断依赖是否准备好(即是否已经实例化)。

if (Get.isPrepared<HomeController>()) {
  print("已准备好实例");
}

删除依赖

Get.delete<CountController>();  // 删除某个依赖
Get.reset();                    // 重置所有依赖和路由状态,类似“重启”。
Get.deleteAll() 								// 删除所有依赖

常用于退出页面或切换用户时。

生命周期回调

任何继承 GetxController 的类都有生命周期:

class HomeController extends GetxController {
  @override
  void onInit() {
    super.onInit();
    print("onInit: 初始化");
  }

  @override
  void onClose() {
    super.onClose();
    print("onClose: 销毁");
  }
}

生命周期管理

GetX 会根据对象的使用情况自动释放 Controller(默认在路由销毁时释放)。

可以设置:

// permanent: true → 永久存活,直到手动删除。
Get.put(CountController(), permanent: true); 
flowchart TD
    A[创建控制器 
Controller] --> B{"注入方式"} B -->|"Get.put()" | C["立即创建
并存入容器"] B -->|"Get.lazyPut()" | D["懒加载
第一次使用时创建"] B -->|"Get.putAsync()" | E["异步创建
等待初始化完成"] B -->|"Get.create()" | F["每次 Get.find()
都创建新实例"] C --> G["GetX 容器
(全局存储依赖)"] D --> G E --> G F --> G G --> H["Get.find()
获取依赖对象"] H --> I{"生命周期管理"} I -->|"默认" | J["随路由销毁自动释放"] I -->|"permanent: true" | K["永久存活
需手动删除"] I -->|"手动" | L["Get.delete()
删除依赖"] I -->|"全部" | M["Get.reset()
清空容器"]

×

喜欢就点赞,疼爱就打赏