2026/9/23 22:59:29

GetX 依赖管理实战指南:Get.put / Get.lazyPut / Get.create 与 Bindings 智能生命周期的完整解析

GetX 依赖管理实战指南:Get.put / Get.lazyPut / Get.create 与 Bindings 智能生命周期的完整解析 前端【免费下载链接】getxOpen screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get.项目地址https://gitcode.com/gh_mirrors/ge/getx点击查看免费下载本篇技术指南围绕 GetGetX的依赖管理系统展开详细讲解Get.put、Get.lazyPut、Get.putAsync、Get.create四种实例化方法的参数与适用场景以及Get.find、Get.delete、replace/lazyReplace的使用方式并结合源码剖析 Bindings、BindingsBuilder 与 SmartManagement 的底层实现。读完本文你将能够在不依赖 Provider context 与 InheritedWidget 的情况下完成依赖注入理解permanent与fenix的本质区别并为路由配置自动回收、按需重建的控制器生命周期。本文对应的原始文档位于 documentation/ar_EG/dependency_management.md英文原版见 documentation/en_US/dependency_management.md全部源码佐证均来自当前仓库 lib/get_instance 与 lib/get_state_manager 等目录。一、为什么需要 Get 的依赖管理器Get 提供了一套简单而强大的依赖管理器你不需要 Provider 的context不需要InheritedWidget只需要一行代码就能像获取普通类一样拿到你的 Controller 或 Bloc 实例Controller controller Get.put(Controller()); // 代替 Controller controller Controller();其核心思想是不要在你使用类的内部去实例化它而是把它实例化到 Get 这个全局容器里让它在整个 App 范围内可用。之后你可以像使用普通类一样使用这个 controller或 Bloc 类。使用前有两个重要提示文档原文 Note如果你正在使用 Get 的状态管理器请重点关注 Bindings API它能更轻松地将视图与控制器连接起来Get 的依赖管理与其他部分状态管理、路由是解耦的。即便你的应用已经在使用其他任何状态管理器也无需更换可以直接无冲突地使用这套依赖注入管理器。从源码看这套机制的核心是一个以类型和可选的 tag为 key 的 HashMap。在 extension_instance.dart 中_singl保存了所有通过Get.put注册的实例工厂_getKey生成 key 的逻辑是name null ? type.toString() : type.toString() name即类型名 tag共同决定唯一性这正是同一类型可通过 tag 注册多个实例的原理。二、四大实例化方法Instancing Methods文档将注册依赖的方式归纳为四种方法各有各的参数与适用场景。Get.put()最常用的同步注册Get.put是最常见的插入依赖的方式例如视图对应的 ControllerGet.putSomeClass(SomeClass()); Get.putLoginController(LoginController(), permanent: true); Get.putListItemController(ListItemController, tag: some unique string);Get.put支持的全部参数如下来自文档Get.putS( // 必填想要保存的类实例比如 controller 或任何对象 // 注意S 表示它可以是任意类型的类 S dependency // 可选当你需要多个同类型实例时使用 // 因为通常用 Get.findController() 获取类需要用 tag 告诉 Get 你要哪个实例 // 必须是唯一的字符串 String tag, // 可选默认情况下Get 会在实例不再被使用时将其释放 // 例如被关闭的视图的 controller。但如果这个实例需要在整个 App // 中一直存活比如 SharedPreferences 之类的实例就使用这个参数 // 默认为 false bool permanent false, // 可选允许你在测试中先使用抽象类再将其替换为另一个类并继续测试 // 默认为 false bool overrideAbstract false, // 可选允许你用一个函数来创建依赖而不是直接传依赖本身 // 这个参数不常用 InstanceBuilderCallbackS builder, )结合源码可以更精确地理解put的行为。在 extension_instance.dart 中S putS( S dependency, { String? tag, bool permanent false, }) { _insert( isSingleton: true, name: tag, permanent: permanent, builder: (() dependency)); return findS(tag: tag); }它通过内部方法_insert以isSingleton: true、permanent: permanent的方式把实例登记进_singl随后立即调用findS也就是说put是注册即初始化实例在调用返回时已经可用。_insert中还有一个细节如果该 key 已存在且不是isDirty状态会直接返回避免重复注册覆盖。仓库测试 get_instance_test.dart 验证了Get.put后Get.find返回同一实例。Get.lazyPut()懒加载lazyPut允许你懒加载依赖只有第一次真正使用Get.find时才会实例化。这非常适合计算开销大的类或者像 Bindings 类里集中注册多个类、但当时并不需要全部使用的情况。/// ApiMock 只有在有人第一次使用 Get.findApiMock 时才会被调用 Get.lazyPutApiMock(() ApiMock()); Get.lazyPutFirebaseAuth( () { // ... 如果需要可以在这里写一些逻辑 return FirebaseAuth(); }, tag: Math.random().toString(), fenix: true ) Get.lazyPutController( () Controller() )lazyPut的参数说明来自文档Get.lazyPutS( // 必填一个方法在类第一次被调用时执行 InstanceBuilderCallback builder, // 可选与 Get.put() 相同用于需要同一类的多个不同实例 // 必须是唯一的 String tag, // 可选与 permanent 类似区别在于实例在不再使用时会被丢弃 // 但当再次需要它时Get 会重新创建实例 // 与 bindings API 中的 SmartManagement.keepFactory 行为一致 // 默认为 false bool fenix false )源码层面lazyPut与put一样调用_insert但不会立即执行find实例只是以工厂回调的形式注册同时有个值得注意的默认逻辑fenix: fenix ?? Get.smartManagement SmartManagement.keepFactory,也就是说如果你没有显式传fenix那么fenix的值会跟随全局smartManagement配置当SmartManagement.keepFactory生效时lazyPut默认开启 fenix 行为详见后文 SmartManagement 章节。仓库测试 get_instance_test.dart 验证了 fenix 语义lazyPut(fenix: true)注册的实例在Get.delete后再find会重新得到 count 归零的新实例而fenix: false时delete之后find会直接抛出异常因为实例已从内存移除。Get.putAsync()异步注册当需要注册一个异步创建的实例时比如需要await初始化使用Get.putAsyncGet.putAsyncSharedPreferences(() async { final prefs await SharedPreferences.getInstance(); await prefs.setInt(counter, 12345); return prefs; }); Get.putAsyncYourAsyncClass( () async await YourAsyncClass() )参数说明来自文档Get.putAsyncS( // 必填一个异步方法用于实例化你的类 AsyncInstanceBuilderCallbackS builder, // 可选与 Get.put() 相同用于同一类的多个不同实例 // 必须是唯一的 String tag, // 可选与 Get.put() 相同用于需要在整个 App 中保持存活的实例 // 默认为 false bool permanent false )与Get.put一样putAsync也是注册即初始化区别仅在于初始化过程是异步完成的注册完成后同样立即对实例调用find完成初始化。Get.create()每次 find 都重新制造Get.create是最特别的一个方法文档原话This one is tricky建议先阅读方法之间的差异一节再回来理解。Get.CreateSomeClass(() SomeClass()); Get.CreateLoginController(() LoginController());参数说明来自文档Get.createS( // 必填一个返回类的函数每次调用 Get.find() 时都会用这个函数重新制造一个实例 // 示例Get.createYourClass(() YourClass()) FcBuilderFuncS builder, // 可选与 Get.put() 中的 tag 类似用于同一类的多个实例 // 当你有一个列表、每个列表项都需要自己的 controller 时非常有用 // 必须是唯一的字符串注意参数名是 name 而不是 tag String name, // 可选与 Get.put() 中的 permanent 类似用于需要在整个 App 中 // 保持存活的实例。区别在于Get.create 中 permanent 默认为 true bool permanent true )从当前仓库源码结构看Get.create的底层语义与内部方法spawn一致以isSingleton: false、permanent: true注册工厂于是每次Get.find都会调用 builder 生成全新实例非单例同时因为permanent: true实例不会在页面切换时被回收。文档特别强调Get.create的目标是创建不共享、但不会被释放的实例例如 listView 中每个列表项想要独立且唯一的 controller因此Get.create必须与 GetWidget 配合使用。仓库中 get_view.dart 的注释也印证了这一点Get 会帮你缓存 controller因此可以安全地使用Get.create。测试 get_instance_test.dart 验证了该语义连续两次find得到的实例ct1 ct2为false。三、使用已注册的实例Get.find 与 Get.delete想象你已经导航了无数个路由现在需要之前遗留在某个 controller 里的数据。传统方案需要状态管理器配合 Provider 或 get_it而 Get 只需要一行Get.find不需要任何额外依赖final controller Get.findController(); // 或者 Controller controller Get.find();是的看起来像魔法Get 会找到你的 controller 并把它交给你。哪怕你实例化了 100 万个 controllerGet 也总能给你正确的那个。拿到实例后就能正常恢复数据Text(controller.textFromApi);由于返回值就是普通类你可以对它做任何事int count Get.findSharedPreferences().getInt(counter); print(count); // out: 12345移除一个实例Get.deleteController(); // 通常你不需要这样做因为 GetX 会自动删除不再使用的 controller从源码看find首先检查该 key 是否已注册未注册则抛出提示会提示你调用Get.put或Get.lazyPut已注册则进入_initDependencies初始化流程——如果实例实现了GetLifeCycleMixin_startController会触发其生命周期方法onStart()并依据smartManagement配置决定是否把该依赖与当前路由绑定RouterReportManager.reportDependencyLinkedToRoute这正是路由移除时自动回收依赖的机制来源。此外还有几个实用的配套 APIGet.findOrNullT()已注册则返回实例否则返回null不会抛异常见 extension_instance.dartGet.isRegisteredT({tag})判断实例是否已注册extension_instance.dartGet.isPreparedT({tag})判断是否还有未被初始化的懒加载工厂extension_instance.dartGet.reset()/Get.resetInstance()清空所有注册实例包括永久实例适合在单元测试的tearDown中使用见 extension_instance.dart。四、指定替代实例replace 与 lazyReplace已注册的实例可以被替换为同类或扩展类的实例之后仍可通过原类型获取。这就是replace与lazyReplace的用途abstract class BaseClass {} class ParentClass extends BaseClass {} class ChildClass extends ParentClass { bool isChild true; } Get.putBaseClass(ParentClass()); Get.replaceBaseClass(ChildClass()); final instance Get.findBaseClass(); print(instance is ChildClass); // true class OtherClass extends BaseClass {} Get.lazyReplaceBaseClass(() OtherClass()); final instance Get.findBaseClass(); print(instance is ChildClass); // false print(instance is OtherClass); // true源码实现印证了替换 删除 重新注册replace先读取原实例的permanent状态用delete(force: permanent)删除原实例再以相同permanent值put新实例lazyReplace先删除再以lazyPut注册新工厂且fenix未显式传入时默认取原实例的permanent值。这一能力与Get.put的overrideAbstract参数搭配常用于测试场景先用抽象类注册再替换为具体实现类。仓库测试 get_instance_test.dart 展示了抽象类Service分别通过lazyPut与spawncreate 语义注入Api实现的用法。五、permanent 与 fenix方法间的本质差异先厘清 permanent 与 fenix二者的根本区别在于你希望如何存储实例。先重申默认行为GetX 默认会在实例不再被使用时将其删除。例如屏幕 1 有 controller 1屏幕 2 有 controller 2当你用Get.off()或Get.offNamed()把第一条路由移出栈时controller 1 失去用途随即被清除。使用permanent: truecontroller 不会在这次切换中丢失——非常适合希望在整个应用生命周期中存活的 Service。fenix: true用于不担心它在页面切换时丢失但当你需要它时它必须是活的的服务。它会释放不再使用的 controller/service/类但当你再次需要时会从灰烬中重生重新创建一个新实例。各方法的底层差异文档给出了非常清晰的底层机制对照结合 extension_instance.dart 中的_insert实现可以完全印证方法底层 insert 参数是否立即 find 初始化实例形态典型场景Get.putpermanent: false, isSingleton: true是调用后立即可用单例直接持有dependency视图 ControllerGet.putAsyncpermanent: false, isSingleton: true是异步完成后立即 find单例需要 await 初始化的实例Get.createpermanent: true, isSingleton: false否等界面用到时才调用每次 find 新建builderFunc每次执行列表项各自的独立 Controller需配合 GetWidgetGet.lazyPut不调用 insert注册到工厂区否首次 find 时创建首次 find 后成为单例计算开销大、集中注册但不立即使用的类逐条展开文档原文要点Get.put 与 Get.putAsync遵循相同的创建顺序区别仅是后者使用异步方法两者都会创建并初始化实例直接通过内部方法insert以permanent: false、isSingleton: true插入内存isSingleton参数唯一的作用是决定取用dependency还是FcBuilderFunc作为依赖来源。随后调用Get.find()立即初始化内存中的实例。Get.create顾名思义会创建依赖。与put类似也调用内部insert但permanent变为true、isSingleton变为false因为是创建不可能是单例。由于permanent: true默认享有页面切换不丢失的好处同时Get.find()不会被立即调用而是等待界面真正使用它时才触发。值得强调的是Get.create的设计目标就是创建不共享、但不会被释放的实例。Get.lazyPut惰性过程。实例会被创建但不会立即被调用而是等待使用。与其他方法相反这里不会调用insert而是把实例插入内存的另一区域——负责判断实例是否可以重建的区域文档称之为factory工厂。这样未来要用的东西不会与正在使用的东西混在一起。fenix的魔法就在这里若fenix: false且smartManagement不是keepFactory那么首次Get.find时实例会从工厂区移到公共实例内存区随即默认从工厂区移除若fenix: true实例即使在移入公共区后仍保留在工厂区将来可再次被调用重建。六、Bindings路由、状态与依赖的完整集成Bindings 也许是 Get 包最突出的差异化能力之一路由管理器、状态管理器与依赖管理器的完整集成。当路由从栈中移除时与之相关的所有 controller、变量和对象实例都会从内存中移除如果你使用了 streams 或 timers它们会被自动关闭你无需操心这些。自 Get v2.10 起完整实现了 Bindings API。你不再需要 init 方法甚至可以不手动声明 controller 的类型——可以在合适的地方启动你的 controller 与 services。Binding 类的作用是把依赖注入解耦同时把路由与状态管理器、依赖管理器绑定在一起这让 Get 能知道某个 controller 被使用时正在显示哪个界面也知道在哪里、以何种方式释放它。此外 Binding 类还允许你获得 SmartManager 配置控制权决定依赖是在路由移出栈时、还是使用它的 widget 布局时被清理或两者都不清理。Bindings 类创建一个实现Binding的类注意当前源码中Bindings已标记为废弃推荐使用Binding见 bindings_interface.dartclass HomeBinding implements Binding {}IDE 会自动提示你重写dependencies方法在该方法里注册这个路由要用到的所有类class HomeBinding implements Binding { override void dependencies() { Get.lazyPutHomeController(() HomeController()); Get.putService(() Api()); } } class DetailsBinding implements Binding { override void dependencies() { Get.lazyPutDetailsController(() DetailsController()); } }然后在路由声明中告知 Get 使用该 Binding从而打通路由管理器 ↔ 依赖 ↔ 状态使用命名路由getPages: [ GetPage( name: /, page: () HomeView(), binding: HomeBinding(), ), GetPage( name: /details, page: () DetailsView(), binding: DetailsBinding(), ), ];使用普通路由Get.to(Home(), binding: HomeBinding()); Get.to(DetailsView(), binding: DetailsBinding())这样你就不必再担心应用的内存管理Get 会替你完成。Binding 类会在路由被调用时执行同时你也可以在GetMaterialApp中设置initialBinding集中注册那些一开始就要创建的依赖GetMaterialApp( initialBinding: SampleBind(), home: Home(), );仓库中initialBinding是GetMaterialApp与GetCupertinoApp的标准配置项见 get_cupertino_app.dart示例工程也采用了 Binding 路由的完整分层可参考 example/lib/routes/app_pages.dart 与 example_nav2/lib/app/routes/app_pages.dart。BindingsBuilder默认方式是为每个路由创建实现Bindings的类但也可以用BindingsBuilder回调直接用函数注册任意依赖getPages: [ GetPage( name: /, page: () HomeView(), binding: BindingsBuilder(() { Get.lazyPutControllerX(() ControllerX()); Get.putService(() Api()); }), ), GetPage( name: /details, page: () DetailsView(), binding: BindingsBuilder(() { Get.lazyPutDetailsController(() DetailsController()); }), ), ];这样就不必为每个路由单独创建一个 Binding 类更简洁。文档说明两种方式都完全有效按个人偏好选择即可。七、SmartManagement智能内存管理GetX 默认会释放不再使用的 controller即使发生异常、使用它的 widget 未被正确销毁也不例外——这就是依赖管理的full模式。如果你希望改变 GetX 控制类释放的方式可以通过SmartManagement类设置不同行为。源码中它是一个三值枚举定义于 smart_management.dart默认值为full见 get_interface.dart。如何修改通常你不需要修改此配置如果确实需要在GetMaterialApp上指定即可void main () { runApp( GetMaterialApp( smartManagement: SmartManagement.onlyBuilder // 在这里配置 home: Home(), ) ) }smartManagement是GetMaterialApp的内置配置参数默认SmartManagement.full见 get_material_app.dart。SmartManagement.full默认模式。释放未被使用且未被标记为 permanent的类。绝大多数情况下你都应保持该配置不变如果你是 GetX 新手请不要改动它。SmartManagement.onlyBuilder该模式下只有通过init:启动、或通过 Binding 里的Get.lazyPut()加载的 controller 才会被释放。如果你使用Get.put()、Get.putAsync()或其他任何方式注册SmartManagement 将没有权限删除该依赖。而默认行为full下即使是通过Get.put实例化的 widget 也会被移除这与onlyBuilder不同。注意命名当前仓库源码中的枚举名为onlyBuilder单数部分文档版本写作onlyBuilders复数使用时以源码为准。此外路由侧也有对应逻辑RouterReportManager只有在smartManagement ! SmartManagement.onlyBuilder时才把依赖与路由绑定见 router_report.dart这解释了为何onlyBuilder模式下Get.put注册的实例不受路由生命周期管理。SmartManagement.keepFactory与SmartManagement.full一样在依赖不再使用时移除它但会保留其工厂factory意味着当你再次需要该实例时会重新创建。因此它和lazyPut(fenix: true)效果等价——事实上源码中lazyPut的默认fenix正是跟随smartManagement keepFactory见 extension_instance.dart。八、Bindings 底层工作原理文档给出了非常直观的底层说明Bindings 创建的是临时工厂transitory factories——在你点击跳转另一界面的瞬间创建并在界面切换动画结束的瞬间销毁整个过程快到 analyzer 都无法察觉。当你再次导航到该界面时会重新调用一个新的临时工厂。因此 Bindings 方式优于直接使用SmartManagement.keepFactory但如果你不想创建 Bindings或想把所有依赖放在同一个 Binding 里keepFactory会是有效的替代。工厂本身占用内存极小——它不持有实例只保存一个具有该类形状的函数内存成本非常低。不过由于本库的设计目标是用最少的资源获得最大性能Get 默认连工厂也会移除。二者按需选用即可。九、注意事项与最佳实践Notes文档在结尾给出了两条关键提醒结合仓库实现可归纳为以下最佳实践不要在使用多个 Bindings 时使用SmartManagement.keepFactory。它被设计用于不使用 Bindings或仅在 GetMaterialApp 的 initialBinding 中关联单个 Binding的场景。Bindings 完全是可选的。你也可以直接在类中使用Get.put()与Get.find()而毫无问题。但如果你使用 Services 或任何其他抽象建议使用 Bindings 以获得更好的组织性。GetxService 不可被普通delete删除。源码 lifecycle.dart 说明与 GetxController 不同GetxService 一旦启动就常驻内存例如 Auth 服务唯一的移除方式是Get.reset()。delete方法对实现GetxServiceMixin的实例也会直接返回除非force: true测试 get_instance_test.dart 验证了这一行为普通delete后isRegistered仍为true只有delete(force: true)才会真正移除。permanent 实例的删除需要 force。delete方法对permanent: true的实例会拒绝删除并输出错误日志除非显式传入force: true见 extension_instance.dart。在测试中善用Get.reset()。它会清空所有注册实例包括永久实例与路由绑定clearRouteBindings保证测试之间互不污染见 extension_instance.dart。生命周期钩子如果你的 Controller 混入GetLifeCycleMixin如 GetxController实例创建时会依次触发onStart → onInit → onReady销毁时触发onDelete → onClose见 lifecycle.dart。这让你可以在onClose中关闭 streams、timers、TextEditingController 等资源配合路由回收机制实现无泄漏的内存管理。以上内容完整继承自 documentation/ar_EG/dependency_management.md并通过对 lib/get_instance/src/extension_instance.dart、lib/get_instance/src/bindings_interface.dart、lib/get_core/src/smart_management.dart、lib/get_instance/src/lifecycle.dart 与 test/instance/get_instance_test.dart 等源码和测试的对照对每种方法的底层参数、内存行为与适用场景做了进一步印证与扩展。赞分享前端【免费下载链接】getxOpen screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get.项目地址https://gitcode.com/gh_mirrors/ge/getx点击查看免费下载相关推荐GetX 依赖管理完全指南Get.put、Get.lazyPut、Bindings 与 SmartManagement 的实战与源码解析GetX 依赖管理完全指南Get.put、Get.lazyPut、Bindings 与 SmartManagement 的实战与源码解析 GetX当前仓库前端GetX 依赖管理完全指南从 Get.put 到 Bindings 的注入、生命周期与内存管理GetX 依赖管理完全指南从 Get.put 到 Bindings 的注入、生命周期与内存管理 GetX当前仓库 gh_mirrors/ge/getx 内前端GetX 依赖管理Dependency Management完整指南Get.put、Bindings 与 SmartManagement 源码级解析GetX 依赖管理Dependency Management完整指南Get.put、Bindings 与 SmartManagement 源码级解析 Ge前端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考