问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

Flutter状态管理GetX使用详解

发布网友 发布时间:2024-10-01 09:03

我来回答

1个回答

热心网友 时间:2024-10-29 06:36

如题,GetX包含很多功能,各种弹出widget、路由管理、国际化、Utils、状态管理等。本文只针对核心功能——状态管理的基础使用部分(暂不解析原理)。从浅入深、全面地做出详细介绍,笔者也是因为之前使用GetX,看过一些文档和blog。这篇文章笔者是从小白的视角来完成的,认真看完肯定使用完全没有问题。虽然笔者已经尽量直白、尽量简单,但是因为文章基本上包含了GetX状态管理的大部分内容,再加上大量的示例代码(多数都是计数器的重复性代码),可能篇幅有点长。

一、声明响应式变量及简单使用

有3种声明方式:

1、使用Rx{Type}

//建议使用初始值,但不是强制性的finalname=RxString('');finalisLogged=RxBool(false);finalcount=RxInt(0);finalbalance=RxDouble(0.0);finalitems=RxList<String>([]);finalmyMap=RxMap<String,int>({});

2、使用Rx,规定泛型Rx

finalname=Rx<String>('');finalisLogged=Rx<Bool>(false);finalcount=Rx<Int>(0);finalbalance=Rx<Double>(0.0);finalnumber=Rx<Num>(0)finalitems=Rx<List<String>>([]);finalmyMap=Rx<Map<String,int>>({});//自定义类-可以是任何类finaluser=Rx<User>();

3、这种更实用、更简单、更可取的方法,只需添加.obs作为value的属性。(推荐使用)

finalname=''.obs;finalisLogged=false.obs;finalcount=0.obs;finalbalance=0.0.obs;finalnumber=0.obs;finalitems=<String>[].obs;finalmyMap=<String,int>{}.obs;//自定义类-可以是任何类finaluser=User().obs;

demo使用,以计数器为例:

入口设置:推荐使用GetMaterialApp(后续示例皆如此)

classMyAppextendsStatelessWidget{constMyApp({Key?key}):super(key:key);@overrideWidgetbuild(BuildContextcontext){///这里使用GetMaterialAppreturnGetMaterialApp(home:Demo1(),);}}

计数器

classDemo1extendsStatelessWidget{Demo1({Key?key}):super(key:key);///3种方式声明变量//RxIntcount=RxInt(0);//varcount=Rx<int>(0);varcount=0.obs;@overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(title:constText("GetX"),),body:Center(child:Column(mainAxisAlignment:MainAxisAlignment.center,children:[//用Obx包装需要使用变量的widgetObx(()=>Text("count的值为:$count",style:constTextStyle(color:Colors.redAccent,fontSize:20),)),constSizedBox(height:30,),ElevatedButton(//按钮点击count值++onPressed:()=>count++,child:constText("点击count++"),)],),),);}}

注意:此时count的类型是RxInt,不是int。可以通过count.value来获取其本身的int值

当我们查看源码的时候,可以发现调用.obs的时候,内部还是通过RxInt<e>(this)进行了一层包装,所以.obs就是为了方便开发者使用的,推荐使用这种方式声明变量。

自定义类的使用

新建一个People类

classPeople{//第一种:直接声明变量//varname="xiaoMing".obs;//varage=18.obs;//第二种:构造函数varname;varage;People({this.name,this.age});}

第一种使用:

//声明varpeople=People();//使用Obx(()=>Text("名字:${people.name.value},年龄:${people.age.value}",style:constTextStyle(color:Colors.redAccent,fontSize:20),)),//改变状态onPressed:(){people.name.value="xiaoLi";people.age.value=15;},

第二种使用:

//声明finalpeople=People(name:"xiaoMing",age:18).obs;//使用Obx(()=>Text("名字:${people.value.name},年龄:${people.value.age}",style:constTextStyle(color:Colors.redAccent,fontSize:20),)),//改变状态onPressed:(){people.value.name="xiaoLi";people.value.age=15;},二、GetxController

在实际项目开发中,我们一般不会像上述那样把UI代码、业务逻辑都放在一起处理,这样对项目的架构、代码的可读性、后期的优化和维护将会是致命的。GetX也为我们提供了解决方案:GetxController。

GetxController提供了三种使用方式:

Obx:响应式状态管理,当数据源变化时,将自动执行刷新组件的方法

GetX:响应式状态管理,当数据源变化时,将自动执行刷新组件的方法

GetBuilder:简单状态管理,当数据源变化时,需要手动执行刷新组件的方法,此状态管理器内部实际上是对StatefulWidget的封装,占用资源极少!

使用场景:

一般来说,对于大多数场景都是可以使用响应式变量。但是每个响应式变量(.obs),都需要生成对应的GetStream,如果对象足够多,将生成大量的GetStream,必将对内存造成较大的压力,该情况下,就要考虑使用简单状态管理了。

响应式状态管理

逻辑层

继承于GetxController的类,处理页面逻辑的,

classCounterControllerextendsGetxController{///定义了该变量为响应式变量,当该变量数值变化时,页面的刷新方法将自动刷新varcount=0.obs;///自增方法voidincrease()=>++count;}

view层

classDemo2extendsStatelessWidget{constDemo2({Key?key}):super(key:key);@overrideWidgetbuild(BuildContextcontext){///通过依赖注入方式实例化的控制器finalcounter=Get.put(CounterController());returnScaffold(appBar:AppBar(title:constText("GetX"),),body:Center(child:Column(mainAxisAlignment:MainAxisAlignment.center,children:[Obx(()=>Text("count的值为:${counter.count}",style:constTextStyle(color:Colors.redAccent,fontSize:20),)),/*GetX<CounterController>(init:counter,builder:(controller){returnText("count的值为:${controller.count}",style:constTextStyle(color:Colors.redAccent,fontSize:20),);},),*/constSizedBox(height:30,),ElevatedButton(//按钮点击count值++onPressed:()=>counter.increase(),child:constText("点击count++"),),],),),);}}

注意:

响应式状态管理器只有当响应式变量的值发生变化时,才会会执行刷新操作,如当变量从“a”再变为“a”,是不会执行刷新操作。

finalcounter=Get.put(CounterController());通过依赖注入方式实例化的控制器,不是Controllercontroller=Controller(),也不是在正在使用的类中实例化的类,所有它可以在整个App中使用,前提是没有销毁。换句话说使用Get.put()实例化类,使用对当下所有子路由可用。后续也可以通过Get.find()找到对应的GetxController。

Get.lazyPut():懒加载一个依赖,只有在使用时才会被实例化。其他功能同Get.put()。

Get.putAsync():注册一个异步的依赖,例如:`

Get.putAsync(()async{finalprefs=awaitSharedPreferences.getInstance();awaitprefs.setInt('counter',12345);returnprefs;});`

不管是Get.put()还是Get.lazyPut(),方法内部还有几个参数,因为不常用,这里不做说明。有兴趣者可以自行查阅文档了解。

大家可以看到,笔者的CounterController实例化是写在build中的,因为stl是无状态组件,不会被二次或多次重载,所以实例操作只会被执行一次。而且在使用PageView时,如果注入操作写在类中,那所有PageView页面控制器会全被初始化,而不是切换到某个页面时,对应页面的控制器才被初始化!所以在使用PageView时,注入操作须写在build方法中。

简单状态管理

逻辑层

finalname=Rx<String>('');finalisLogged=Rx<Bool>(false);finalcount=Rx<Int>(0);finalbalance=Rx<Double>(0.0);finalnumber=Rx<Num>(0)finalitems=Rx<List<String>>([]);finalmyMap=Rx<Map<String,int>>({});//自定义类-可以是任何类finaluser=Rx<User>();0

view层

finalname=Rx<String>('');finalisLogged=Rx<Bool>(false);finalcount=Rx<Int>(0);finalbalance=Rx<Double>(0.0);finalnumber=Rx<Num>(0)finalitems=Rx<List<String>>([]);finalmyMap=Rx<Map<String,int>>({});//自定义类-可以是任何类finaluser=Rx<User>();1

注意:

参数init:因为在加载变量的时候就使用Get.put()生成了CounterController对象,GetBuilder会自动查找该对象,所以,就可以不使用init参数。

GetBuilder拥有StatefulWidget所有周期回调,如:initState、dispose可以在相应回调内做一些操作。但是比这更好的方法是直接从控制器中使用onInit()和onClose()方法。

指定id刷新

多个GetBuilder使用同一个CounterController的变量,但是我们只想更新其中一个GetBuilder的变量,就可以在添加id参数

逻辑层

finalname=Rx<String>('');finalisLogged=Rx<Bool>(false);finalcount=Rx<Int>(0);finalbalance=Rx<Double>(0.0);finalnumber=Rx<Num>(0)finalitems=Rx<List<String>>([]);finalmyMap=Rx<Map<String,int>>({});//自定义类-可以是任何类finaluser=Rx<User>();2

view层

finalname=Rx<String>('');finalisLogged=Rx<Bool>(false);finalcount=Rx<Int>(0);finalbalance=Rx<Double>(0.0);finalnumber=Rx<Num>(0)finalitems=Rx<List<String>>([]);finalmyMap=Rx<Map<String,int>>({});//自定义类-可以是任何类finaluser=Rx<User>();3跨页面交互

场景:OnePagepush到TwoPage,在TwoPage操作计数器后,返回,在OnePage显示TwoPage点击的次数。

页面一:

逻辑层:

finalname=Rx<String>('');finalisLogged=Rx<Bool>(false);finalcount=Rx<Int>(0);finalbalance=Rx<Double>(0.0);finalnumber=Rx<Num>(0)finalitems=Rx<List<String>>([]);finalmyMap=Rx<Map<String,int>>({});//自定义类-可以是任何类finaluser=Rx<User>();4

view层:

finalname=Rx<String>('');finalisLogged=Rx<Bool>(false);finalcount=Rx<Int>(0);finalbalance=Rx<Double>(0.0);finalnumber=Rx<Num>(0)finalitems=Rx<List<String>>([]);finalmyMap=Rx<Map<String,int>>({});//自定义类-可以是任何类finaluser=Rx<User>();5页面二:

逻辑层:

finalname=Rx<String>('');finalisLogged=Rx<Bool>(false);finalcount=Rx<Int>(0);finalbalance=Rx<Double>(0.0);finalnumber=Rx<Num>(0)finalitems=Rx<List<String>>([]);finalmyMap=Rx<Map<String,int>>({});//自定义类-可以是任何类finaluser=Rx<User>();6

注意:

GetxController包含比较完整的生命周期回调,可以在onInit()接受传递的数据;如果接收的数据需要刷新到界面上,请在onReady()回调里面接收数据操作,onReady()是在addPostFrameCallback回调中调用,刷新数据的操作在onReady()进行,能保证界面是初始加载完毕后才进行页面刷新操作。

view层:

finalname=Rx<String>('');finalisLogged=Rx<Bool>(false);finalcount=Rx<Int>(0);finalbalance=Rx<Double>(0.0);finalnumber=Rx<Num>(0)finalitems=Rx<List<String>>([]);finalmyMap=Rx<Map<String,int>>({});//自定义类-可以是任何类finaluser=Rx<User>();7

通过Get.find(),获取到了之前实例化OneController,再操作相应的事件。

架构优化

上述都是将所有的状态变量和操作放在同一个地方,但是在复杂的业务场景下,这就显得很冗余,不利于后期维护和优化。于是我们会划分三个结构:state(状态层),logic(逻辑层),view(界面层)。

计数器改造:

state层

统一管理所有的状态变量

finalname=Rx<String>('');finalisLogged=Rx<Bool>(false);finalcount=Rx<Int>(0);finalbalance=Rx<Double>(0.0);finalnumber=Rx<Num>(0)finalitems=Rx<List<String>>([]);finalmyMap=Rx<Map<String,int>>({});//自定义类-可以是任何类finaluser=Rx<User>();8

logic层

实例化状态类,以便操作所有的变量

finalname=Rx<String>('');finalisLogged=Rx<Bool>(false);finalcount=Rx<Int>(0);finalbalance=Rx<Double>(0.0);finalnumber=Rx<Num>(0)finalitems=Rx<List<String>>([]);finalmyMap=Rx<Map<String,int>>({});//自定义类-可以是任何类finaluser=Rx<User>();9

view层

finalname=''.obs;finalisLogged=false.obs;finalcount=0.obs;finalbalance=0.0.obs;finalnumber=0.obs;finalitems=<String>[].obs;finalmyMap=<String,int>{}.obs;//自定义类-可以是任何类finaluser=User().obs;0

上述重构了之后,代码结构就很清晰明了。

state只专注数据,需要使用数据,直接通过state获取

logic只专注于触发事件交互,操作或更新数据

view只专注UI显示

GetxController事件监听:Workers

Workers将协助你在事件发生时触发特定的回调。

finalname=''.obs;finalisLogged=false.obs;finalcount=0.obs;finalbalance=0.0.obs;finalnumber=0.obs;finalitems=<String>[].obs;finalmyMap=<String,int>{}.obs;//自定义类-可以是任何类finaluser=User().obs;1

注意:

Worker应该总是在启动Controller或Class时使用,所以应该总是在onInit(推荐)、Class构造函数或StatefulWidget的initState(大多数情况下不推荐这种做法,但应该不会有任何副作用)。

GetxController生命周期finalname=''.obs;finalisLogged=false.obs;finalcount=0.obs;finalbalance=0.0.obs;finalnumber=0.obs;finalitems=<String>[].obs;finalmyMap=<String,int>{}.obs;//自定义类-可以是任何类finaluser=User().obs;2三、Binding的使用

在使用GetX的时候,往往每次都是用需要手动实例化一个控制器finalcontroller=Get.put(CounterController());,如果每个界面都要实例化一次,有些许麻烦。使用Binding能解决上述问题,可以在项目初始化时把所有需要进行状态管理的控制器进行统一初始化,直接使用Get.find()找到对应的GetxController使用。

可以将路由、状态管理器和依赖管理器完全集成

这里介绍三种使用方式,推

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
做青红椒炒毛肚有哪些好吃的诀窍? 如何自制好吃的香辣毛肚? 穿越火线警告码SX(2,509,0)如何解决 CF sx警告码(2,770,0)什么原因 ...要求重新启动电脑 警告码是2 xxxx 0 有时重启4 5次才能玩 win7系统... 穿越火线 sx 警告码 2,994,0 怎么解决 CF出现非法模块SX警告码(2,990,0) 我为什么上CF体验服没开G有非法模块???怎么办啊 警告码(2,502,0) 穿越火线进游戏后 2分钟左右 就出来个 SX 警告码2,990,0 看到刘老师那慈祥的面庞,我感慨万分,我想到了这样几个赞美刘老师的成语... 谁能帮修改一下下面的计数代码防止刷新,最好直接加进里面去! 自己使用的一套ASP网页计数器代码每次刷新都会增加访问数,帮忙看看加 ... 怎么用手机识别明星脸? 手机怎么识别图片中的文字内容 我换了台电脑,人家说同步的话原手机里面的歌曲和图片会消失,想知道怎么... 快手举报几次就会封号吗? 新浪微博修正粉丝功能使用教程_微博修正粉丝什么意思 考注会英语要什么条件 如何修正微博的粉丝数? visualstudio的版本有什么区别? 婆婆47岁了给它买个什么颜色的包比较好 微信为什么老显示系统版本低 lg^(-1)是什么意思啊,好久没有念数学了 耳朵又小又薄又没耳垂的女人财运要有借力点? lg(-1)是什么意思? 得意扬扬得意洋洋区别 6个黑科技网站,永久免费 7个冷门但逆天的黑科技神器 梦到路边的土岸很高什么意思? 昨天晚上梦到和我很好的朋友那个 我们都是女的,她是T。为什么我偶尔会想... 我用php做了一个计数器,只要我刷新它就加1,怎么才能不加1 请写个代码... 淘宝计数器看不懂 万里长城的资料简介 关于长城的简介 2024景山公园门票现在需要预约吗 ai怎么做绒的质感ai怎么画毛绒绒的服装款式图 景山公园门票当天可以买吗 ai绘画泳装女生如何生成怎么画泳装动漫 北京景山公园门票需要预约吗 北京景山公园怎么预约 ai款式图怎么去掉填充ai款式图怎么填色 ai款式图怎么去掉人台线ai线框怎么取消 安娜的衣橱是什么意思? 怎么让ai画出泳装效果怎么让ai画出泳装效果 景山公园预约门票_河南省新密市有哪些景点 ai怎么画出毛笔字效果? cubase快捷键 助剂厂家选哪家公司 上海哪里有质量好的泡沫胶水卖? 上海神雨胶水在哪里 上海哪里有ABS胶水 怎样做家常版西红柿炒花菜?