Getx:tag的作用

Tag的作用

依赖注入时使用 tag

Get.put() / Get.find() 等依赖注入里使用 tag,其目的是**注册、获取指定的控制器实例**

// 注册两个同类型的控制器,但通过 tag 区分
Get.put(CounterController(), tag: 'c1');
Get.put(CounterController(), tag: 'c2');

// 获取指定 tag 的控制器
final c1 = Get.find<CounterController>(tag: 'c1');
final c2 = Get.find<CounterController>(tag: 'c2');
  • 没有 tag:按类型检索,整个 App 只能同时存在一个 CounterController 实例。

  • tag:同一类型可以注册多个实例,靠 tag 来唯一标识和区分。

  • 类比:tag 就像是给每个控制器实例贴了一个「名字」,否则 GetX 无法分辨到底要用哪一个。

在UI 绑定组件时使用 tag

GetBuilder / GetX / Obx 等 UI 绑定组件里使用 tag,其目的是**绑定到特定 tag 下的控制器实例,避免多个相同类型的控制器互相干扰**

GetBuilder<CounterController>(
  tag: 'c1',
  builder: (c) => Text('计数器1: ${c.count}'),
);

GetBuilder<CounterController>(
  tag: 'c2',
  builder: (c) => Text('计数器2: ${c.count}'),
);
  • GetBuilder 会根据 type + tag 组合找到对应控制器。

  • 如果两个 GetBuilder 都不写 tag,它们会绑定到同一个控制器实例,互相影响。

  • 如果使用 init: 创建控制器,也可以配合 tag 使用,确保实例隔离:

    GetBuilder<CounterController>(
      init: CounterController(),
      tag: 'c1',
      builder: ...
    )
    

案例分析

不使用Tag时遇到的问题

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

class CounterController extends GetxController {
  int count = 0;
  void increment() {
    count++;
    update();
  }
}

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(home: CounterPage());
  }
}

class CounterPage extends StatelessWidget {
  const CounterPage({super.key});

  @override
  Widget build(BuildContext context) {
    // 这里我们想创建两个控制器实例
    final c1 = Get.put(CounterController());
    final c2 = Get.put(CounterController()); // 实际上会被复用成同一个实例

    return Scaffold(
      appBar: AppBar(title: const Text('没有 tag')),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          GetBuilder<CounterController>(
            builder: (c) =>
                Text('计数器1: ${c.count}', style: const TextStyle(fontSize: 20)),
          ),
          GetBuilder<CounterController>(
            builder: (c) =>
                Text('计数器2: ${c.count}', style: const TextStyle(fontSize: 20)),
          ),
          ElevatedButton(onPressed: c1.increment, child: const Text('计数器1 +1')),
          ElevatedButton(onPressed: c2.increment, child: const Text('计数器2 +1')),
        ],
      ),
    );
  }
}

页面上有两个计数器区块,都使用 GetBuilder<CounterController>,但是控制器实例本想是两个独立的,但因为都没写 tag,最后被当作一个控制器复用,导致两个计数器总是一起变化。

使用Tag成功隔离

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

class CounterController extends GetxController {
  int count = 0;
  void increment() {
    count++;
    update();
  }
}

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(home: CounterPage());
  }
}

class CounterPage extends StatelessWidget {
  const CounterPage({super.key});

  @override
  Widget build(BuildContext context) {
    // 用 tag 强制创建两个独立实例
    final c1 = Get.put(CounterController(), tag: 'c1');
    final c2 = Get.put(CounterController(), tag: 'c2');
    
    return Scaffold(
      appBar: AppBar(title: const Text('有 tag')),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          GetBuilder<CounterController>(
            tag: 'c1',
            builder: (c) =>
                Text('计数器1: ${c.count}', style: const TextStyle(fontSize: 20)),
          ),
          GetBuilder<CounterController>(
            tag: 'c2',
            builder: (c) =>
                Text('计数器2: ${c.count}', style: const TextStyle(fontSize: 20)),
          ),
          ElevatedButton(onPressed: c1.increment, child: const Text('计数器1 +1')),
          ElevatedButton(onPressed: c2.increment, child: const Text('计数器2 +1')),
        ],
      ),
    );
  }
}

点击「计数器1 +1」 → 只更新计数器1

点击「计数器2 +1」 → 只更新计数器2

二者 互不干扰,问题解决

当需要在同一页面使用相同类型控制器的多个实例时,一定要给 GetBuilderGet.put() 配对加上 tag,否则会出现「明明想要独立,结果共享」的干扰问题。

两个地方都要加Tag

Get.put()GetBuilder 都可以带 tag

只有当 Get.put()GetBuilder 用了 相同的 tag 时,GetBuilder 才能找到对应的那个控制器实例

如果你 Get.put() 时没写 tag,那就只能得到“默认实例”

如果你 GetBuilder 写了 tag,但 Get.put() 没有写相同 tag,是找不到实例的,会报错

Get.put() 是否加 tag GetBuilder 是否加 tag 结果
❌ 无 tag ❌ 无 tag 正常:都使用默认实例
✅ 有 tag ❌ 无 tag GetBuilder 找不到实例(报错)
❌ 无 tag ✅ 有 tag GetBuilder 找不到实例(报错)
✅ 有 tag ✅ 有 tag(相同) 正常:找到对应实例
✅ 有 tag ✅ 有 tag(不同) 各自使用自己的实例,互不干扰
final c1 = Get.put(CounterController());
final c2 = Get.put(CounterController());


children: [
  GetBuilder<CounterController>(
    tag: 'c1',
    builder: (c) =>
        Text('计数器1: ${c.count}', style: const TextStyle(fontSize: 20)),
  ),
  GetBuilder<CounterController>(
    tag: 'c2',
    builder: (c) =>
        Text('计数器2: ${c.count}', style: const TextStyle(fontSize: 20)),
  ),
  ElevatedButton(onPressed: c1.increment, child: const Text('计数器1 +1')),
  ElevatedButton(onPressed: c2.increment, child: const Text('计数器2 +1')),
],

如上面,Get.put()不加tag而GetBuilder 有tag的时候,在创建的时候就会报错:

════════ Exception caught by widgets library ═══════════════════════════════════ The following _TypeError was thrown building KeyedSubtree-[GlobalKey#751ac]: Null check operator used on a null value The relevant error-causing widget was: Scaffold Scaffold:file:///Users/xieshaolin/workpalce/flutterTest/flutter_sophomore/lib/mainGetBuilderTag2.dart:35:12 When the exception was thrown, this was the stack: 
#0 GetBuilderState.initState (package:get/get_state_manager/src/simple/get_state.dart:134:40) get_state.dart:134

是用init的方式

案例

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(home: CounterPage());
  }
}

class CounterController extends GetxController {
  int count = 0;
  void increment() {
    count++;
    update();
  }
}

class CounterPage extends StatelessWidget {
  const CounterPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('有 tag + init')),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          // 左边计数器
          GetBuilder<CounterController>(
            init: CounterController(), // 创建实例
            tag: 'c1', // 指定唯一tag
            builder: (c) => Column(
              children: [
                Text('计数器1: ${c.count}', style: const TextStyle(fontSize: 20)),
                ElevatedButton(
                  onPressed: c.increment,
                  child: const Text('计数器1 +1'),
                ),
              ],
            ),
          ),

          const SizedBox(height: 40),

          // 右边计数器
          GetBuilder<CounterController>(
            init: CounterController(),
            tag: 'c2',
            builder: (c) => Column(
              children: [
                Text('计数器2: ${c.count}', style: const TextStyle(fontSize: 20)),
                ElevatedButton(
                  onPressed: c.increment,
                  child: const Text('计数器2 +1'),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

init做了什么

GetBuilder<CounterController>(
        init: CounterController(), // 创建实例
        tag: 'c1', // 指定唯一tag
        builder: (c) => Column(
          children: [
            Text('计数器1: ${c.count}', style: const TextStyle(fontSize: 20)),
            ElevatedButton(
              onPressed: c.increment,
              child: const Text('计数器1 +1'),
            ),
          ],
        ),
      ),
  • init 会在 GetBuilder 内部自动执行 Get.put(init, tag: tag),所以你不需要再外部 Get.put 一次GetBuilder 会帮你注册
  • 最后在 GetBuilder dispose 时,自动调用 Get.delete<CounterController>() 释放实例
  • 所以它是局部、生命周期跟随这个 widget 的实例

和手动 Get.put 对比

场景 Get.put() init:
控制器只用在一个地方 可用,但要手动管理删除 更推荐,生命周期自动管理
控制器要跨页面复用 / 全局状态 必须用 Get.put() 注册 init: 每次都会生成新实例,不能共享
想用 tag 区分多个实例 Get.put(..., tag) + GetBuilder(tag) init: + tag 也行,效果等价

×

喜欢就点赞,疼爱就打赏