GetxController原理深度解析----万字图解!!!!
getx是flutter开发中使用最频繁也最受欢迎的开发框架,要使用它必须先掌握其原理,并利用其提供的工具,达到更好的利用框架的目的,在了解原理的同时在结合设计模式去分析,不仅能学到原理,还能加强设计模式的学习
一、GetxController的不同创建方式和使用场景
1.Get.lazyPut(() => XXXController());
2.Get.putAsync(() => XXXController());
3.Get.create(() => XXXController());
4.Get.put(() => XXXController());
原理解析及使用场景
//延迟初始化,只有在调用Get.find()的时候才会创建controller
void lazyPut<S>(
//函数方式创建,懒加载
InstanceBuilderCallback<S> builder, {
//对builder内容进行标记
String? tag,
//是否持久化保存controller
bool? fenix,
bool permanent = false,
}) {
_insert(
isSingleton: true,
name: tag,
permanent: permanent,
builder: builder,
fenix: fenix ?? Get.smartManagement == SmartManagement.keepFactory,
);
}
void create<S>(
//函数方式创建,懒加载
InstanceBuilderCallback<S> builder, {
String? tag,
bool permanent = true,
}) {
_insert(
isSingleton: false,
name: tag,
builder: builder,
permanent: permanent,
);
}
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);
}
1.tag(可选):如果想要创建相同类型的controller(由于controller只会创建一次,就像单例,
如果想创建多个,则用tag做标记)
2.permanent(可选):默认情况下,get会在实例不再使用后销毁(例如,一个已经销毁的视图controller),如果需要实例
在整个生命周期中都存在,则设为true。
3.fenix(可选):下次使用时是否重建,当不使用时,实例会被丢弃,但当再次需要使用时,Get会重新创建实例
就像 bindings api 中的 "SmartManagement.keepFactory "一样。
4.isSingleton(不可选):是否是单例形式存储。如果为true则直接调用builder回调,false则先使用dependency直接创建的实例,如果不存在则调用回调函数创建实例。
1.lazyPut 懒加载,传回调函数,只有在调用Get.find()的时候才会调用回调函数创建实例(跟随widget生命周期变化)
2.putAsync 异步实例创建,比如sharePreference.(跟随widget生命周期变化)
3.create 传入的permanent为true,持久化保存实力,不随生命周期变化
4.put 普通创建实例(跟随widget生命周期变化)
二、GetxController的实例获取与使用
//如果是用create创建的controller则调用此方法每次都会重新创建实例
//如果注册的是controller,则会初始化它的生命周期
S find<S>({String? tag}) {
//通过tag标记寻找已保存的controller(如果没有的话通过controller的类名寻找)
final key = _getKey(S, tag);
//判断是否注册过,注册过则直接获取controller实例的包装类_InstanceBuilderFactory
if (isRegistered<S>(tag: tag)) {
final dep = _singl[key];
if (dep == 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 ?? dep.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())"';
}
}
上面的代码比较简单,就是从容器中获取controller实例,我们重点看看_initDependencies(name: tag);
S? _initDependencies<S>({String? name}) {
final key = _getKey(S, name);
final isInit = _singl[key]!.isInit;
S? i;
//第一次执行会走进来,因为isInit默认为false
if (!isInit) {
i = _startController<S>(tag: name);
if (_singl[key]!.isSingleton!) {
_singl[key]!.isInit = true;
if (Get.smartManagement != SmartManagement.onlyBuilder) {
RouterReportManager.reportDependencyLinkedToRoute(_getKey(S, name));
}
}
}
return i;
}
/// 真正的初始化controller的逻辑
S _startController<S>({String? tag}) {
final key = _getKey(S, tag);
final i = _singl[key]!.getDependency() as S;
if (i is GetLifeCycleBase) {
//开始执行controller的生命周期
//oninit --- 并同时监听onReady执行(界面布局完成后)
i.onStart();
if (tag == null) {
Get.log('Instance "$S" has been initialized');
} else {
Get.log('Instance "$S" with tag "$tag" has been initialized');
}
if (!_singl[key]!.isSingleton!) {
RouterReportManager.appendRouteByCreate(i);
}
}
return i;
}
// 说明只执行一次onInit();初始化
void _onStart() {
if (_initialized) return;
onInit();
_initialized = true;
}
上面的代码只有一个逻辑,就是执行controller的生命周期函数
abstract class DisposableInterface extends GetLifeCycle {
//当调用Get.find()的时候执行此方法。
void onInit() {
super.onInit();
//这里开始监听当前页面的vsync信号帧绘制,绘制完成后回调onReady()方法
Get.engine.addPostFrameCallback((_) => onReady());
}
/// Called 1 frame after onInit(). It is the perfect place to enter
/// navigation events, like snackbar, dialogs, or a new route, or
/// async request.
void onReady() {
//这里需要客户端进行重写
super.onReady();
}
1.onInit只会执行一次,调用find的时候会首先调用生命周期onInit方法
2.根据vsync信号刷新回调onReady生命周期方法。即界面构建完成之后调用
总结:只有controller在当前页面使用Get.find()的时候。首先会执行onInit方法。如果在widget中调用,同时还会监听页面绘制完成并调用onReady方法。
注意:如果只是通过Get.find()的方式获取controller,并不会调用controller的delete()方法清空内存。需要手动调用Get.delete()方法从内存中清除controller。
三、GetxController的销毁时机
1.在widget的dispose()中手动调用Get.delete()清除controller
2.绑定widget生命周期,跟随widget生命周期清除
getx提供了多个widget供开发者使用。其中有下列常用的几种
负责管理controller的widget
1.GetView(具有自动获取controller的功能)
class AwesomeController extends GetxController {
final String title = 'My Awesome View';
}
class AwesomeView extends GetView<AwesomeController> {
/// 如果要给controller做个标记的话可以这么写
/// Get.find<AwesomeController>(tag:"myTag");
final String tag = "myTag";
AwesomeView({Key key}):super(key:key);
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20),
child: Text( controller.title ),
);
}
}
2.GetWidget(缓存controller)
内部跟着缓存走,须使用Get.create(()=>Controller());(因为getWidget适用于保存同一类controller的不同实例。),所以每次都需要调用创建方法。在其从内存中退出时,如widget从数中卸载时,会自动调用onclose对controller进行销毁
部分源码解析
void onClose() {
if (_isCreator) {
//异步调用controller的销毁逻辑
Get.asap(() {
widget!.controller!.onDelete();
Get.log('"${widget!.controller.runtimeType}" onClose() called');
Get.log('"${widget!.controller.runtimeType}" deleted from memory');
GetWidget._cache[widget!] = null;
});
}
info = null;
super.onClose();
}
在widget即将销毁时,会判断是否是通过create创建的controller,如果是则执行销毁缓存controller的方法。
以下是使用方式
class GetViewAndGetWidgetExample extends GetWidget<GetViewCountController> {
Widget build(BuildContext context) {
Get.create(() => GetViewCountController());
return Scaffold(
appBar: AppBar(
title: Text("GetX GetView"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Obx(() => Text(
"count的值为: ${controller?.count}",
style: TextStyle(
color: Colors.red,
fontSize: 30
),
)),
SizedBox(height: 20,),
ElevatedButton(
onPressed: () {
controller.increment();
},
child: Text("点我加1"))
],
),
),
);
}
}
getwidget用的比较少,一般都是用的getview
2.GetResponsiveView (平板,桌面等适配)
负责状态管理的widget(StatefulWidget类,具有状态管理功能)
1.GetBuilder(相比getx多了id和filter的变量)
2.GetX
3.Obx
1.从使用性能看,GetBuilder是性能最好的工具,需要手动调用update()进行更新
2.obx比较常用,只有初始化变量后就可以响应式更新
3.Getx是结合了obx和Getbuilder的功能,比较消耗内存。
一 .GetBuilder原理解析
部分源码截取
void initState() {
// _GetBuilderState._currentState = this;
super.initState();
//初始化GetBuilder的时候可以传入initState进行初始化操作。
widget.initState?.call(this);
var isRegistered = GetInstance().isRegistered<T>(tag: widget.tag);
//global默认为true,即为全局controller。可在当前widget或全局使用
if (widget.global) {
if (isRegistered) {
if (GetInstance().isPrepared<T>(tag: widget.tag)) {
_isCreator = true;
} else {
_isCreator = false;
}
//如果注册过则通过find的方式获取controller
controller = GetInstance().find<T>(tag: widget.tag);
} else {
//如果未注册过,则先调用传入的init初始化controller。
//类似下图
//GetBuilder<MusicSetController>(
// init: MusicSetController(),
// builder: (_) {
controller = widget.init;
_isCreator = true;
//将controller放进缓存中
GetInstance().put<T>(controller!, tag: widget.tag);
}
} else {
//如果非全局,则直接初始化并调用controller的onstart方法。
controller = widget.init;
_isCreator = true;
controller?.onStart();
}
if (widget.filter != null) {
_filter = widget.filter!(controller!);
}
//往下看
_subscribeToController();
}
//注册监听器,setstate((){});getUpdate就是setstate监听器。这里会将监听方法存起来,在手动调用
//refresh()或refreshGroup(id)的时候会调用监听器刷新,即setstate.
void _subscribeToController() {
_remove?.call();
_remove = (widget.id == null)
? controller?.addListener(
_filter != null ? _filterUpdate : getUpdate,
)
: controller?.addListenerId(
widget.id,
_filter != null ? _filterUpdate : getUpdate,
);
}
1.getBiulder首先会先调用传入的initState初始化函数(如果传入的话)。
2.判断controller是否存在,如果存在则从缓存中获取,否则重新创建一个(从传入的init创建)。
3.注册监听器。只有在手动调用refresh或refreshGroup的时候会执行GetBuider的setstate方法进行刷新。
GetBuilder的销毁
void dispose() {
super.dispose();
//首先调用手动传入的dispose方法。
widget.dispose?.call(this);
//判断是否创建过或者assignId是否为true。则调用销毁方法。一般_isCreator初始化的时候就为true了。
if (_isCreator! || widget.assignId) {
if (widget.autoRemove && GetInstance().isRegistered<T>(tag: widget.tag)) {
GetInstance().delete<T>(tag: widget.tag);
}
}
_remove?.call();
controller = null;
_isCreator = null;
_remove = null;
_filter = null;
}
GetBuilder使用方法
Widget build(BuildContext context) {
// TODO: implement build
return Material(
child: GetBuilder<MusicSetController>(
//如果首次使用,这个必传。需要进行初始化操作
init: MusicSetController(),
builder: (_) {
return Container();
},
//如要为了保证其执行销毁操作,可传入此参数。在widget销毁的时候,controller也会跟着销毁。
assignId: true,
),
Obx使用方法。
Widget build(BuildContext context) {
RxInt count = 0.obx;
return Scaffold(
appBar: AppBar(
title: Text("GetX GetView"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Obx(() => Text(
"count的值为: ${count.value}",
style: TextStyle(
color: Colors.red,
fontSize: 30
),
)),
SizedBox(height: 20,),
ElevatedButton(
onPressed: () {
count ++;
},
child: Text("点我加1"))
],
),
),
);
}
1.obx调用比较简单,只需要包裹一个使用count变量的widget
2.同时将count变量初始化成RxInt类型
3.使用变量的时候通过count.value调用即可。
4.如果count变量发生变化,则只刷新obx的widget
obx的原理比较复杂,详细看下一章节。