Flutter框架篇:GetxController原理深度解析!!!!

avatar
作者
猴君
阅读量:0

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的原理比较复杂,详细看下一章节。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!