目錄:
1.優(yōu)點(diǎn)
2.優(yōu)點(diǎn)分析: GetX怎么將邏輯層和界面層解耦的
3.優(yōu)點(diǎn)分析: GetX怎么實(shí)現(xiàn)狀態(tài)管理的? Obx的基本原理是什么献幔? 局部刷新原理? obx和obs?
4.優(yōu)點(diǎn)分析: binding基本原理是什么操刀?
5.優(yōu)點(diǎn)分析: 路由管理基本原理是什么?
6.getx的缺點(diǎn)是啥?
7.手寫getX
1. GetX相關(guān)優(yōu)勢(shì)
1.1 )依賴注入
GetX是通過(guò)依賴注入的方式,存儲(chǔ)相應(yīng)的XxxGetxController似扔;已經(jīng)脫離了InheritedWidget那一套玩法酿愧,自己手動(dòng)去管理這些實(shí)例屯换,使用場(chǎng)景被大大拓展
簡(jiǎn)單的思路驻仅,卻能產(chǎn)生深遠(yuǎn)的影響:優(yōu)雅的跨頁(yè)面功能便是基于這種設(shè)計(jì)而實(shí)現(xiàn)的、獲取實(shí)例無(wú)需BuildContext摆舟、GetBuilder自動(dòng)化的處理及其減少了入?yún)⒌鹊?/p>
1.2 )跨頁(yè)面交互的狀態(tài)管理
這絕對(duì)是GetX的一個(gè)優(yōu)點(diǎn)亥曹!對(duì)于復(fù)雜的生產(chǎn)環(huán)境,跨頁(yè)面交互的場(chǎng)景恨诱,實(shí)在太常見(jiàn)了媳瞪,GetX的跨頁(yè)面交互,實(shí)現(xiàn)的也較為優(yōu)雅
1.3 )路由管理
getx內(nèi)部實(shí)現(xiàn)了路由管理照宝,而且用起來(lái)蛇受,非常簡(jiǎn)單!bloc沒(méi)實(shí)現(xiàn)路由管理厕鹃,我不得不找一個(gè)star量高的路由框架兢仰,就選擇了fluro乍丈,但是不得不吐槽下,fluro用起來(lái)真的很折磨人把将,每次新建一個(gè)頁(yè)面轻专,最讓我抗拒的就是去寫fluro路由代碼,橫跨幾個(gè)文件來(lái)回寫秸弛,頭皮發(fā)麻
GetX實(shí)現(xiàn)了動(dòng)態(tài)路由傳參,也就是說(shuō)直接在命名路由上拼參數(shù)洪碳,然后能拿到這些拼在路由上的參數(shù)递览,也就是說(shuō)用flutter寫H5,直接能通過(guò)Url傳值瞳腌,OMG绞铃!可以無(wú)腦舍棄復(fù)雜的fluro了
1.4 ) 實(shí)現(xiàn)了全局BuildContext
1.5 )國(guó)際化,主題實(shí)現(xiàn)
生命周期
用了Getx的state管理之后, 你再也用不著StatefulWidget了. 僅僅StatelessWidget就夠你用了! 性能自然也提升很多!
2. GetX怎么將邏輯層和界面層解耦的
此處需要?jiǎng)澐秩齻€(gè)結(jié)構(gòu)了:state(狀態(tài)層)嫂侍,logic(邏輯層)儿捧,view(界面層)
為什么寫成這樣三個(gè)模塊,需要把State單獨(dú)提出來(lái)挑宠,為了復(fù)雜的業(yè)務(wù), 顯的更簡(jiǎn)單!
舉例:
之前的寫法
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的實(shí)現(xiàn)
新建一個(gè)類, 定義一個(gè)狀態(tài), GetxController
添加一個(gè)Obx就能自動(dòng)監(jiān)聽(tīng)狀態(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實(shí)現(xiàn)狀態(tài)管理
GetX 的刷新方案分為手動(dòng)刷新與自動(dòng)刷新菲盾,GetBuilder 與 Obx ,分別對(duì)應(yīng)范圍刷新與局部刷新
2者的區(qū)別:
Obx 響應(yīng)式狀態(tài)管理
Obx 可以配合響應(yīng)式字段局部的精準(zhǔn)刷新避免父容器無(wú)效重構(gòu)各淀,缺點(diǎn)是字段變?yōu)轫憫?yīng)式的Rx包裝類懒鉴,布局也需要被Obx包裹了,破壞了原生代碼觀賞性碎浇。
GetBuilder 狀態(tài)管理器
GetBuilder 就是指定區(qū)域范圍手動(dòng)去刷新的临谱,可以分區(qū)設(shè)置多個(gè)刷新區(qū)域,可選擇單個(gè)控件或容器奴璃,在一些特定場(chǎng)景下有奇效悉默,但是如果不理解濫用一樣會(huì)導(dǎo)致性能問(wèn)題。
3.1 Obx 的基本原理是什么苟穆?
Obx是配合Rx響應(yīng)式變量使用
這樣一來(lái)我們就明白了Obx實(shí)際上是一個(gè)StatefulWidget抄课,它里面監(jiān)聽(tīng)了一個(gè)GetStream,一旦GetStream有事件通知雳旅,它就會(huì)進(jìn)行setState重新進(jìn)行Widget的構(gòu)造.
GetBuilder 與 Obx 兩者結(jié)合剖膳,一個(gè)指定區(qū)域范圍手動(dòng)刷新,一個(gè)是局部控件點(diǎn)對(duì)點(diǎn)刷新
var build = () => Text(name.value)
Obx(build);
源碼: Obx繼承了一個(gè)抽象ObxWidget類岭辣,將傳遞進(jìn)來(lái)的build方法給了ObxWidget
class Obx extends ObxWidget {
final WidgetCallback builder;
const Obx(this.builder);
@override
Widget build() => builder();
}
ObxWidget繼承了有狀態(tài)組件吱晒,并且build函數(shù)讓Obx類實(shí)現(xiàn)了
abstract class ObxWidget extends StatefulWidget {
const ObxWidget({Key? key}) : super(key: key);
@override
_ObxState createState() => _ObxState();
@protected
Widget build();
}
對(duì)GexX的狀態(tài)管理做一個(gè)簡(jiǎn)單總結(jié),
基于Obx收集依賴狀態(tài), 實(shí)際一個(gè)StatefulWidget沦童,它的State也就是ObxState中監(jiān)聽(tīng)了GetStream事件流仑濒,通過(guò)接收GetStream事件流調(diào)用setState重新構(gòu)建Obx叹话,Rx對(duì)象在改變value的時(shí)候會(huì)向GetStream事件流發(fā)送事件,這樣就會(huì)導(dǎo)致Obx進(jìn)行刷新了.
3.2 GetBuilder
GetBuilder 是一個(gè) Widget 組件墩瞳, 在 GetX 的狀態(tài)管理中驼壶,GetBuilder 的主要作用是結(jié)合 GetxController 實(shí)現(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ù),從而實(shí)現(xiàn)計(jì)數(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ū)域范圍手動(dòng)去刷新的热凹,可以分區(qū)設(shè)置多個(gè)刷新區(qū)域,可選擇單個(gè)控件或容器泪电,在一些特定場(chǎng)景下有奇效般妙,但是如果不理解濫用一樣會(huì)導(dǎo)致性能問(wèn)題。
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!);
}
通過(guò)對(duì) GetBuilder 的源碼分析相速,基本了解了 GetBuilder 各個(gè)參數(shù)的作用和實(shí)現(xiàn)原理碟渺。
GetBuilder 參數(shù)作用總結(jié)如下:
builder: Widget 構(gòu)建器,創(chuàng)建界面顯示的 Widget
init: 初始化 Controller 值突诬,當(dāng) global 為 false 時(shí)使用該值作為 Controller苫拍,當(dāng) global 為 true 時(shí)且 Controller 未注冊(cè)依賴,則將 init 的值注入依賴使用旺隙。
global: 是否全局绒极,作用于 Controller 初始化中,與 init 結(jié)合使用
autoRemove: 是否自動(dòng)移除 Controller 依賴蔬捷,結(jié)合 assignId 一起使用
assignId: 為 true 時(shí)結(jié)合 autoRemove 使用會(huì)自動(dòng)移除 Controller 依賴關(guān)系
filter: 過(guò)濾器集峦,通過(guò)返回值過(guò)濾是否需要刷新,返回值變化時(shí)才會(huì)刷新界面
tag: Controller 依賴注入的 tag抠刺,根據(jù) tag 獲取 Controller 實(shí)例
id: 刷新標(biāo)識(shí)塔淤,結(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路由頁(yè)面進(jìn)行綁定备恤;進(jìn)入頁(yè)面的時(shí)候,統(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. 路由管理之命名路由
特點(diǎn):封裝了context, 封裝了攔截器!
路由管理之簡(jiǎn)單路由
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())
],
)
問(wèn)題: 為何不用Flutter自己的Router系統(tǒng)?
使用時(shí)還需要有一個(gè)context實(shí)例. 但我們并不是隨時(shí)隨地都持有一個(gè)context的, 這也局限了我們的使用場(chǎng)景.
6. GETX的缺點(diǎn):
第一個(gè)缺點(diǎn)
Get.to(widgetObj, bindings)是可以注入binding.
但是Get.toNamed()并不支持binding參數(shù)啊. 我的跳轉(zhuǎn)一般都是用toNamed的, 所以注定了這種方式我用不了.
第二個(gè)缺點(diǎn)
這個(gè)缺點(diǎn)很隱藏, 很容易出問(wèn)題. 以上面的binding為例
HomeBinding中提供了 HomeController, Service 兩個(gè)對(duì)象
DetailsBinding中提供了 DetailsController 對(duì)象 但其實(shí)我們的Details頁(yè)中也會(huì)用到Service對(duì)象.
之所以不出現(xiàn)"details頁(yè)中說(shuō)找不到Service"的crash, 是因?yàn)橛脩粝却蜷_(kāi)的home頁(yè), Home已經(jīng)往Get中寫入了Service對(duì)象了, 所以等之后打開(kāi)detail頁(yè)時(shí), serivce對(duì)象已經(jīng)有了, 能夠Get.find()得到, 所以不會(huì)有NPE錯(cuò)誤.
但要是deep link的場(chǎng)景呢?
: 你直接跳到了Detail頁(yè), 結(jié)果就因?yàn)闆](méi)有經(jīng)過(guò)home頁(yè), 所以Service service = Get.find()找不到service對(duì)象, 應(yīng)用會(huì)crash.
所以現(xiàn)在就明白了, 第二個(gè)缺點(diǎn)就是: 上面兩個(gè)Binding有隱藏的依賴性 DetailsBinding其實(shí)依賴于HomeBinding. HomeBinding不先放好service, 那DetailsBinding提供不了Serivce, 就可能會(huì)讓Detail頁(yè)crash.
第三個(gè)缺點(diǎn): obs會(huì)頻繁刷新锦秒;
7. 手寫getX
3大核心功能
7.1 依賴注入
///依賴注入露泊,外部可將實(shí)例,注入該類中旅择,由該類管理
class Easy {
///注入實(shí)例
static T put<T>(T dependency, {String? tag}) =>
_EasyInstance().put(dependency, tag: tag);
///獲取注入的實(shí)例
static T find<T>({String? tag, String? key}) =>
_EasyInstance().find<T>(tag: tag, key: key);
///刪除實(shí)例
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 = {};
///注入實(shí)例
T put<T>(T dependency, {String? tag}) {
final key = _getKey(T, tag);
//只保存第一次注入:針對(duì)自動(dòng)刷新機(jī)制優(yōu)化惭笑,每次熱重載的時(shí)候,數(shù)據(jù)不會(huì)重置
_single.putIfAbsent(key, () => _InstanceInfo<T>(dependency));
return find<T>(tag: tag);
}
///獲取注入的實(shí)例
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())""';
}
}
///刪除實(shí)例
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)管理
///自定義個(gè)監(jiān)聽(tīng)觸發(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 路由管理
///刷新控件,自帶回收機(jī)制
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);
}
}