Getx/GetBuilder的init自动注入

  1. 引入案例
  2. init默认注册到容器中
    1. 源码
    2. widget.global
    3. GetInstance()
  3. 一个错误

引入案例

这里由两个GetBuilder组成。

最外层是init: CounterController()

但是内层使用了init: controller

那么这个controller是如何传递到内部的呢。

import 'package:flutter/material.dart';
import 'package:get/get.dart';

/// 控制器
class CounterController extends GetxController {
  int count = 0;

  void increment() {
    count++;
    update(['counter']); // 只刷新 counter
  }

  void reset() {
    count = 0;
    update(['counter']); // 只刷新 counter
  }
}

/// 页面,继承 GetView
class CounterPage extends GetView<CounterController> {
  const CounterPage({super.key});

  /// 数字展示
  Widget _buildCounter() {
    return GetBuilder<CounterController>(
      id: "counter",
      init: controller, // 这里的 controller 就是 Get.find<CounterController>()
      builder: (c) =>
          Text("计数:${c.count}", style: const TextStyle(fontSize: 24)),
    );
  }

  /// 按钮
  Widget _buildButtons() {
    return GetBuilder<CounterController>(
      id: "buttons",
      init: controller,
      builder: (c) => Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(onPressed: c.increment, child: const Text("加一")),
          const SizedBox(width: 20),
          ElevatedButton(onPressed: c.reset, child: const Text("重置")),
        ],
      ),
    );
  }

  /// 主体
  Widget _buildView() {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [_buildCounter(), const SizedBox(height: 20), _buildButtons()],
    );
  }

  @override
  Widget build(BuildContext context) {
    return GetBuilder<CounterController>(
      id: "root",
      init: CounterController(), // 外层 init
      builder: (_) {
        return Scaffold(
          appBar: AppBar(title: const Text("嵌套 GetBuilder Demo")),
          body: Center(child: _buildView()),
        );
      },
    );
  }
}

/// 入口
void main() {
  runApp(const GetMaterialApp(home: CounterPage()));
}

init默认注册到容器中

源码

以下是GetBuilder的源码,GetX的源码也类似。都证明:init默认自动注册到全局依赖容器

class GetBuilder<T extends GetxController> extends StatefulWidget {
  final GetControllerBuilder<T> builder;
  final bool global;
  final Object? id;
  final String? tag;
  final bool autoRemove;
  final bool assignId;
  final Object Function(T value)? filter;
  final void Function(GetBuilderState<T> state)? initState,
      dispose,
      didChangeDependencies;
  final void Function(GetBuilder oldWidget, GetBuilderState<T> state)?
      didUpdateWidget;
  final T? init;

  const GetBuilder({
    Key? key,
    this.init,
    this.global = true,
    required this.builder,
    this.autoRemove = true,
    this.assignId = false,
    this.initState,
    this.filter,
    this.tag,
    this.dispose,
    this.id,
    this.didChangeDependencies,
    this.didUpdateWidget,
  }) : super(key: key);

  @override
  GetBuilderState<T> createState() => GetBuilderState<T>();
}

class GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
    with GetStateUpdaterMixin {
  T? controller;
  bool? _isCreator = false;
  VoidCallback? _remove;
  Object? _filter;

  @override
  void initState() {
    // 调用父类的 initState
    super.initState();

    // 如果用户在 GetBuilder 里传了 initState 回调,这里会执行
    widget.initState?.call(this);

    // 判断:这个类型 T 的控制器(可能带 tag)是否已经在 GetX 容器里注册过
    var isRegistered = GetInstance().isRegistered<T>(tag: widget.tag);

    // 分两种情况:global = true(默认,全局注册) 或 global = false(局部实例)
    if (widget.global) {
      if (isRegistered) {
        // 已经注册过
        if (GetInstance().isPrepared<T>(tag: widget.tag)) {
          // 如果是“预注册”状态(lazyPut 的情况),标记 _isCreator = true
          _isCreator = true;
        } else {
          // 如果是普通的已注册实例,标记 _isCreator = false
          _isCreator = false;
        }
        // 从全局容器里拿这个 controller
        controller = GetInstance().find<T>(tag: widget.tag);
      } else {
        // 如果没有注册过,就用 init 传进来的实例
        controller = widget.init;
        _isCreator = true;

        // 并且把这个 controller 注册到全局依赖容器(相当于 Get.put)
        GetInstance().put<T>(controller!, tag: widget.tag);
      }
    } else {
      // global = false:完全局部模式,不注册到全局容器
      controller = widget.init;
      _isCreator = true;

      // 手动调用控制器的 onStart 生命周期
      controller?.onStart();
    }

    // 如果用户传了 filter(控制何时刷新 UI),在这里初始化
    if (widget.filter != null) {
      _filter = widget.filter!(controller!);
    }

    // 订阅 controller 的更新通知,用来触发 builder 重建
    _subscribeToController();
  }

widget.global

const GetBuilder({
  Key? key,
  this.init,
  this.global = true,   // 👈 默认值是 true
  required this.builder,
  ...
}) : super(key: key);

GetBuilder 的构造函数里,默认情况下,global = true

global = true(默认)时,控制器会放进全局依赖容器,页面之间可共享

if (widget.global) {
  if (isRegistered) {
    controller = GetInstance().find<T>();
  } else {
    controller = widget.init;
    GetInstance().put<T>(controller!); // 👈 自动注册到依赖容器
  }
}
  • 如果控制器已经在全局容器注册过 → find() 拿到同一个实例
  • 如果没注册过 → put(init) 注册到容器

global = false时,控制器是局部的,出这个 widget 就没了

else {
  controller = widget.init;
  controller?.onStart();
}
  • 完全不会往依赖容器注册
  • 只是简单地把你传的 init 控制器赋值给 controller
  • 生命周期只跟这个 GetBuilder 绑定

GetInstance()

GetInstance()GetX 的依赖管理容器(本质上是 Get 的底层实现),里面维护了一张 全局的 Map,存储所有 put 进去的实例。

Map<Type, dynamic> _factory = {};

isRegistered<T>():用来判断某个类型 T 的依赖实例有没有被注册过

GetInstance().isRegistered<T>(tag: widget.tag);
// 等价于
Get.isRegistered<T>(tag: tag);
// 检查某个类型的依赖(可带 tag)是否已经在 GetX 容器里存在。

一个错误

import 'package:flutter/material.dart';
import 'package:get/get.dart';

/// 控制器
class CounterController extends GetxController {
  int count = 0;

  void increment() {
    count++;
    update(); // 通知 GetBuilder 刷新
  }

  void reset() {
    count = 0;
    update(['counter']); // 只刷新 id 为 counter 的 GetBuilder
  }
}

/// 页面
class CounterPage extends GetView<CounterController> {
  const CounterPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("GetBuilder + GetView Demo")),

      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            /// 用 GetBuilder 监听
            GetBuilder<CounterController>(
              id: "counter",
              init: CounterController(), // 这里注册全局
              builder: (controller) => Text(
                "计数:${controller.count}",
                style: const TextStyle(fontSize: 24),
              ),
            ),

            const SizedBox(height: 20),

            ElevatedButton(
              onPressed: controller.increment, // 这里会报错
              child: const Text("加一"),
            ),

            ElevatedButton(
              onPressed: controller.reset,
              child: const Text("重置"),
            ),
          ],
        ),
      ),
    );
  }
}

上面这个案例报了一个这个错误:

"CounterController" not found. You need to call 
"Get.put(CounterController())" or 
"Get.lazyPut(()=>CounterController())"

为什么会报错:

  • GetBuilderinit → 会自动 Get.put(init),把这个 CounterController 注册到全局容器

  • 但是!这个注册动作发生在 GetBuilderinitState()

  • 而你在 build() 里调用了 controller.increment,这时 GetBuilder 还没执行 initState,所以容器里还没有 CounterController

  • GetViewcontrollerGet.find()

  • 结果:Get.find<CounterController>() 找不到实例 → 报错。

×

喜欢就点赞,疼爱就打赏