简介
GetX 是一个轻量且强大的解决方案,集成了状态管理、路由管理和依赖注入。它提供了简单的语法和高性能的响应式编程。
基本概念
控制器
class CounterController extends GetxController {
var count = 0.obs; // 创建可观察变量
void increment() {
count++; // 自动通知UI更新
}
@override
void onInit() {
super.onInit();
// 控制器初始化
}
@override
void onClose() {
// 控制器销毁
super.onClose();
}
}
响应式变量
// 基本类型
final name = 'GetX'.obs;
final count = 0.obs;
final isLogged = false.obs;
// 自定义类
final user = User().obs;
// 列表
final items = <String>[].obs;
基本用法
注册控制器
void main() {
// 方式1: 全局注入
Get.put(CounterController());
// 方式2: 懒加载注入
Get.lazyPut(() => CounterController());
runApp(MyApp());
}
访问状态
// 方式1: Obx
Obx(() => Text('Count: ${controller.count}'));
// 方式2: GetX
GetX<CounterController>(
builder: (controller) {
return Text('Count: ${controller.count}');
},
);
// 方式3: GetBuilder
GetBuilder<CounterController>(
builder: (controller) {
return Text('Count: ${controller.count}');
},
);
完整示例
// 用户模型
class User {
final String name;
final int age;
User({required this.name, required this.age});
}
// 用户控制器
class UserController extends GetxController {
final user = User(name: 'Guest', age: 0).obs;
final isLoading = false.obs;
void updateUser(String name, int age) {
user.update((val) {
val?.name = name;
val?.age = age;
});
}
Future<void> fetchUser() async {
isLoading.value = true;
try {
await Future.delayed(Duration(seconds: 2)); // 模拟网络请求
updateUser('John Doe', 25);
} finally {
isLoading.value = false;
}
}
}
// 页面组件
class UserPage extends StatelessWidget {
final controller = Get.put(UserController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('GetX Demo')),
body: Center(
child: Obx(() {
if (controller.isLoading.value) {
return CircularProgressIndicator();
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Name: ${controller.user.value.name}',
style: TextStyle(fontSize: 24),
),
Text(
'Age: ${controller.user.value.age}',
style: TextStyle(fontSize: 24),
),
ElevatedButton(
onPressed: () => controller.fetchUser(),
child: Text('Fetch User'),
),
],
);
}),
),
);
}
}
// 主应用
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'GetX Demo',
home: UserPage(),
);
}
}
路由管理
基本导航
// 导航到新页面
Get.to(() => SecondPage());
// 替换当前页面
Get.off(() => SecondPage());
// 清空所有页面并导航
Get.offAll(() => HomePage());
// 带参数导航
Get.to(
() => DetailPage(),
arguments: {'id': 1},
);
// 获取参数
final args = Get.arguments;
命名路由
GetMaterialApp(
getPages: [
GetPage(name: '/', page: () => HomePage()),
GetPage(name: '/detail', page: () => DetailPage()),
],
);
// 导航
Get.toNamed('/detail', arguments: {'id': 1});
依赖注入
绑定控制器
class HomeBinding implements Bindings {
@override
void dependencies() {
Get.put(HomeController());
Get.put(UserController());
}
}
GetMaterialApp(
initialBinding: HomeBinding(),
// ...
);
服务注入
// 定义服务接口
abstract class ApiClient {
Future<User> getUser();
}
// 实现服务
class ApiClientImpl implements ApiClient {
@override
Future<User> getUser() async {
// 实现
}
}
// 注入服务
Get.put<ApiClient>(ApiClientImpl());
// 使用服务
final apiClient = Get.find<ApiClient>();
最佳实践
- controller 按页面/功能拆:一个页面一个 Controller,只管这个页面的状态和逻辑——别搞一个 GodController 管全 App,改一行抖三抖
- 搜索框/表单输入用 debounce:
debounce(keyword, (_) => search(), time: 500.ms);用户每敲一个字母就发一次请求,服务端会先被你打挂 - 依赖注入分清楚 Get.put 和 Get.lazyPut:页面级 controller 用
Get.lazyPut,随路由创建、随页面销毁;Get.put是立即创建常驻内存,只适合全局单例(如 UserController) - onClose 里关资源:Timer、StreamSubscription、TextEditingController 都要在 onClose 里释放——退出登录切账号后,旧 controller 的 timer 还在跑就会写脏数据(freight 项目真实踩坑)
- Worker 只做副作用:
ever(loginState, (_) => 跳登录页)可以,别在 Worker 回调里再改被监听的变量,会触发循环更新
注意事项
.obs注意粒度:列表用<Item>[].obs整体替换,别给几百条数据的每个 item 都套 .obs——内存和通知开销都扛不住- Obx 只包”会变的那一小块”:整个页面包一个 Obx,任何变量一变整页 rebuild;正确姿势是只把数量、价格那几行 Text 包起来
- Get.arguments 是 dynamic:取出来先判空再转类型,
as String在 null 上直接炸(参数传递篇的坑,这里再强调一次) - 响应式是”引用比较”:
list.add(item)不会触发更新,要list.refresh()或重新赋值——新手 90% 的”数据变了 UI 不刷”都是这个原因
实战案例
我的五个 GetX 项目(旅行类应用、某物流冷链项目 等)统一用这套结构:pages/travel_select_city/ 下面三个文件——index.dart(只做绑定)、controller.dart(逻辑+状态)、view.dart(UI)。比如选城市页,controller 里 var cityList = <City>[].obs,view 里 Obx(() => ListView(...)),数据一变 UI 自动刷。index.dart 里 Get.lazyPut(() => TravelSelectCityController()) 做依赖注入。这个三件套我雷打不动:controller 绝不碰 widget,view 绝不写逻辑。有一次图省事在 view 里直接调了接口,后来这个页面要复用到另一个入口,逻辑抽不出来,重构花了一下午。从那以后严格执行分层。
总结
GetX 提供了一套完整的应用开发解决方案,通过简单的API可以快速实现状态管理、路由管理和依赖注入。理解并掌握 GetX 的使用可以显著提高 Flutter 应用的开发效率。
