Getx簡介
詳情請看:https://github.com/jonataslaw/getx/blob/master/README.zh-cn.md 中文介紹
版本:get: 3.24.0
Getx 提供了非常豐富的功能, 功能模塊:
├── lib
│ ├── get_connect [網(wǎng)絡模塊]
│ │ ├── http
│ │ └── sockets
│ ├── get_core [核心模塊]
│ ├── get_instance [依賴管理模塊]
│ ├── get_navigation [路由管理模塊]
│ ├── get_rx [響應式模塊(類似RxDart)]
│ ├── get_state_manager [狀態(tài)管理模塊]
│ └── get_utils [通用工具]
│ ├── extensions [一些基礎功能擴展]
│ ├── get_utils [提供常用功能集(各種判斷isNull玛臂、isBlank封孙、isNum等等等)]
│ ├── platform [提供平臺判斷(isAndroid、isIOS)]
│ └── queue [隊列]
安裝
- 在pubspec.yaml文件中添加依賴 get: ^3.24.0
- MaterialApp改為GetMaterialApp
請看以下完整代碼泡徙,實現(xiàn)了HelloWorld的功能:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() => runApp(GetMaterialApp(home: Home()));
/// 計數(shù)器控制器
class CounterController extends GetxController {
var count = 0.obs;
}
class Home extends StatelessWidget {
// 初始化計數(shù)器控制器膜蠢,放入依賴管理中,并獲取這個實例
final _counter = Get.put(CounterController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Count Getx')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
Obx(() => Text(// 響應數(shù)據(jù)變化
'${_counter.count.value}',
style: Theme.of(context).textTheme.headline4,
)),
],
),
),
floatingActionButton: FloatingActionButton(
// 觸發(fā)數(shù)據(jù)變化
onPressed: () => _counter.count + 1,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
- 代碼分析
- CounterController類
- 繼承自GetxController
- 聲明了 一個響應式的變量count庶橱,類型為RxInt贪惹,get_rx模塊中定義寂嘉。
- Home類
- 變量_counter 初始化計數(shù)器控制器枫绅,放入依賴管理中硼端,并獲取這個實例
- 對應的響應數(shù)據(jù)變化使用了 Rx類型對應的Widget:Obx。 當CounterController.count 值方發(fā)生變化時县耽,觸發(fā)Obx的更新镣典。當在Obx中調(diào)用_counter.count.value時,RxInterface.proxy.addListener()為這個值添加了監(jiān)聽事件
有幾個疑問
1. 狀態(tài)管理數(shù)據(jù)是怎么存儲的澎剥?
final _counter = Get.put(CounterController());
調(diào)用流程詳見下圖:
static final Map<String, _InstanceBuilderFactory> _singl = {};
......
S put<S>(
S dependency, {
String tag,
bool permanent = false,
@deprecated InstanceBuilderCallback<S> builder,
}) {
_insert(
isSingleton: true,
name: tag,
permanent: permanent,
builder: builder ?? (() => dependency));
return find<S>(tag: tag);
}
調(diào)用棧中哑姚,可以看到CounterController最終數(shù)據(jù)存到了GetInstance全局單例中芜茵。 與provider狀態(tài)管理相比,provider數(shù)據(jù)是存放到了InheritedWidget中绞佩,InheritedWidget特性是只有子節(jié)點才能獲取到存放的數(shù)據(jù)蒸辆, 在和界面無關時,無法獲取到provider中的數(shù)據(jù)谆奥。相比之下拂玻,Getx把數(shù)據(jù)存到了單例對象中,不依賴于UI魄懂, 這種方式更加靈活闯第,試用范圍更廣。
2. 狀態(tài)管理怎么做到自動管理的填帽?
詳見下圖:
-
添加GetxController和路由的綁定關系
stack2.png
// 保存路由和GetxController的對應關系
static final Map<String, String> _routesKey = {};
......
// 將CounterController的類名與當前路由對應起來篡腌, 存入_routesKey中。
void _registerRouteInstance<S>({String tag}) {
_routesKey.putIfAbsent(_getKey(S, tag), () => Get.reference);
}
-
當頁面退出時嘹悼,GetPageRoute.dispose 移除_routesKey中對應的Key與Value杨伙,并刪除在GetInstance._singl中保存的GetxController類
截屏2021-01-08 下午2.22.00.png
注意:Get.put調(diào)用的時機,像開頭例子中Home就不能正確綁定路徑棠赛。 因為還沒有進入Home頁面時膛腐,Get.put就已經(jīng)觸發(fā)了,這個時候Get.reference獲取的路徑是Home的上一個路由路徑辩涝。
3. Obx() 是怎么響應Obs變量變化的勘天?
Obx(() => Text(// 響應數(shù)據(jù)變化
'${_counter.count.value}',
style: Theme.of(context).textTheme.headline4,
)),
只能看到_counter.count.value獲取了值,應該也是這個地方綁定了數(shù)據(jù)響應關系商膊〕杞看源碼具體實現(xiàn):
T get value {
if (RxInterface.proxy != null) {
RxInterface.proxy.addListener(subject); // 這里建立了數(shù)據(jù)關系,添加了數(shù)據(jù)變化監(jiān)聽
}
return _value;
}
subject為GetStream<T>類型实幕。數(shù)據(jù)變化監(jiān)聽弄明白了堤器,再看看Obx是怎么監(jiān)聽數(shù)據(jù)變化的,看Obx源碼:
class Obx extends ObxWidget {
final WidgetCallback builder;
const Obx(this.builder);
@override
Widget build() => builder();
}
Obx繼承自ObxWidget整吆。
abstract class ObxWidget extends StatefulWidget {
const ObxWidget({Key key}) : super(key: key);
@override
_ObxState createState() => _ObxState();
@protected
Widget build();
}
class _ObxState extends State<ObxWidget> {
RxInterface _observer;
StreamSubscription subs;
_ObxState() {
_observer = RxNotifier();
}
@override
void initState() {
subs = _observer.listen(_updateTree);
super.initState();
}
Widget get notifyChilds {
final observer = RxInterface.proxy;
RxInterface.proxy = _observer;
final result = widget.build();
if (!_observer.canUpdate) {
throw """
[Get] the improper use of a GetX has been detected.
You should only use GetX or Obx for the specific widget that will be updated.
If you are seeing this error, you probably did not insert any observable variables into GetX/Obx
or insert them outside the scope that GetX considers suitable for an update
(example: GetX => HeavyWidget => variableObservable).
If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
""";
}
RxInterface.proxy = observer;
return result;
}
@override
Widget build(BuildContext context) => notifyChilds;
}
- initState() 方法中掂为, subs = _observer.listen(_updateTree),監(jiān)聽了數(shù)據(jù)更新,當數(shù)據(jù)更新后寸齐,調(diào)用_updateTree更新視圖。
- notifyChilds() 方法中RxInterface.proxy = _observer; 返回到Obx()開頭的代碼:
Obx(() => Text(// 響應數(shù)據(jù)變化
'${_counter.count.value}',
style: Theme.of(context).textTheme.headline4,
)),
T get value {
if (RxInterface.proxy != null) {
RxInterface.proxy.addListener(subject); // 這里建立了數(shù)據(jù)關系扰法,添加了數(shù)據(jù)變化監(jiān)聽
}
return _value;
}
可以看到RxInterface.proxy毅厚。 當Obx 執(zhí)行build時調(diào)用notifyChilds()吸耿,會把當前_observer賦值給RxInterface.proxy,再執(zhí)行widget.build()咽安,運行到_counter.count.value的get方法妆棒,RxInterface.proxy.addListener(subject); 這樣變量和Widget的關聯(lián)就完整建立。
- 恢復RxInterface.proxy糕珊,詳見下面代碼:
final result = widget.build();
if (!_observer.canUpdate) {
throw """
[Get] the improper use of a GetX has been detected.
You should only use GetX or Obx for the specific widget that will be updated.
If you are seeing this error, you probably did not insert any observable variables into GetX/Obx
or insert them outside the scope that GetX considers suitable for an update
(example: GetX => HeavyWidget => variableObservable).
If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
""";
}
RxInterface.proxy = observer;
RxInterface.proxy = observer; 恢復RxInterface.proxy為之前保存的值红选。
4. GetBuilder
這個綁定關系比較好理解,就不寫了
以上代碼為自己的理解玛瘸,有錯誤的歡迎各位大佬指正苟蹈。