GetX 导航与状态管理详解
1. GetX 简介
GetX 是一个轻量级且强大的 Flutter 插件,集成了状态管理、依赖注入、路由管理等功能。它具有高性能、低耦合、易用性强的特点,是 Flutter 开发中的重要工具。
2. 状态管理
2.1 响应式状态管理
GetX 提供了多种响应式变量类型:
RxString: 字符串响应式变量RxInt: 整数响应式变量RxDouble: 双精度浮点数响应式变量RxBool: 布尔响应式变量RxList<T>: 响应式列表RxMap<K, V>: 响应式映射RxSet<T>: 响应式集合Rx<T>: 通用响应式变量
final RxString name = "John".obs;
final RxInt count = 0.obs;
final RxList<String> items = <String>[].obs;
final Rx<Book?> selectedBook = Rx<Book?>(null);
2.2 响应式变量的使用
响应式变量的值通过 .value 获取和设置:
// 获取值
String currentName = name.value;
// 设置值
name.value = "Jane";
// 监听变化
name.listen((value) => print("New value: $value"));
// 条件更新
count.value++; // 自动通知监听者
2.3 Obx 组件
Obx 组件用于监听响应式变量的变化并自动重建 UI:
Obx(() => Text(controller.name.value))
// 或者使用 GetX
GetX<MyController>(
builder: (controller) => Text(controller.name.value)
)
// 或者使用 GetBuilder
GetBuilder<MyController>(
builder: (controller) => Text(controller.name.value)
)
2.4 监听器
ever: 每次值变化时执行once: 值第一次变化时执行debounce: 防抖监听delay: 延迟监听
// 每次变化都执行
ever(count, (value) => print("Count changed to $value"));
// 只执行一次
once(name, (value) => print("Name initially: $value"));
// 防抖
debounce(count, (value) => print("Debounced: $value"), time: Duration(milliseconds: 300));
3. 依赖注入
3.1 Get.put()
将控制器或服务注册到 GetX 容器中:
Get.put(MyController());
Get.put(MyService(), permanent: true); // 永久保存
Get.putAsync(() => MyController().init()); // 异步初始化
3.2 Get.lazyPut()
延迟注入,只有在需要时才创建实例:
Get.lazyPut<MyController>(() => MyController());
Get.lazyPut<MyService>(() => MyService(), fenix: true); // 永远存活
3.3 Get.find()
从 GetX 容器中获取已注册的实例:
final controller = Get.find<MyController>();
final service = Get.find<MyService>();
3.4 GetxService
用于创建单例服务:
class MyService extends GetxService {
static MyService get to => Get.find();
Future<MyService> init() async {
// 初始化代码
return this;
}
}
3.5 Bindings
用于在路由级别管理依赖关系:
class HomeBinding implements Bindings {
@override
void dependencies() {
Get.lazyPut(() => HomeController());
Get.lazyPut(() => UserService());
}
}
4. 路由管理
4.1 GetMaterialApp
替代 MaterialApp,启用 GetX 路由功能:
return GetMaterialApp(
title: 'My App',
initialRoute: '/',
getPages: [
GetPage(name: '/', page: () => HomeScreen()),
GetPage(name: '/details', page: () => DetailsScreen()),
],
);
4.2 基本路由操作
Get.to(): 跳转到新页面(压入路由栈)Get.off(): 替换当前页面(移除当前页面并跳转)Get.offAll(): 清空路由栈并跳转到新页面Get.back(): 返回上一页(弹出路由栈)
// 跳转到新页面
Get.to(SecondScreen());
// 带参数跳转
Get.to(SecondScreen(), arguments: {'id': 123});
// 替换当前页面
Get.off(ThirdScreen());
// 清空路由栈并跳转
Get.offAll(HomeScreen());
// 返回上一页
Get.back();
// 返回并带回值
Get.back(result: 'Success');
// 返回并替换所有路由
Get.until((route) => route.isFirst);
4.3 命名路由
// 定义命名路由
static final pages = [
GetPage(
name: '/home',
page: () => HomeScreen(),
binding: HomeBinding(), // 依赖注入
transition: Transition.fade, // 转场动画
middlewares: [AuthMiddleware()], // 中间件
),
GetPage(
name: '/product/:id', // 路由参数
page: () => ProductScreen(),
binding: ProductBinding(),
),
];
// 跳转到命名路由
Get.toNamed('/product/123');
Get.toNamed('/product', arguments: {'id': 123});
4.4 参数传递
- 通过
arguments传递复杂对象 - 通过
parameters传递简单参数 - 通过路由参数获取
Get.parameters
// 传递参数
Get.toNamed('/product', arguments: {
'productId': 123,
'productName': 'iPhone',
});
// 接收参数
class ProductController extends GetxController {
@override
void onInit() {
super.onInit();
var args = Get.arguments; // {'productId': 123, 'productName': 'iPhone'}
var params = Get.parameters; // {'id': '123'} for '/product/:id'
}
}
4.5 页面转换动画
GetX 提供了多种内置转场动画:
Transition.fade: 淡入淡出Transition.rightToLeft: 从右到左滑动Transition.leftToRight: 从左到右滑动Transition.upToDown: 从上到下滑动Transition.downToUp: 从下到上滑动Transition.scale: 缩放效果Transition.circularReveal: 圆形展开效果Transition.complex: 复杂动画组合
GetPage(
name: '/home',
page: () => HomeScreen(),
transition: Transition.fadeIn,
transitionDuration: Duration(milliseconds: 500),
)
4.6 路由中间件
class AuthMiddleware extends GetMiddleware {
@override
int? get priority => 1;
@override
RouteSettings? redirect(String? route) {
if (!AuthService.to.isAuthenticated) {
return const RouteSettings(name: '/login');
}
return null;
}
}
4.7 路由守卫
GetPage(
name: '/profile',
page: () => ProfileScreen(),
middlewares: [AuthMiddleware()],
)
5. 控制器生命周期
5.1 生命周期方法
onInit(): 控制器初始化时调用onReady(): 控制器准备好时调用(Widget 构建后)onClose(): 控制器关闭时调用
class MyController extends GetxController {
@override
void onInit() {
super.onInit();
// 初始化响应式变量
// 设置监听器
// 订阅服务
}
@override
void onReady() {
super.onReady();
// Widget 构建完成后执行
// 执行需要等待 Widget 构建的操作
}
@override
void onClose() {
// 清理资源
// 取消订阅
// 关闭 Stream
super.onClose();
}
}
5.2 控制器管理
class MyController extends GetxController {
final count = 0.obs;
void increment() => count.value++;
@override
void onClose() {
// 重要:清理资源
count.close(); // 关闭响应式变量
super.onClose();
}
}
6. UI 组件
6.1 GetView
简化控制器访问的 StatefulWidget:
class MyWidget extends GetView<MyController> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(controller.count.value.toString()),
),
floatingActionButton: FloatingActionButton(
onPressed: controller.increment,
child: Icon(Icons.add),
),
);
}
}
6.2 GetWidget
类似 GetView,但适用于 StatelessWidget:
class MyWidget extends GetWidget<MyController> {
@override
Widget build(BuildContext context) {
return Text(Get.find<MyController>().myProperty);
}
}
6.3 GetBuilder
手动控制重建的组件:
GetBuilder<MyController>(
builder: (controller) {
return Text(controller.count.toString());
},
)
6.4 GetX
专门用于响应式变量的组件:
GetX<MyController>(
builder: (controller) {
return Text(controller.count.value.toString());
},
)
7. 对话框和提示
7.1 Snackbar
Get.snackbar(
"Title", // 标题
"Message", // 消息
snackPosition: SnackPosition.BOTTOM, // 位置
duration: Duration(seconds: 3), // 持续时间
backgroundColor: Colors.blue, // 背景色
colorText: Colors.white, // 文字颜色
icon: Icon(Icons.check), // 图标
mainButton: TextButton( // 主按钮
onPressed: () => Get.back(),
child: Text("Undo"),
),
);
7.2 对话框
Get.defaultDialog(
title: "Confirm",
middleText: "Are you sure?",
textConfirm: "Yes",
textCancel: "No",
onConfirm: () {
Get.back(result: true);
},
onCancel: () {
Get.back(result: false);
},
);
// 自定义对话框
Get.dialog(
AlertDialog(
title: Text("Custom Dialog"),
content: Text("This is a custom dialog"),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text("OK"),
),
],
),
);
7.3 底部表单
Get.bottomSheet(
Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text("Bottom Sheet Content"),
ElevatedButton(
onPressed: () => Get.back(),
child: Text("Close"),
),
],
),
),
);
8. 主题管理
8.1 动态主题切换
// 切换主题
Get.changeTheme(ThemeData.dark());
Get.changeThemeMode(ThemeMode.dark);
// 在控制器中切换主题
class ThemeController extends GetxController {
final RxBool isDarkMode = false.obs;
void toggleTheme() {
isDarkMode.value = !isDarkMode.value;
Get.changeThemeMode(isDarkMode.value ? ThemeMode.dark : ThemeMode.light);
}
}
8.2 主题模式
ThemeMode.light: 浅色模式ThemeMode.dark: 深色模式ThemeMode.system: 系统模式
GetMaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: ThemeMode.system,
)
9. 平台检测
if (GetPlatform.isWeb) {
// Web 特定代码
}
if (GetPlatform.isMobile) {
// 移动端特定代码
}
if (GetPlatform.isAndroid) {
// Android 特定代码
}
if (GetPlatform.isIOS) {
// iOS 特定代码
}
if (GetPlatform.isDesktop) {
// 桌面端特定代码
}
if (GetPlatform.isWindows) {
// Windows 特定代码
}
if (GetPlatform.isLinux) {
// Linux 特定代码
}
if (GetPlatform.isMacOS) {
// macOS 特定代码
}
10. 工具函数
10.1 GetUtils
提供各种实用工具函数:
// 验证函数
GetUtils.isEmail("test@example.com"); // 验证邮箱
GetUtils.isValidUrl("https://example.com"); // 验证URL
GetUtils.isNumeric("123"); // 验证数字
GetUtils.isPhoneNumber("+1234567890"); // 验证电话号码
// 防抖和节流
GetUtils.debounce(() => print("Debounced"), time: Duration(milliseconds: 300));
GetUtils.throttle(() => print("Throttled"), time: Duration(milliseconds: 500));
// 其他工具
GetUtils.capitalize("hello"); // 首字母大写
GetUtils.toTitleCase("hello world"); // 标题格式
GetUtils.random(1, 10); // 随机数
10.2 异步操作
Get.isSnackbarOpen; // 检查 Snackbar 是否打开
Get.focusScope; // 获取焦点范围
Get.rawPath; // 获取原始路径
Get.routing; // 获取当前路由信息
11. 数据持久化
11.1 GetStorage
轻量级本地存储解决方案:
import 'package:get_storage/get_storage.dart';
class StorageService extends GetxService {
late final box = GetStorage();
Future<void> init() async {
await GetStorage.init(); // 初始化
}
// 保存数据
Future<void> write(String key, dynamic value) async {
await box.write(key, value);
}
// 读取数据
T? read<T>(String key, [T? defaultValue]) {
return box.read<T>(key) ?? defaultValue;
}
// 删除数据
Future<void> remove(String key) async {
await box.remove(key);
}
// 清空所有数据
Future<void> erase() async {
await box.erase();
}
}
12. 导航深度控制
12.1 路由栈操作
// 获取当前路由信息
Get.currentRoute;
// 检查路由栈长度
Get.routing.isBackButtonActive;
// 直到某个条件为止
Get.until((route) => Get.currentRoute == '/home');
// 替换当前路由
Get.offAllNamed('/new-route');
// 按条件移除路由
Get.removeRoute(predicate: (route) => route.settings.name == '/temp');
12.2 嵌套路由
// 嵌套导航示例
class MainTabPage extends GetView<AppController> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: controller.currentIndex.value,
children: const [
HomePage(),
ProfilePage(),
SettingsPage(),
],
),
bottomNavigationBar: Obx(
() => BottomNavigationBar(
currentIndex: controller.currentIndex.value,
onTap: controller.changeTabIndex,
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'),
],
),
),
);
}
}
13. 最佳实践
13.1 组织架构
- 将控制器、服务、模型分离
- 使用 bindings 管理依赖关系
- 合理使用响应式变量
- 遵循单一职责原则
13.2 性能优化
- 避免不必要的重建
- 合理使用懒加载
- 及时释放资源
- 使用合适的监听器
13.3 代码组织
// 控制器绑定示例
class HomeBinding implements Bindings {
@override
void dependencies() {
Get.lazyPut(() => HomeController());
Get.lazyPut(() => UserService());
Get.lazyPut(() => ProductService());
}
}
// 在路由中使用
GetPage(
name: '/home',
page: () => HomeScreen(),
binding: HomeBinding(),
)
14. 常见问题
14.1 内存泄漏
确保在 onClose() 中清理资源:
@override
void onClose() {
// 关闭所有响应式变量
count.close();
list.close();
// 取消所有订阅
subscription?.cancel();
// 关闭所有控制器
controller.close();
super.onClose();
}
14.2 路由错误
使用 GetPage 配置路由以避免错误:
GetPage(
name: '/home',
page: () => HomeScreen(),
binding: HomeBinding(),
middlewares: [AuthMiddleware()],
transition: Transition.cupertino,
)
14.3 状态同步问题
使用响应式变量确保状态同步:
// 正确的方式
final count = 0.obs;
// 错误的方式
var count = 0; // 不会触发重建
15. 高级功能
15.1 条件路由
Get.toNamed(
condition ? '/dashboard' : '/onboarding',
arguments: data,
);
15.2 动态路由参数
// 动态构建路由
String buildRoute(String base, Map<String, String> params) {
String route = base;
params.forEach((key, value) {
route = route.replaceAll(':$key', value);
});
return route;
}
15.3 路由拦截器
class RouteInterceptor extends GetMiddleware {
@override
RouteSettings? redirect(String? route) {
// 根据条件重定向
if (needsAuth(route) && !isAuthenticated()) {
return const RouteSettings(name: '/login');
}
return null;
}
}
16. 总结
GetX 是一个功能强大且易于使用的 Flutter 状态管理和路由管理解决方案。通过响应式编程、依赖注入和简化的路由管理,它可以显著提高开发效率和代码质量。
GetX 的核心优势包括:
- 简单易用: API 设计简洁,学习成本低
- 性能优秀: 响应式更新机制,只重建需要的部分
- 功能完整: 集成状态管理、依赖注入、路由管理
- 内存友好: 自动管理生命周期,防止内存泄漏
- 类型安全: 提供良好的类型检查支持
掌握 GetX 的各项功能有助于构建高效、可维护的 Flutter 应用。通过合理使用响应式变量、依赖注入和路由管理,可以构建出用户体验良好、性能优异的移动应用。