GetX 是 Flutter 生态中非常流行的轻量级、高性能框架,它将状态管理、路由管理、依赖注入三大核心功能整合在一起,同时提供了大量实用工具。
一、GetX 三大核心功能
| 功能 | 说明 | 关键类/方法 |
|---|---|---|
| 状态管理 | 响应式编程,自动更新UI | GetxController, Obx, GetX, Rx |
| 路由管理 | 无需 BuildContext 的导航 |
Get.to(), Get.back(), Get.offAll() |
| 依赖注入 | 自动管理 Controller 生命周期 | Get.put(), Get.find(), Bindings |
二、状态管理详解
1. 响应式状态(Reactive State)
GetX 使用 Rx 类型实现响应式编程,当数据变化时自动刷新 UI。
import 'package:get/get.dart';
class CounterController extends GetxController {
// 定义响应式变量
var count = 0.obs; // int 的响应式版本
var name = 'Flutter'.obs; // String 的响应式版本
var items = <String>[].obs; // List 的响应式版本
void increment() => count++;
void changeName(String newName) => name.value = newName;
}
2. 三种监听方式
方式一:Obx(最轻量,推荐)
class HomePage extends StatelessWidget {
final controller = Get.put(CounterController());
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
// Obx 只监听其内部使用的 Rx 变量
child: Obx(() => Text(
'点击次数: ${controller.count.value}',
style: TextStyle(fontSize: 24),
)),
),
floatingActionButton: FloatingActionButton(
onPressed: controller.increment,
child: Icon(Icons.add),
),
);
}
}
方式二:GetX(监听整个 Controller)
GetX<CounterController>(
builder: (controller) {
return Text('点击次数: ${controller.count.value}');
},
)
方式三:GetBuilder(手动更新,性能最高)
class CounterController extends GetxController {
int count = 0;
void increment() {
count++;
update(); // 手动触发更新,类似 notifyListeners()
}
}
// UI 中使用
GetBuilder<CounterController>(
builder: (controller) {
return Text('点击次数: ${controller.count}');
},
)
3. Workers(监听器)
Workers 用于监听 Rx 变量的变化并执行副作用:
class UserController extends GetxController {
final name = ''.obs;
final age = 0.obs;
@override
void onInit() {
super.onInit();
// 每次变化都执行
ever(name, (value) => print('名字变为: $value'));
// 只执行一次
once(age, (value) => print('年龄第一次变化: $value'));
// 防抖(停止输入 1 秒后执行)
debounce(name, (value) => print('搜索: $value'), time: Duration(seconds: 1));
// 节流(每 2 秒最多执行一次)
interval(count, (value) => print('计数: $value'), time: Duration(seconds: 2));
}
}
三、路由管理
GetX 路由完全脱离 BuildContext,代码更简洁。
基础导航
// 跳转到新页面
Get.to(HomePage());
// 返回上一页
Get.back();
// 替换当前页面(无法返回)
Get.off(HomePage());
// 清除所有页面并跳转(如登录后进入首页)
Get.offAll(HomePage());
// 跳转到命名路由
Get.toNamed('/home');
// 带参数跳转
Get.toNamed('/profile', arguments: {'id': 123, 'name': '张三'});
路由配置
void main() {
runApp(GetMaterialApp(
initialRoute: '/',
getPages: [
GetPage(name: '/', page: () => SplashPage()),
GetPage(name: '/home', page: () => HomePage()),
GetPage(
name: '/profile/:id',
page: () => ProfilePage(),
// 中间件(如登录验证)
middlewares: [AuthMiddleware()],
// 转场动画
transition: Transition.cupertino,
),
],
));
}
接收参数
class ProfilePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 获取路由参数
final id = Get.parameters['id'];
final args = Get.arguments;
return Scaffold(
appBar: AppBar(title: Text('用户 $id')),
);
}
}
四、依赖注入(Bindings)
Bindings 用于将 Controller 与页面绑定,自动管理生命周期。
1. 基础使用
class HomeBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => HomeController());
Get.lazyPut(() => UserController());
}
}
// 路由中绑定
GetPage(
name: '/home',
page: () => HomePage(),
binding: HomeBinding(),
)
2. 依赖注入方式对比
// 立即注入(应用启动时创建)
Get.put(Controller());
// 懒加载(第一次使用时创建,推荐)
Get.lazyPut(() => Controller());
// 异步注入(如需要网络请求初始化)
Get.putAsync(() async => await SharedPrefsController.init());
// 创建新实例(每次调用都新建)
Get.create(() => Controller());
3. 查找依赖
dart
// 在任意位置获取 Controller
final controller = Get.find<HomeController>();
// 删除依赖(释放资源)
Get.delete<HomeController>();
五、Controller 生命周期
class MyController extends GetxController {
@override
void onInit() {
super.onInit();
// 初始化数据、监听 Workers
}
@override
void onReady() {
super.onReady();
// 首次渲染完成后调用(类似 initState 之后)
}
@override
void onClose() {
// 页面销毁时调用,释放资源、取消订阅
super.onClose();
}
}
六、实用工具
1. Snackbar(提示条)
Get.snackbar(
'操作成功',
'数据已保存',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.green,
colorText: Colors.white,
duration: Duration(seconds: 2),
);
2. Dialog(对话框)
Get.defaultDialog(
title: '确认删除',
middleText: '此操作不可撤销',
textConfirm: '删除',
textCancel: '取消',
confirmTextColor: Colors.white,
onConfirm: () => Get.back(),
);
// 自定义 Dialog
Get.dialog(AlertDialog(...));
3. BottomSheet(底部弹
Get.bottomSheet(
Container(
height: 200,
color: Colors.white,
child: Center(child: Text('底部内容')),
),
);
4. 主题切换
// 切换深色/浅色模式
Get.changeTheme(Get.isDarkMode ? ThemeData.light() : ThemeData.dark());
// 监听系统主题
Get.changeThemeMode(ThemeMode.system);
5. 国际化(i18n)
class Messages extends Translations {
@override
Map<String, Map<String, String>> get keys => {
'zh_CN': {
'hello': '你好',
'welcome': '欢迎 @name',
},
'en_US': {
'hello': 'Hello',
'welcome': 'Welcome @name',
},
};
}
// 使用
Text('hello'.tr); // 你好
Text('welcome'.trParams({'name': '张三'})); // 欢迎 张三
七、完整实战示例
// main.dart
void main() {
runApp(GetMaterialApp(
title: 'GetX Demo',
initialRoute: '/',
getPages: [
GetPage(name: '/', page: () => LoginPage()),
GetPage(
name: '/home',
page: () => HomePage(),
binding: HomeBinding(),
),
],
));
}
// controllers/home_controller.dart
class HomeController extends GetxController {
final tasks = <Task>[].obs;
final isLoading = false.obs;
@override
void onInit() {
fetchTasks();
super.onInit();
}
void fetchTasks() async {
isLoading.value = true;
await Future.delayed(Duration(seconds: 1));
tasks.addAll([
Task(title: '学习 GetX', done: false),
Task(title: '完成项目', done: false),
]);
isLoading.value = false;
}
void toggleTask(int index) {
tasks[index].done = !tasks[index].done;
tasks.refresh(); // 刷新列表
}
}
// bindings/home_binding.dart
class HomeBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => HomeController());
}
}
// pages/home_page.dart
class HomePage extends StatelessWidget {
final controller = Get.find<HomeController>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('任务列表')),
body: Obx(() {
if (controller.isLoading.value) {
return Center(child: CircularProgressIndicator());
}
return ListView.builder(
itemCount: controller.tasks.length,
itemBuilder: (context, index) {
final task = controller.tasks[index];
return ListTile(
title: Text(
task.title,
style: TextStyle(
decoration: task.done ? TextDecoration.lineThrough : null,
),
),
trailing: Checkbox(
value: task.done,
onChanged: (_) => controller.toggleTask(index),
),
);
},
);
}),
);
}
}
八、GetX vs 其他方案
| 特性 | GetX | Provider | Riverpod | BLoC |
|---|---|---|---|---|
| 学习曲线 | ⭐ 低 | 中 | 中 | 高 |
| 代码量 | 极少 | 较少 | 较少 | 较多 |
| 性能 | 高 | 高 | 高 | 高 |
| 路由管理 | ✅ 内置 | ❌ 需额外库 | ❌ 需额外库 | ❌ 需额外库 |
| 依赖注入 | ✅ 内置 | ❌ 需额外库 | ✅ 内置 | ❌ 需额外库 |
| 社区活跃度 | 高 | 高 | 高 | 高 |
| 官方推荐 | 非官方 | 非官方 | 非官方 | 非官方 |
九、注意事项
-
避免在
build中调用Get.put():应在页面顶部或 Binding 中注入 -
Obx 嵌套问题:不要在 Obx 内部再创建 Obx 监听同一个 Controller,会导致重复监听
-
Rx 集合的更新 :修改 List/Map 后需调用
.refresh()或.value = ... -
GetMaterialApp :使用 GetX 路由必须用
GetMaterialApp替代MaterialApp -
与原生 Navigator 混用:尽量避免,可能导致路由栈混乱
GetX 的最大优势在于**"一站式"解决方案**------用一套框架同时解决状态、路由、依赖三大问题,代码简洁、学习成本低,非常适合中小型项目快速开发。对于大型项目,也可以只使用其中某个模块(如只用状态管理,路由仍用 Navigator 2.0)。