Flutter之GetX依赖注入Bindings使用详解

5,169 阅读5分钟

「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战

GetX 依赖注入相关文章:

本文将介绍在 GetX 依赖注入中 Bindings 的作用和使用。

作用

Bindings 主要配合 GetX 路由和依赖一起使用,作用是在路由跳转页面加载时注入当前页面所需的依赖关系。Bindings 的好处是能统一管理页面的依赖关系,当业务复杂时可能一个页面需要注入大量的依赖,此时使用 Bindings 能更方便的维护页面的依赖关系。

使用

前面说了 Bindings 需要结合 GetX 路由一起使用,而 GetX 路由分为普通路由别名路由,接下来分别看看如何使用。

首选创建一个自定义 Bindings 继承自 Bindings,比如计数器界面,创建一个 CounterBindings 在 dependencies 方法中注入 CounterController, 代码如下:

class CounterBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => CounterController());
  }
}

上面通过 lazyPut 懒加载方式注入的,也可以使用前面讲到的其他注入方式注入。

普通路由

普通路由使用 Bindings 很简单,在路由跳转时加上 binding 参数传入创建的自定义 Bindings 对象即可:

Get.to(CounterPage(), binding: CounterBinding());

Get.off(CounterPage(), binding: CounterBinding());

Get.offAll(CounterPage(), binding: CounterBinding());

这样通过路由进入 CounterPage 时就会自动调用 CounterBinding 的 dependencies 方法初始化注入对应的依赖,在 CounterPage 中就能正常使用 Get.find 获取到注入的 CounterController 对象。

别名路由

Flutter应用框架搭建(一)GetX集成及使用详解 一文中介绍了别名路由的使用,需要先创建 GetPage 确定别名与页面的关系并配置到 GetMaterialAppgetPages 中,使用时通过 Get.toNamed 进行路由跳转,而 Get.toNamed 方法并没有 binding 参数用于传入 Bindings。

使用别名路由时需要在创建 GetPage 时就传入 Bindings 对象,如下:

GetPage(name: "/counter", page: () => CounterPage(), binding: CounterBinding());

跳转时正常使用 Get.toNamed 就能达到同样的效果。

Get.toNamed("/counter");

别名路由与普通路由对于 Bindings 的使用上还有一个区别,普通路由只有一个 binding 参数,只能传入一个 Bindings 对象,而别名路由除了 binding 参数以外还有一个 bindings 参数,可传入 Bindings 数组。使用如下:

GetPage(
  name: "/counter",
  page: () => CounterPage(),
  binding: CounterBinding(),
  bindings: [PageABinding(), PageBBinding(), PageCBinding()]);

那 bindings 的作用是什么呢?为什么需要传入一个数组?

通常一个页面只需要一个 Bindings 用来管理页面的依赖,但是当使用到 ViewPager 等嵌套组件或者存在页面嵌套时,因为页面中嵌套的页面不是通过路由加载出来的所以无法自动调用 Bindings 的 dependencies 方法来初始化依赖关系,而嵌套的页面有可能也需要单独显示,为了提高页面的复用性也会为嵌套页面创建 Bindings ,这样当页面嵌套使用时就可以把嵌套页面的 Bindings 传入到主页面路由的 bindings 中,使用如下:

/// ViewPager 页面路由
GetPage(
  name: "/viewpager",
  page: () => ViewPagerPage(),
  binding: ViewPagerBinding(),
  bindings: [PageABinding(), PageBBinding(), PageCBinding()]);

/// 单独 PageA pageB pageC 路由
GetPage(
  name: "/pageA",
  page: () => PageAPage(),
  binding: PageABinding(),);
GetPage(
  name: "/pageB",
  page: () => PageBPage(),
  binding: PageBBinding(),);
GetPage(
  name: "/pageC",
  page: () => PageCPage(),
  binding: PageCBinding(),);

/// 使用
Get.toNamed("/viewpager");

Get.toNamed("/pageA");
Get.toNamed("/pageB");
Get.toNamed("/pageC");

这样就能实现,当在 ViewPager 中使用时也能初始化 ViewPager 中嵌套页面的依赖,单独使用某个 Page 时也能正常加载依赖。

原理

前面讲了 Bindings 的作用和使用方法,下面通过源码简单分析一下 Bindings 的原理。

Bindings 是一个抽象类,只有一个 dependencies 抽象方法,源码如下:

abstract class Bindings {
  void dependencies();
}

在页面路由中注册 Bindings 后,页面初始化时会调用 Bindings 的 dependencies 方法,初始化页面依赖,其调用是在 GetPageRoutebuildContent 中,而 GetPageRoute 是继承至 Flutter 的 PageRoute 即在路由跳转加载页面内容时调用, 核心源码如下:

Widget _getChild() {
  if (_child != null) return _child!;
  final middlewareRunner = MiddlewareRunner(middlewares);

  /// 获取 Bindings
  final localbindings = [
    if (bindings != null) ...bindings!,
    if (binding != null) ...[binding!]
  ];
  /// 调用中间件的 onBindingsStart 方法
  final bindingsToBind = middlewareRunner.runOnBindingsStart(localbindings);
  
  /// 调用 Bindings 的 dependencies 方法
  if (bindingsToBind != null) {
    for (final binding in bindingsToBind) {
      binding.dependencies();
    }
  }

  final pageToBuild = middlewareRunner.runOnPageBuildStart(page)!;
  _child = middlewareRunner.runOnPageBuilt(pageToBuild());
  return _child!;
}

@override
Widget buildContent(BuildContext context) {
  return _getChild();
}

源码核心代码就是在创建页面 Widget 时获取路由传入的 Bindings ,然后依次调用 Bindings 的 dependencies 方法。

其中:

  /// 获取 Bindings
  final localbindings = [
    if (bindings != null) ...bindings!,
    if (binding != null) ...[binding!]
  ];
  /// 调用中间件的 onBindingsStart 方法
  final bindingsToBind = middlewareRunner.runOnBindingsStart(localbindings);
  
  /// 调用 Bindings 的 dependencies 方法
  if (bindingsToBind != null) {
    for (final binding in bindingsToBind) {
      binding.dependencies();
    }
  }

就是将路由中传入的 bindings 和 binding 取出放入同一个数组。然后依次调用 dependencies 方法,其中 binding 就是路由或 GetPage 中传入的 binding 参数,而 bindings 就是使用别名路由时在 ``GetPage 中传入的 Bindings 数组。

总结

本文通过介绍在 GetX 依赖注入中 Bindings 的作用以及使用方法,再结合 GetX 的源码分析了 Bindings 的实现原理,更进一步了解了 Bindings 为什么能实现页面依赖注入的管理,希望通过源码让大家更好的理解 GetX 中的 Bindings ,从而在开发中灵活使用 Bindings 管理页面所需的依赖。