Flutter made easy 这可不是我说的啊,他们的 readme 就是这么写的
虽然他们的文档已经写得很好了,但我还是想专门写点东西
Getx 是个啥
这是一个很方便使用的包,让状态管理、依赖注入和路由管理都变得简单多了,而且拿它写 MVVM 体感上和安卓原生是差不多的
当然它最主要的用途还是管理状态(数据),毕竟你也不想拖着InheritedWidget写一大堆模板代码吧
当然管理状态还有像 provider 之类的包,可以都去了解一下,然后看看哪种更合适你(或者是你的项目)
状态管理
状态管理说着高级,实际上主要还是在不同的 Widget 之间共享变量
简单的基本用法
现在如果有两个页面 PageA和PageB,它们同时可以修改一个变量count并显示,那我们怎么才能让修改在两个页面间同步呢
聪明的你肯定想到在它们的父组件里面加个InheritedWidget,但是吧每次都要创建这么一个类加上一堆长长的方法名,也太麻烦了
此时 Getx 的 controller就派上用场了,顾名思义它是一个控制器,像ViewModel一样生命周期独立于视图(当然在合适的时机也会自动释放啦)。于是需要共享的变量就可以放在里面
class CountController extend GetxController {
int count = 0;
}
然后在要使用这个变量之前初始化一下这个 controller(这部分涉及 Getx 的依赖管理)
Get.put(CountController()); // 初始化一个 CountController,并将它加入 Getx 的依赖管理器中
然后在需要使用这个变量的时候获取这个controller的实例
class PageA extend StatelessWidget {
@override
Widget build(BuildContext context) {
final controller = Get.find<CountController>(); // 在依赖管理器中找到一个 CountController 对象
print(controller.count);
...
}
}
就这么简单
但还有更方便的,因为我们要把这个count显示出来,所以在它变化时要更新 UI 就得用setState。可是如果我想写响应式呢?当然可以!只需要给这个变量做一点小标记,再在 UI 中略微修改就可以做到了
把count改成下面这样
class CountController extend GetxController {
var count = 0.obs;
}
把界面改成这样
class PageA extend StatelessWidget {
@override
Widget build(BuildContext context) {
final controller = Get.find<CountController>(); // 在依赖管理器中找到一个 CountController 对象
return Obx(() => Text(controller.count.value.toString()));
}
}
这个Text的内容就会随着count的值改变而改变,妈妈从此再也不用担心我忘记setState了!
不复杂的进阶用法
.obs 是啥
给上面的count加上.obs之后,这个变量就是“可观察的”了,也就是 Observable
除了直接加.obs,还可以用Rx<Type>来创建
var count = Rx<Int>(0); // 初始值是不必须的
还有一些很常用的类有对应的能快速创建对象的方法
final name = RxString('');
final isLogged = RxBool(false);
final count = RxInt(0);
final balance = RxDouble(0.0);
final items = RxList<String>([]);
final myMap = RxMap<String, int>({});
还没写完呢
依赖管理
解释什么是依赖注入是一件复杂的事,你只需要知道 Getx 提供了一个很好用的依赖管理器:只要你之前把你需要用的对象给放进去,那你就能随时随地,不需要传递什么上下文,在代码的任何一个位置获取到这个对象的实例
当然最多的还是放 Getx 的 Controller:
Controller controller = Get.put(Controller());
如果你需要在别的地方用到它,只需要这样:
final controller = Get.find();
真是太简单了!
但是很多时候事情没那么简单
就是说,如果我放了不止一个对象,Getx 又怎么知道我要的是哪个呢?这倒不是难事,因为Get.put()和Get.find()都是能指定类型的,就像这样:
Get.put<MyController>(MyController()); // 这里边的指定不是必须的
Get.put<YourController>(YourController());
final someController = Get.find<MyController>();
这样就能稳稳地获取到正确的类型了
然后又出现了新的问题:如果我有好多个MyController,又该怎么找到想要的那个呢?
这也很简单,因为Get.put()和Get.find()还可以指定一个 tag:
Get.put<MyController>(MyController(), tag: 'My first Controller');
Get.put<MyController>(MyController(), tag: 'My second Controller');
final someController = Get.find<MyController>(tag: 'My first Controller'); // 当然是获取第一个 Controller 啦
只要保证 tag 的字符串是唯一的,就能保证一定能获取对应的对象了
下面是官方文档对Get.put()的参数介绍:
Get.put<S>(
// 必备:你想得到保存的类,比如控制器或其他东西。
// 注:"S "意味着它可以是任何类型的类。
S dependency
// 可选:当你想要多个相同类型的类时,可以用这个方法。
// 因为你通常使用Get.find<Controller>()来获取一个类。
// 你需要使用标签来告诉你需要哪个实例。
// 必须是唯一的字符串
String tag,
// 可选:默认情况下,get会在实例不再使用后进行销毁
// (例如:一个已经销毁的视图的Controller)
// 但你可能需要这个实例在整个应用生命周期中保留在那里,就像一个sharedPreferences的实例或其他东西。
//所以你设置这个选项
// 默认值为false
bool permanent = false,
// 可选:允许你在测试中使用一个抽象类后,用另一个抽象类代替它,然后再进行测试。
// 默认为false
bool overrideAbstract = false,
// 可选:允许你使用函数而不是依赖(dependency)本身来创建依赖。
// 这个不常用
InstanceBuilderCallback<S> builder,
)
还有一些别的用法这里就不讲了,因为平常用得比较少,想学的话可以自己去研究 ( ̄﹃ ̄)
路由管理
官方文档是这么讲的
其实是我懒得写了,反正这些用法五分钟就学得会
其他的好东西
切换语言
可以像下面这样快速切换应用的语言:
var locale = Locale('en', 'US');
Get.updateLocale(locale);
获取上下文
可以用Get.context随时获取当前上下文,有一说一这个功能确实有点夸张,但是很多时候能少写一个参数欸