目錄:
1.優(yōu)點
2.優(yōu)點分析: GetX怎么將邏輯層和界面層解耦的
3.優(yōu)點分析: GetX怎么實現(xiàn)狀態(tài)管理的? Obx的基本原理是什么? 局部刷新原理? obx和obs?
4.優(yōu)點分析: binding基本原理是什么?
5.優(yōu)點分析: 路由管理基本原理是什么?
6.getx的缺點是啥?
7.手寫getX
1. GetX相關(guān)優(yōu)勢
1.1 )依賴注入
GetX是通過依賴注入的方式,存儲相應(yīng)的XxxGetxController;已經(jīng)脫離了InheritedWidget那一套玩法,自己手動去管理這些實例,使用場景被大大拓展
簡單的思路,卻能產(chǎn)生深遠的影響:優(yōu)雅的跨頁面功能便是基于這種設(shè)計而實現(xiàn)的、獲取實例無需BuildContext、GetBuilder自動化的處理及其減少了入?yún)⒌鹊?/p>
1.2 )跨頁面交互的狀態(tài)管理
這絕對是GetX的一個優(yōu)點!對于復(fù)雜的生產(chǎn)環(huán)境,跨頁面交互的場景,實在太常見了,GetX的跨頁面交互,實現(xiàn)的也較為優(yōu)雅
1.3 )路由管理
getx內(nèi)部實現(xiàn)了路由管理,而且用起來,非常簡單!bloc沒實現(xiàn)路由管理,我不得不找一個star量高的路由框架,就選擇了fluro,但是不得不吐槽下,fluro用起來真的很折磨人,每次新建一個頁面,最讓我抗拒的就是去寫fluro路由代碼,橫跨幾個文件來回寫,頭皮發(fā)麻
GetX實現(xiàn)了動態(tài)路由傳參,也就是說直接在命名路由上拼參數(shù),然后能拿到這些拼在路由上的參數(shù),也就是說用flutter寫H5,直接能通過Url傳值,OMG!可以無腦舍棄復(fù)雜的fluro了
1.4 ) 實現(xiàn)了全局BuildContext
1.5 )國際化,主題實現(xiàn)
生命周期
用了Getx的state管理之后, 你再也用不著StatefulWidget了. 僅僅StatelessWidget就夠你用了! 性能自然也提升很多!
2. GetX怎么將邏輯層和界面層解耦的
此處需要劃分三個結(jié)構(gòu)了:state(狀態(tài)層),logic(邏輯層),view(界面層)
為什么寫成這樣三個模塊,需要把State單獨提出來,為了復(fù)雜的業(yè)務(wù), 顯的更簡單!
舉例:
之前的寫法
class IdentificationCard extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return IdentificationState();
}
}
class IdentificationState extends State<IdentificationCard> {
String date = "555";
String name = "666";
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('date : $date'),
Text('name : $name'),
GestureDetector(onTap: () {
setState(() {
date = "777";
});
}, child: const Text('修改'))],
);
}
}
用getx的實現(xiàn)
新建一個類, 定義一個狀態(tài), GetxController
添加一個Obx就能自動監(jiān)聽狀態(tài)的改變并且刷新UI了
/// 狀態(tài)
class IdentificationState {
RxString date = "555".obs;
RxString name = "666".obs;
}
/// 業(yè)務(wù)邏輯
class IdentificationController extends GetxController {
IdentificationState state = IdentificationState();
}
/// 展示
class IdentificationCard extends StatelessWidget {
IdentificationController controller = Get.put(IdentificationController());
@override
Widget build(BuildContext context) {
return Obx(() {
return Column(
children: [
Text('date : ${controller.state.date}'),
Text('name : ${controller.state.name}'),
GestureDetector(
onTap: () {
controller.state.date.value = "777";
},
child: const Text('修改'))
],
);
});
}
}
3.GetX實現(xiàn)狀態(tài)管理
GetX 的刷新方案分為手動刷新與自動刷新,GetBuilder 與 Obx ,分別對應(yīng)范圍刷新與局部刷新
2者的區(qū)別:
Obx 響應(yīng)式狀態(tài)管理
Obx 可以配合響應(yīng)式字段局部的精準(zhǔn)刷新避免父容器無效重構(gòu),缺點是字段變?yōu)轫憫?yīng)式的Rx包裝類,布局也需要被Obx包裹了,破壞了原生代碼觀賞性。
GetBuilder 狀態(tài)管理器
GetBuilder 就是指定區(qū)域范圍手動去刷新的,可以分區(qū)設(shè)置多個刷新區(qū)域,可選擇單個控件或容器,在一些特定場景下有奇效,但是如果不理解濫用一樣會導(dǎo)致性能問題。
3.1 Obx 的基本原理是什么?
Obx是配合Rx響應(yīng)式變量使用
這樣一來我們就明白了Obx實際上是一個StatefulWidget,它里面監(jiān)聽了一個GetStream,一旦GetStream有事件通知,它就會進行setState重新進行Widget的構(gòu)造.
GetBuilder 與 Obx 兩者結(jié)合,一個指定區(qū)域范圍手動刷新,一個是局部控件點對點刷新
var build = () => Text(name.value)
Obx(build);
源碼: Obx繼承了一個抽象ObxWidget類,將傳遞進來的build方法給了ObxWidget
class Obx extends ObxWidget {
final WidgetCallback builder;
const Obx(this.builder);
@override
Widget build() => builder();
}
ObxWidget繼承了有狀態(tài)組件,并且build函數(shù)讓Obx類實現(xiàn)了
abstract class ObxWidget extends StatefulWidget {
const ObxWidget({Key? key}) : super(key: key);
@override
_ObxState createState() => _ObxState();
@protected
Widget build();
}
對GexX的狀態(tài)管理做一個簡單總結(jié),
基于Obx收集依賴狀態(tài), 實際一個StatefulWidget,它的State也就是ObxState中監(jiān)聽了GetStream事件流,通過接收GetStream事件流調(diào)用setState重新構(gòu)建Obx,Rx對象在改變value的時候會向GetStream事件流發(fā)送事件,這樣就會導(dǎo)致Obx進行刷新了.

3.2 GetBuilder
GetBuilder 是一個 Widget 組件, 在 GetX 的狀態(tài)管理中,GetBuilder 的主要作用是結(jié)合 GetxController 實現(xiàn)界面數(shù)據(jù)的更新
demo使用
class CounterBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => CounterController());
}
}
class CounterController extends GetxController {
int count = 0;
void increase(){
count += 1;
update();
}
}
class CounterPage extends StatelessWidget {
final controller = Get.find<CounterController>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Counter"),
),
body: Center(
child: GetBuilder<CounterController>(builder: (logic) {
return Text("${controller.count}", style: const TextStyle(fontSize: 50),);
}),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: controller.increase,
),
);
}
}
demo調(diào)用總結(jié): 然后調(diào)用 update 方法更新界面數(shù)據(jù),從而實現(xiàn)計數(shù)器的功能。
狀態(tài)管理源碼分析:
class GetBuilder<T extends GetxController> extends StatefulWidget {
final GetControllerBuilder<T> builder;
final bool global;
final Object? id;
final String? tag;
final bool autoRemove;
final bool assignId;
final Object Function(T value)? filter;
final void Function(GetBuilderState<T> state)? initState,
dispose,
didChangeDependencies;
final void Function(GetBuilder oldWidget, GetBuilderState<T> state)?
didUpdateWidget;
final T? init;
const GetBuilder({
Key? key,
this.init,
this.global = true,
required this.builder,
this.autoRemove = true,
this.assignId = false,
this.initState,
this.filter,
this.tag,
this.dispose,
this.id,
this.didChangeDependencies,
this.didUpdateWidget,
}) : super(key: key);
@override
GetBuilderState<T> createState() => GetBuilderState<T>();
}
GetBuilder 是繼承自 StatefulWidget
GetBuilder 就是指定區(qū)域范圍手動去刷新的,可以分區(qū)設(shè)置多個刷新區(qū)域,可選擇單個控件或容器,在一些特定場景下有奇效,但是如果不理解濫用一樣會導(dǎo)致性能問題。
GetBuilderState
class GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
with GetStateUpdaterMixin {
T? controller;
bool? _isCreator = false;
VoidCallback? _remove;
Object? _filter;
@override
void initState() {...}
void _subscribeToController() {...}
void _filterUpdate() {...}
@override
void dispose() {...}
@override
void didChangeDependencies() {...}
@override
void didUpdateWidget(GetBuilder oldWidget) {...}
@override
Widget build(BuildContext context) {...}
}
方法: build()
@override
Widget build(BuildContext context) {
return widget.builder(controller!);
}

通過對 GetBuilder 的源碼分析,基本了解了 GetBuilder 各個參數(shù)的作用和實現(xiàn)原理。
GetBuilder 參數(shù)作用總結(jié)如下:
builder: Widget 構(gòu)建器,創(chuàng)建界面顯示的 Widget
init: 初始化 Controller 值,當(dāng) global 為 false 時使用該值作為 Controller,當(dāng) global 為 true 時且 Controller 未注冊依賴,則將 init 的值注入依賴使用。
global: 是否全局,作用于 Controller 初始化中,與 init 結(jié)合使用
autoRemove: 是否自動移除 Controller 依賴,結(jié)合 assignId 一起使用
assignId: 為 true 時結(jié)合 autoRemove 使用會自動移除 Controller 依賴關(guān)系
filter: 過濾器,通過返回值過濾是否需要刷新,返回值變化時才會刷新界面
tag: Controller 依賴注入的 tag,根據(jù) tag 獲取 Controller 實例
id: 刷新標(biāo)識,結(jié)合 Controller 的 update 使用,可以刷新指定 GetBuilder 控件內(nèi)的 Widget
initState: 回調(diào)函數(shù),生命周期 initState 方法中調(diào)用
dispose: 回調(diào)函數(shù),生命周期 dispose 中調(diào)用
didUpdateWidget: 回調(diào)函數(shù),生命周期 didUpdateWidget 中調(diào)用
didChangeDependencies: 回調(diào)函數(shù),生命周期 didChangeDependencies 中調(diào)用
4. 依賴管理: Binding:
依賴注入: 就是賦值, 但是很多類給你的類賦值, 這樣就很亂了
Binding的使用: 一般和controller在一起使用
binding模塊需要在getx路由頁面進行綁定;進入頁面的時候,統(tǒng)一懶注入binding模塊的GetXController

class _GetImpl extends GetInterface {}
final Get = _GetImpl();
extension Inst on GetInterface {
S put<S>(S dependency,
{String? tag,
bool permanent = false,
InstanceBuilderCallback<S>? builder}) =>
GetInstance().put<S>(dependency, tag: tag, permanent: permanent);
}
class GetInstance {
factory GetInstance() => _getInstance ??= GetInstance._();
const GetInstance._();
static GetInstance? _getInstance;
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);
}
void _insert<S>({
bool? isSingleton,
String? name,
bool permanent = false,
required InstanceBuilderCallback<S> builder,
bool fenix = false,
}) {
final key = _getKey(S, name);
_singl.putIfAbsent(
key,
() => _InstanceBuilderFactory<S>(
isSingleton,
builder,
permanent,
false,
fenix,
name,
),
);
}
String _getKey(Type type, String? name) {
return name == null ? type.toString() : type.toString() + name;
}
S find<S>({String? tag}) {
final key = _getKey(S, tag);
if (isRegistered<S>(tag: tag)) {
if (_singl[key] == null) {
if (tag == null) {
throw 'Class "$S" is not registered';
} else {
throw 'Class "$S" with tag "$tag" is not registered';
}
}
final i = _initDependencies<S>(name: tag);
return i ?? _singl[key]!.getDependency() as S;
} else {
// ignore: lines_longer_than_80_chars
throw '"$S" not found. You need to call "Get.put($S())" or "Get.lazyPut(()=>$S())"';
}
}
}
5. 路由管理之命名路由
特點:封裝了context, 封裝了攔截器!
路由管理之簡單路由
GetMaterialApp(
unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
routingCallback: (routing) {
if(routing?.current == '/second'){
///處理一些業(yè)務(wù)
}
},
initialRoute: '/',
getPages: [
GetPage(name: '/first', page: ()=>First()),
GetPage(name: '/second', page: ()=>Second())
],
)

問題: 為何不用Flutter自己的Router系統(tǒng)?
使用時還需要有一個context實例. 但我們并不是隨時隨地都持有一個context的, 這也局限了我們的使用場景.
6. GETX的缺點:
第一個缺點
Get.to(widgetObj, bindings)是可以注入binding.
但是Get.toNamed()并不支持binding參數(shù)啊. 我的跳轉(zhuǎn)一般都是用toNamed的, 所以注定了這種方式我用不了.
第二個缺點
這個缺點很隱藏, 很容易出問題. 以上面的binding為例
HomeBinding中提供了 HomeController, Service 兩個對象
DetailsBinding中提供了 DetailsController 對象 但其實我們的Details頁中也會用到Service對象.
之所以不出現(xiàn)"details頁中說找不到Service"的crash, 是因為用戶先打開的home頁, Home已經(jīng)往Get中寫入了Service對象了, 所以等之后打開detail頁時, serivce對象已經(jīng)有了, 能夠Get.find()得到, 所以不會有NPE錯誤.
但要是deep link的場景呢?
: 你直接跳到了Detail頁, 結(jié)果就因為沒有經(jīng)過home頁, 所以Service service = Get.find()找不到service對象, 應(yīng)用會crash.
所以現(xiàn)在就明白了, 第二個缺點就是: 上面兩個Binding有隱藏的依賴性 DetailsBinding其實依賴于HomeBinding. HomeBinding不先放好service, 那DetailsBinding提供不了Serivce, 就可能會讓Detail頁crash.
第三個缺點: obs會頻繁刷新;
7. 手寫getX
3大核心功能
7.1 依賴注入
///依賴注入,外部可將實例,注入該類中,由該類管理
class Easy {
///注入實例
static T put<T>(T dependency, {String? tag}) =>
_EasyInstance().put(dependency, tag: tag);
///獲取注入的實例
static T find<T>({String? tag, String? key}) =>
_EasyInstance().find<T>(tag: tag, key: key);
///刪除實例
static bool delete<T>({String? tag, String? key}) =>
_EasyInstance().delete<T>(tag: tag, key: key);
}
///具體邏輯
class _EasyInstance {
factory _EasyInstance() => _instance ??= _EasyInstance._();
static _EasyInstance? _instance;
_EasyInstance._();
static final Map<String, _InstanceInfo> _single = {};
///注入實例
T put<T>(T dependency, {String? tag}) {
final key = _getKey(T, tag);
//只保存第一次注入:針對自動刷新機制優(yōu)化,每次熱重載的時候,數(shù)據(jù)不會重置
_single.putIfAbsent(key, () => _InstanceInfo<T>(dependency));
return find<T>(tag: tag);
}
///獲取注入的實例
T find<T>({String? tag, String? key}) {
final newKey = key ?? _getKey(T, tag);
var info = _single[newKey];
if (info?.value != null) {
return info!.value;
} else {
throw '"$T" not found. You need to call "Easy.put($T())""';
}
}
///刪除實例
bool delete<T>({String? tag, String? key}) {
final newKey = key ?? _getKey(T, tag);
if (!_single.containsKey(newKey)) {
print('Instance "$newKey" already removed.');
return false;
}
_single.remove(newKey);
print('Instance "$newKey" deleted.');
return true;
}
String _getKey(Type type, String? name) {
return name == null ? type.toString() : type.toString() + name;
}
}
class _InstanceInfo<T> {
_InstanceInfo(this.value);
T value;
}
7.2 狀態(tài)管理
///自定義個監(jiān)聽觸發(fā)類
class EasyXNotifier {
List<VoidCallback> _listeners = [];
void addListener(VoidCallback listener) {
_listeners.add(listener);
}
void removeListener(VoidCallback listener) {
for (final entry in _listeners) {
if (entry == listener) {
_listeners.remove(entry);
return;
}
}
}
void dispose() {
_listeners.clear();
}
void notify() {
if (_listeners.isEmpty) return;
for (final entry in _listeners) {
try {
entry.call();
} catch (e) {
print(e.toString());
}
}
}
}
7.3 路由管理
///刷新控件,自帶回收機制
class EasyBuilder<T extends EasyXController> extends StatefulWidget {
final Widget Function(T logic) builder;
final String? tag;
final bool autoRemove;
const EasyBuilder({
Key? key,
required this.builder,
this.autoRemove = true,
this.tag,
}) : super(key: key);
@override
_EasyBuilderState<T> createState() => _EasyBuilderState<T>();
}
class _EasyBuilderState<T extends EasyXController>
extends State<EasyBuilder<T>> {
late T controller;
@override
void initState() {
super.initState();
controller = Easy.find<T>(tag: widget.tag);
controller.xNotifier.addListener(() {
if (mounted) setState(() {});
});
}
@override
void dispose() {
if (widget.autoRemove) {
Easy.delete<T>(tag: widget.tag);
}
controller.xNotifier.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return widget.builder(controller);
}
}