为什么选择GetX
Flutter状态管理方案众多,我在「回留」项目中选择GetX的原因:
- 学习曲线平缓,上手快
- 集成了状态管理、路由、依赖注入
- 代码量少,开发效率高
- 性能表现良好
项目结构组织
推荐按功能模块组织代码:
lib/
├── app/
│ ├── modules/
│ │ ├── home/
│ │ │ ├── bindings/
│ │ │ │ └── home_binding.dart
│ │ │ ├── controllers/
│ │ │ │ └── home_controller.dart
│ │ │ └── views/
│ │ │ └── home_view.dart
│ │ ├── photo/
│ │ │ ├── bindings/
│ │ │ ├── controllers/
│ │ │ └── views/
│ ├── routes/
│ │ ├── app_pages.dart
│ │ └── app_routes.dart
│ └── services/
│ └── storage_service.dart
Controller生命周期管理
GetX的Controller有完整的生命周期,合理使用可以避免内存泄漏:
class PhotoController extends GetxController {
final photos = <Photo>[].obs;
@override
void onInit() {
super.onInit();
// 初始化时调用,适合加载数据
loadPhotos();
}
@override
void onReady() {
super.onReady();
// Widget渲染完成后调用
// 适合执行需要context的操作
}
@override
void onClose() {
// 控制器销毁时调用
// 清理资源、取消订阅等
super.onClose();
}
Future<void> loadPhotos() async {
// 加载照片逻辑
}
}
依赖注入的正确姿势
使用Binding统一管理依赖注入:
class HomeBinding extends Bindings {
@override
void dependencies() {
// 懒加载,使用时才创建
Get.lazyPut<HomeController>(() => HomeController());
// 立即创建
// Get.put(HomeController());
// 每次获取都创建新实例
// Get.create<HomeController>(() => HomeController());
}
}
在路由中绑定:
GetPage(
name: Routes.HOME,
page: () => const HomeView(),
binding: HomeBinding(),
),
响应式状态 vs 简单状态
GetX提供两种状态管理方式:
响应式状态(.obs)
class CounterController extends GetxController {
final count = 0.obs;
void increment() => count.value++;
}
// 在View中使用
Obx(() => Text('${controller.count.value}'))
简单状态(update)
class CounterController extends GetxController {
int count = 0;
void increment() {
count++;
update(); // 手动触发更新
}
}
// 在View中使用
GetBuilder<CounterController>(
builder: (c) => Text('${c.count}'),
)
选择建议:
- 简单数据用.obs,代码更简洁
- 复杂对象或需要精细控制更新时用update()
- 列表数据推荐用.obs,自动监听增删改
Worker的妙用
Worker可以监听响应式变量的变化:
class SearchController extends GetxController {
final searchText = ''.obs;
final results = <String>[].obs;
@override
void onInit() {
super.onInit();
// 防抖:停止输入500ms后执行搜索
debounce(
searchText,
(_) => performSearch(),
time: const Duration(milliseconds: 500),
);
// 其他Worker类型
// ever() - 每次变化都执行
// once() - 只执行一次
// interval() - 固定间隔执行
}
void performSearch() {
// 执行搜索
}
}
全局服务的管理
对于需要全局访问的服务,在应用启动时初始化:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// 初始化全局服务
await initServices();
runApp(const MyApp());
}
Future<void> initServices() async {
// 永久存在的服务
Get.put(StorageService(), permanent: true);
Get.put(AuthService(), permanent: true);
// 等待异步初始化完成
await Get.find<StorageService>().init();
}
常见问题与解决
1. Controller被意外销毁
使用Get.find()时,确保Controller已经注入:
// 安全的获取方式
final controller = Get.find<HomeController>();
// 或者使用isRegistered检查
if (Get.isRegistered<HomeController>()) {
final controller = Get.find<HomeController>();
}
2. 页面返回后状态丢失
如果需要保持状态,使用permanent参数:
Get.put(MyController(), permanent: true);
3. 内存泄漏
确保在onClose中清理资源:
@override
void onClose() {
scrollController.dispose();
subscription?.cancel();
super.onClose();
}
总结
GetX是一个功能强大且易用的Flutter框架,合理使用可以大大提升开发效率。关键是理解其生命周期管理和依赖注入机制,避免常见的坑。