在Flutter开发中状态管理是必不可少的,这个和前端vue的开发类似。flutter原生提供状态管理组件InheritedWidget
功能单一,使用场景有限,不过flutter社区有很多优秀的状态管理插件:如笔者一直用的是 provider
,它可以实现主题切换、灰色模式、暗黑模式等常见的App的功能。和provider
不同的是 Getx
更加的强大。我们可以认为Getx
是flutter的一个框架,它不仅提供了状态管理,还提供了一些常用的组件、路由管理等。
在使用Getx
之前,我们不能着急上马,应认真读一下官方说明,具体详见https://pub.dev/packages/get,在官方的前言里面有瞩目的Getx
说明,需要重点关注:
1 性能:Getx
专注于性能和资源消耗最小。Getx
不使用Streams或ChangeNotifier。
2 Getx
允许视图、演示逻辑、业务逻辑、依赖注入和导航的完全解耦。您不需要上下文来在路由之间导航,因此您不依赖小部件树(可视化)。您不需要上下文来通过 inheritedWidget访问控制器/块,因此您可以将演示逻辑和业务逻辑与可视化层完全解耦。您不需要通过MultiProvider将Controllers/Models/Blocs类注入到小部件树中。为此,GetX使用自己的依赖注入功能,将DI与其视图完全解耦。
3 如果您仅将Getx
用于状态管理或依赖项管理,则无需使
用GetMaterialApp。GetMaterialApp对于与路由和无上下文相关的路由、小组件、国际化、sheet、对话框和高级apis是必要的。
Getx简单使用
导入头文件import 'package:get/get.dart';
1 在mian.dart里面使用GetMaterialApp
,其实这样使用Getx
对工程的侵入非常大的,所以在项目的初期应该考虑是否使用Getx
,而不是随着项目深入使用GetMaterialApp
。
2 常用的小组件
2.1 snackbar
Get.snackbar("Snackbar 标题", "欢迎使用Snackbar");
2.2 Dialog
Get.defaultDialog();
2.3 Sheet
Get.bottomSheet()
3 路由router
Getx
的路由是调研过程中最大的收获,无论使用flutter自带的路由,还是使用第三方路由,都离不开 context
,有些路由需要使用命令才能生成路由,比如auto_route
,需要在dev_dependencies;
生成路由:aauto_route_generator
,flutter packages pub run build_runner build
,使用起来比较啰嗦。那么Getx
的路由完全和context
隔离,只需要Get.to(OhterPageWidget())
,就可以跳转界面。to()
方法接口也非常丰富,也可以自定义动画,转场动画等。
Future<T?>? to<T>(
dynamic page, {
bool? opaque,
Transition? transition,
Curve? curve,
Duration? duration,
int? id,
String? routeName,
bool fullscreenDialog = false,
dynamic arguments,
Bindings? binding,
bool preventDuplicates = true,
bool? popGesture,
double Function(BuildContext context)? gestureWidth,
})
除了to()
方法外,还可用用toNamed
;
在main.dart入口设置路由路径:
getPages: [
// 首页
GetPage(name: "/home", page: ()=>Home()),
// 我的
GetPage(name: "/mine", page: ()=>Minne()),
// 设置
GetPage(name: "/setting", page: ()=>Setting())
],
使用toNamed
方法 Get.toNamed("/home")
。
4 状态管理
在学习状态管理之前我们需要了解Obx
和GetxController
。
Obx
关于它官方是这样描述的:
反应状态管理器(GetX/Obx):
反应式编程可以疏远许多人,因为它据说很复杂。GetX将反应式编程变成了非常简单的事情:
您不需要创建StreamController。
您不需要为每个变量创建StreamBuilder
您无需为每个州创建类。
您无需为初始值创建get。
你不需要使用代码生成器
使用Get进行反应式编程就像使用setState一样简单。
比如我们观察var name = 'Jonatas Borges'
的变化,只需写成var name = 'Jonatas Borges'.obs
,除了给属性值后面添加.obs
外,还有2种定义Obx
变量的方法:
第一种 使用 Rx{Type} 。
final name = RxString('Jonatas Borges');
第二种是使用 Rx,规定泛型 Rx<Type>
final name = Rx<String>('Jonatas Borges');
GetxController
主要的作用是用于UI代码与业务逻辑分离开来。下面是个简单的使用:
class Controller extends GetxController {
var count = 0.obs;
var age = 18.obs;
void increment() => count++;
@override
void onInit() {
super.onInit();
//监听count的值变化
ever(count, (callback) => print("count"));
// 监听count age的值
everAll([count, age], (callback) => print(callback));
once(count, (callback) => print("count值改变时调用,只执行一次"));
debounce(count, (callback) => print("1s后调用,防止DDos"));
interval(count, (callback) => print("忽略1s内所有变动"));
}
}
...
appBar: AppBar(
title: Obx(
// 展示count
() => Text("Clicks: ${c.count}"),
),
),
...
floatingActionButton: FloatingActionButton(
// 修改count
onPressed: () => c.increment(),
child: Icon(Icons.add),
),
uniqueID
:GetxController
监听过程中,单独在某个GetBuilder
上设置。这样更好的管理这个状态。
```tag```:区分不同的控制器来实现不同的功能,减少控制器代码量和重复创建。
class Controller extends GetxController {
...
void testUniqueID() {
age += 10;
// home_age_id:uniqueID
update(["home_age_id"]);
}
...
}
final Controller c = Get.put(Controller());
// print(c);
return Scaffold(
body: Column(
children: [
//GetBuilder <Controller> 必须指定 不然会崩溃?。?!
GetBuilder<Controller>(
id: "home_age_id",
tag:"tagxxxx",
builder: (controller) {
return Obx(
() => Text("Clicks: ${c.age}"),
);
// return Text("1111");
// return Text("年龄值为: ${c.age}",);
},
),
ElevatedButton(
onPressed: () => c.testUniqueID(),
child: Text("增加年龄"),
)
],
),
);
4 国际化配置
国际化配置是状态管理领域的一个重要应用,使用 Getx
非??旖莘奖?,是做这种应用的不二选择。
1 自定义个继承与Translations
的类,重写keys
class InternationalConfigure extends Translations {
@override
// TODO: implement keys
Map<String, Map<String, String>> get keys => {
'zh_CN': {
'hello': "你好, GetX"
},
'en_US': {
'hello': 'Hello GetX',
},
};
}
2 在入口出国际化配置
GetMaterialApp {
...
translations: InternationalConfigure(),
// 设置默认语言
locale: Locale("zh",'CN'),
// 在配置错误的情况下 显示的语言
fallbackLocale: Locale("zh",'CN'),
...
}
3 使用国际化配置
首先在状态管理类里实现updateLocale
方法,
class Controller extends GetxController {
...
void changeLanguage(String languageCode, String countryCode) {
Get.updateLocale(Locale(languageCode, countryCode));
}
...
}
然后就可以切换使用了。
Text('hello'.tr, style: TextStyle(color: Colors.pink, fontSize: 30)),
ElevatedButton(
onPressed: () => c.changeLanguage('zh', 'CN'),
child: Text("切换中文"),
),
ElevatedButton(
onPressed: () => c.changeLanguage('en', 'US'),
child: Text("切换英文"),
),