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

马肤
这是懒羊羊

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(
	//函数方式创建,懒加载
    InstanceBuilderCallback 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(
  	//函数方式创建,懒加载
    InstanceBuilderCallback builder, {
    String? tag,
    bool permanent = true,
  }) {
    _insert(
      isSingleton: false,
      name: tag,
      builder: builder,
      permanent: permanent,
    );
  }  
   S put(
   //直接创建
    S dependency, {
    String? tag,
    bool permanent = false,
    @deprecated InstanceBuilderCallback? builder,
  }) {
    _insert(
        isSingleton: true,
        name: tag,
        permanent: permanent,
        builder: builder ?? (() => dependency));
    return find(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直接创建的实例,如果不存在则调用回调函数创建实例。

Flutter框架篇:GetxController原理深度解析!!!!,在这里插入图片描述,词库加载错误:未能找到文件“C:\Users\Administrator\Desktop\火车头9.8破解版\Configuration\Dict_Stopwords.txt”。,操作,没有,li,第1张

1.lazyPut 懒加载,传回调函数,只有在调用Get.find()的时候才会调用回调函数创建实例(跟随widget生命周期变化)

2.putAsync 异步实例创建,比如sharePreference.(跟随widget生命周期变化)

3.create 传入的permanent为true,持久化保存实力,不随生命周期变化

4.put 普通创建实例(跟随widget生命周期变化)

二、GetxController的实例获取与使用

  //如果是用create创建的controller则调用此方法每次都会重新创建实例
  //如果注册的是controller,则会初始化它的生命周期
  S find({String? tag}) {
  	//通过tag标记寻找已保存的controller(如果没有的话通过controller的类名寻找)
    final key = _getKey(S, tag);
    //判断是否注册过,注册过则直接获取controller实例的包装类_InstanceBuilderFactory
    if (isRegistered(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(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({String? name}) {
    final key = _getKey(S, name);
    final isInit = _singl[key]!.isInit;
    S? i;
    //第一次执行会走进来,因为isInit默认为false
    if (!isInit) {
      i = _startController(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({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()的时候执行此方法。
  @override
  @mustCallSuper
  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.
  @override
  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 {
   /// 如果要给controller做个标记的话可以这么写
   /// Get.find(tag:"myTag");
   @override
  final String tag = "myTag";
   AwesomeView({Key key}):super(key:key);
   @override
   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进行销毁

部分源码解析

@override
  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 {
  @override
  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

Flutter框架篇:GetxController原理深度解析!!!!,在这里插入图片描述,词库加载错误:未能找到文件“C:\Users\Administrator\Desktop\火车头9.8破解版\Configuration\Dict_Stopwords.txt”。,操作,没有,li,第2张

1.从使用性能看,GetBuilder是性能最好的工具,需要手动调用update()进行更新

2.obx比较常用,只有初始化变量后就可以响应式更新

3.Getx是结合了obx和Getbuilder的功能,比较消耗内存。

一 .GetBuilder原理解析

部分源码截取

@override
  void initState() {
    // _GetBuilderState._currentState = this;
    super.initState();
    //初始化GetBuilder的时候可以传入initState进行初始化操作。
    widget.initState?.call(this);
	
    var isRegistered = GetInstance().isRegistered(tag: widget.tag);
	//global默认为true,即为全局controller。可在当前widget或全局使用
    if (widget.global) {
      if (isRegistered) {
        if (GetInstance().isPrepared(tag: widget.tag)) {
          _isCreator = true;
        } else {
          _isCreator = false;
        }
        //如果注册过则通过find的方式获取controller
        controller = GetInstance().find(tag: widget.tag);
      } else {
      	//如果未注册过,则先调用传入的init初始化controller。
      	//类似下图
      	//GetBuilder(
        //	init: MusicSetController(),
        //	builder: (_) {
        controller = widget.init;
        _isCreator = true;
        //将controller放进缓存中
        GetInstance().put(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的销毁

@override
  void dispose() {
    super.dispose();
    //首先调用手动传入的dispose方法。
    widget.dispose?.call(this);
    //判断是否创建过或者assignId是否为true。则调用销毁方法。一般_isCreator初始化的时候就为true了。
    if (_isCreator! || widget.assignId) {
      if (widget.autoRemove && GetInstance().isRegistered(tag: widget.tag)) {
        GetInstance().delete(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(
      	//如果首次使用,这个必传。需要进行初始化操作
        init: MusicSetController(),
        builder: (_) {
          return Container();
        },
        //如要为了保证其执行销毁操作,可传入此参数。在widget销毁的时候,controller也会跟着销毁。
        assignId: true,
      ),

Obx使用方法。

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


文章版权声明:除非注明,否则均为VPS857原创文章,转载或复制请以超链接形式并注明出处。

发表评论

快捷回复:表情:
评论列表 (暂无评论,0人围观)

还没有评论,来说两句吧...

目录[+]

取消
微信二维码
微信二维码
支付宝二维码