在Flutter開發(fā)中狀態(tài)管理是必不可少的,這個和前端vue的開發(fā)類似。flutter原生提供狀態(tài)管理組件InheritedWidget功能單一,使用場景有限,不過flutter社區(qū)有很多優(yōu)秀的狀態(tài)管理插件:如筆者一直用的是 provider,它可以實(shí)現(xiàn)主題切換、灰色模式、暗黑模式等常見的App的功能。和provider不同的是 Getx更加的強(qiáng)大。我們可以認(rèn)為Getx是flutter的一個框架,它不僅提供了狀態(tài)管理,還提供了一些常用的組件、路由管理等。
在使用Getx之前,我們不能著急上馬,應(yīng)認(rèn)真讀一下官方說明,具體詳見https://pub.dev/packages/get,在官方的前言里面有矚目的Getx說明,需要重點(diǎn)關(guān)注:
1 性能:Getx專注于性能和資源消耗最小。Getx不使用Streams或ChangeNotifier。
2 Getx允許視圖、演示邏輯、業(yè)務(wù)邏輯、依賴注入和導(dǎo)航的完全解耦。您不需要上下文來在路由之間導(dǎo)航,因此您不依賴小部件樹(可視化)。您不需要上下文來通過 inheritedWidget訪問控制器/塊,因此您可以將演示邏輯和業(yè)務(wù)邏輯與可視化層完全解耦。您不需要通過MultiProvider將Controllers/Models/Blocs類注入到小部件樹中。為此,GetX使用自己的依賴注入功能,將DI與其視圖完全解耦。
3 如果您僅將Getx用于狀態(tài)管理或依賴項(xiàng)管理,則無需使用GetMaterialApp。GetMaterialApp對于與路由和無上下文相關(guān)的路由、小組件、國際化、sheet、對話框和高級apis是必要的。
Getx簡單使用
導(dǎo)入頭文件import 'package:get/get.dart';
1 在mian.dart里面使用GetMaterialApp,其實(shí)這樣使用Getx對工程的侵入非常大的,所以在項(xiàng)目的初期應(yīng)該考慮是否使用Getx,而不是隨著項(xiàng)目深入使用GetMaterialApp。
2 常用的小組件
2.1 snackbar
Get.snackbar("Snackbar 標(biāo)題", "歡迎使用Snackbar");
2.2 Dialog
Get.defaultDialog();
2.3 Sheet
Get.bottomSheet()
3 路由router
Getx的路由是調(diào)研過程中最大的收獲,無論使用flutter自帶的路由,還是使用第三方路由,都離不開 context,有些路由需要使用命令才能生成路由,比如auto_route,需要在dev_dependencies;生成路由:aauto_route_generator,flutter packages pub run build_runner build,使用起來比較啰嗦。那么Getx的路由完全和context隔離,只需要Get.to(OhterPageWidget()),就可以跳轉(zhuǎn)界面。to()方法接口也非常豐富,也可以自定義動畫,轉(zhuǎn)場動畫等。
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入口設(shè)置路由路徑:
getPages: [
// 首頁
GetPage(name: "/home", page: ()=>Home()),
// 我的
GetPage(name: "/mine", page: ()=>Minne()),
// 設(shè)置
GetPage(name: "/setting", page: ()=>Setting())
],
使用toNamed方法 Get.toNamed("/home")。
4 狀態(tài)管理
在學(xué)習(xí)狀態(tài)管理之前我們需要了解Obx和GetxController。
Obx關(guān)于它官方是這樣描述的:
反應(yīng)狀態(tài)管理器(GetX/Obx):
反應(yīng)式編程可以疏遠(yuǎn)許多人,因?yàn)樗鼡?jù)說很復(fù)雜。GetX將反應(yīng)式編程變成了非常簡單的事情:
您不需要創(chuàng)建StreamController。
您不需要為每個變量創(chuàng)建StreamBuilder
您無需為每個州創(chuàng)建類。
您無需為初始值創(chuàng)建get。
你不需要使用代碼生成器
使用Get進(jìn)行反應(yīng)式編程就像使用setState一樣簡單。
比如我們觀察var name = 'Jonatas Borges'的變化,只需寫成var name = 'Jonatas Borges'.obs,除了給屬性值后面添加.obs外,還有2種定義Obx變量的方法:
第一種 使用 Rx{Type} 。
final name = RxString('Jonatas Borges');
第二種是使用 Rx,規(guī)定泛型 Rx<Type>
final name = Rx<String>('Jonatas Borges');
GetxController主要的作用是用于UI代碼與業(yè)務(wù)邏輯分離開來。下面是個簡單的使用:
class Controller extends GetxController {
var count = 0.obs;
var age = 18.obs;
void increment() => count++;
@override
void onInit() {
super.onInit();
//監(jiān)聽count的值變化
ever(count, (callback) => print("count"));
// 監(jiān)聽count age的值
everAll([count, age], (callback) => print(callback));
once(count, (callback) => print("count值改變時(shí)調(diào)用,只執(zhí)行一次"));
debounce(count, (callback) => print("1s后調(diào)用,防止DDos"));
interval(count, (callback) => print("忽略1s內(nèi)所有變動"));
}
}
...
appBar: AppBar(
title: Obx(
// 展示count
() => Text("Clicks: ${c.count}"),
),
),
...
floatingActionButton: FloatingActionButton(
// 修改count
onPressed: () => c.increment(),
child: Icon(Icons.add),
),
uniqueID:GetxController監(jiān)聽過程中,單獨(dú)在某個GetBuilder上設(shè)置。這樣更好的管理這個狀態(tài)。
```tag```:區(qū)分不同的控制器來實(shí)現(xiàn)不同的功能,減少控制器代碼量和重復(fù)創(chuàng)建。
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 國際化配置
國際化配置是狀態(tài)管理領(lǐng)域的一個重要應(yīng)用,使用 Getx非常快捷方便,是做這種應(yīng)用的不二選擇。
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(),
// 設(shè)置默認(rèn)語言
locale: Locale("zh",'CN'),
// 在配置錯誤的情況下 顯示的語言
fallbackLocale: Locale("zh",'CN'),
...
}
3 使用國際化配置
首先在狀態(tài)管理類里實(shí)現(xiàn)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("切換英文"),
),