GetWidget 设计哲学
与GetView的本质区别
dart
// GetView 源码片段
abstract class GetView<T> extends StatelessWidget {
const GetView({Key? key}) : super(key: key);
T get controller => GetInstance().find<T>();
// 每次build都会获取最新实例
}
// GetWidget 源码片段
abstract class GetWidget<T extends GetxController> extends StatelessWidget {
const GetWidget({Key? key}) : super(key: key);
T get controller => GetInstance().find<T>(tag: _tag);
// 保持对同一实例的引用
}
核心特性
- 单例模式:始终返回同一个控制器实例
- 状态保持:Widget 卸载后控制器仍然存在
- 跨组件共享:多个 Widget 可访问同一实例
- 手动回收:需要主动调用 Get.delete
使用场景对比
典型用例矩阵
场景 | GetView | GetWidget |
---|---|---|
普通页面 | ✓ | ✗ |
全局用户状态 | ✗ | ✓ |
购物车 | ✗ | ✓ |
应用主题设置 | ✗ | ✓ |
页面间数据传递 | ✗ | ✓ |
需要保持后台状态的组件 | ✗ | ✓ |
实战示例:购物车系统
控制器定义
cart_controller.dart
class CartController extends GetxController {
final items = <Product>[].obs;
void addItem(Product product) {
items.add(product);
update();
}
// 保持单例
static CartController get instance => Get.put(CartController());
}
使用GetWidget绑定
cart_icon.dart
class CartIcon extends GetWidget<CartController> {
@override
Widget build(BuildContext context) {
return Stack(
children: [
Icon(Icons.shopping_cart),
Obx(() => Text('${controller.items.length}')),
],
);
}
}
// 在多个页面使用
class ProductPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [CartIcon()], // 多个页面共享同一实例
),
);
}
}
生命周期管理
典型生命周期流程
sequenceDiagram
participant WidgetA
participant GetWidget
participant Controller
WidgetA->>GetWidget: 首次创建
GetWidget->>Controller: Get.put()
Controller-->>GetWidget: 返回实例
GetWidget->>WidgetA: 构建完成
WidgetA->>GetWidget: 移除组件
GetWidget->>Controller: 保持存活
WidgetA->>GetWidget: 再次创建
GetWidget->>Controller: 获取已有实例
手动回收策略
dart
// 在退出应用时清理
@override
void dispose() {
if (Get.isRegistered<CartController>()) {
Get.delete<CartController>();
}
super.dispose();
}
// 条件性删除
Get.delete<CartController>(
force: true, // 强制删除
condition: (c) => c.items.isEmpty, // 满足条件才删除
);
高级用法
1. 带参数的初始化
dart
class ConfigController extends GetxController {
final String env;
ConfigController({required this.env});
}
// 初始化时
Get.put(ConfigController(env: 'prod'), permanent: true);
// 在GetWidget中访问
class ConfigWidget extends GetWidget<ConfigController> {
@override
Widget build(BuildContext context) {
return Text('当前环境:${controller.env}');
}
}
2. 多实例管理
dart
class ChatController extends GetxController {
final String chatId;
ChatController(this.chatId);
}
// 不同会话页面
class ChatPage extends GetWidget<ChatController> {
final String chatId;
ChatPage(this.chatId) {
Get.put(ChatController(chatId), tag: chatId);
}
@override
Widget build(BuildContext context) {
return Text('会话ID:${controller.chatId}');
}
}
3. 结合GetBuilder
dart
class HybridWidget extends GetWidget<CartController> {
@override
Widget build(BuildContext context) {
return GetBuilder<CartController>(
builder: (c) => Column(
children: [
Text('总数量:${c.items.length}'),
ElevatedButton(
onPressed: () => c.clearCart(),
child: Text('清空'),
)
],
),
);
}
}
性能优化方案
1. 按需更新
dart
class OptimizedCart extends GetWidget<CartController> {
@override
Widget build(BuildContext context) {
return Obx(
() => ListView.builder(
itemCount: controller.items.length,
itemBuilder: (_, i) => ItemWidget(
item: controller.items[i],
// 仅当价格变化时更新
shouldRebuild: (old, current) => old.price != current.price,
),
),
);
}
}
2. 状态快照
dart
extension SnapshotExtension on GetxController {
T get snapshot => Get.find<T>(tag: 'snapshot_${this.runtimeType}');
void saveSnapshot() {
Get.put<T>(this, tag: 'snapshot_${this.runtimeType}');
}
}
// 使用示例
controller.saveSnapshot();
final backup = controller.snapshot;
3. 内存监控
dart
void monitorMemory() {
Get.addObserver(
(route, previousRoute) {
if (route.isCurrent) {
debugPrint('当前控制器实例:${Get.instances}');
}
},
);
}
最佳实践指南
- 命名规范
- 全局控制器添加
Global
后缀:CartGlobalController - 页面级控制器使用
Page
后缀:ProductPageController - 保持单例的控制器标注
@singleton
注解
- 项目结构建议
bash
lib/
├─ core/
│ ├─ controllers/ # GetWidget使用的全局控制器
│ │ ├─ app_controller.dart
│ │ ├─ config_controller.dart
├─ modules/
│ ├─ cart/
│ │ ├─ cart_widget.dart # 使用GetWidget的共享组件
- 调试技巧
python
// 打印控制器树
void debugControllerTree() {
Get.printInfo(
info: '''
控制器实例列表:
${Get.instances.map((e) => e.runtimeType).join('\n')}
'''
);
}
总结
GetWidget 的核心价值体现在:
- 全局状态管理:跨组件/页面共享数据
- 持久化能力:保持后台状态不丢失
- 性能优化:避免重复创建控制器
- 复杂场景支持:聊天会话、购物车等需要状态保持的场景
建议在以下场景优先使用 GetWidget:
- 需要跨多个页面共享的状态
- 后台持续运行的业务逻辑(如音乐播放)
- 需要保持用户操作记录的模块
- 应用全局配置信息管理
通过合理运用 GetWidget,可以构建出更健壮、更易维护的 Flutter 应用架构。