flutter 专题二十四 Flutter 响应式状态管理框架GetX

一、状态管理框架对比

在Flutter的状态管理框架中,主流的状态管理框架有四个:GetX(又称为Get)、BLoC、MobX、Provider。

Provider

其中,Provider是Flutter社区提供的一种状态管理工具,本质上是对InheritedWidget组件的封装,具有如下一些优点:

  • 简化的资源分配与处置
  • 懒加载
  • 创建新类时减少大量的模板代码
  • 支持 DevTools
  • 更通用的调用InheritedWidget的方式
  • 提升类的可扩展性,整体的监听架构时间复杂度以指数级增长
BLoC

BLoC是Business Logic Component的英文缩写,中文译为业务逻辑组件,是一种使用响应式编程来构建应用的方式。BLoC最早由谷歌的Paolo Soares和Cong Hui设计并开发,设计的初衷是为了实现页面视图与业务逻辑的分离。下图演示了BLoC模式的应用程序的架构示意图。

BLoC依赖Stream和StreamController,组件通过Sink发送状态事件,然后再通过Stream通知其他组件进行状态刷新,事件的处理和通知更新都由BLoC负责,如下图所示。

GetX

GetX是一种轻量级且强大的Flutter解决方案,集高性能状态管理、智能依赖注入和路由管理于一体,是Flutter开发不可多得的工具。具有如下优势:

  • 依赖注入: 依赖注入是一种消除组件之间依赖的方式,用来降低使用者与其依赖组件之间的耦合度。使用依赖注入具有便于重构和便于扩展的好处,比如获取实例无需BuildContext、GetBuilder自动化处理及减少入参等等。
  • 跨页面交互: 跨页面交互是一种常见的场景,GetX可以很优雅的实现跨页面交互,如参数传递和跨页面的状态管理。
  • 路由管理 :Getx内部实现了路由管理,而且使用起来也很简单。同时,GetX实现了动态路由传参,也就是说直接在命名路由上拼参数。

二、基本使用

2.1 安装与引用

和其他的Flutter插件一样,使用GetX之前需要先在项目中导入GetX插件,GetX插件分为非空安全和空安全两个版本,分别是应对2.0之前和之后的版本。

#非空安全最后一个版本(flutter 2.0之前版本)
get: ^3.26.0#空安全版本 
get: ^4.6.5

然后,在需要使用的地方引入get。

import 'package:get/get.dart';

2.2 使用GetX改造Counter App

为了展示GetX的强大功能,我们将对Flutter官方的计数器示例使用GetX进行改造。并且,使用GetX之后,业务逻辑和屏幕之间的共享状态的管理将会发生变化,经过改造之后,原本需要近100行代码才能使用的功能,现在26行就能够实现。

第1步,将项目的MaterialApp变成GetMaterialApp,如下所示。

void main() => runApp(GetMaterialApp(home: Home()));

需要说明的是,在应用的最顶层使用GetMaterialApp会创建路由,如果您只是想使用GetX进行状态管理或依赖项管理,则没有必要将MaterialApp修改为GetMaterialApp。如果项目中需要使用诸如路由、snackbar、国际化、bottomSheets、对话框和上下文相关的高级api,那么可以使用GetMaterialApp。

同时,如果使用了GetMaterialApp的路由功能,那么可以使用Get.to(), Get.back()等函数来管理路由时。如果不打算使用它,那么就没有必要执行第一步。

第2步,创建业务逻辑类,并将所有变量、方法和控制器放在里面。然后使用.obs创建可观察变量。

class Controller extends GetxController{var count = 0.obs;increment() => count++;
}

第3步,创建视图,然后GetX的Controller获取状态,在创建视图时使用StatelessWidget即可,不再需要使用Statfulwidget,更加节约内存和性能开销。

class Home extends StatelessWidget {@overrideWidget build(context) {final Controller c = Get.put(Controller());return Scaffold(appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),body: Center(child: ElevatedButton(child: Text("Go to Other"), onPressed: () => Get.to(Other()))),floatingActionButton:FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));}
}class Other extends StatelessWidget {final Controller c = Get.find();@overrideWidget build(context){return Scaffold(body: Center(child: Text("${c.count}")));}
}

从示例可以看到,引入GetX状态管理框架之后,代码的结构也相对的发生了变化,并且逻辑上也变得更加清晰,非常适合中大型项目。

2.3 GetX代码插件

由于GetX的使用流程比较固定,我们完全可以将它开发成一个IDE插件,下面是社区上开源的一个IDE插件,大家可以将其下载下来,然后进行自定义的升级。

  • Github:getx_template
  • Jetbrains:getx_template

目前,插件已经支持生成GetX的所有文件。首先,我们打开Android Studuo,在Plugins 搜索GeX,然后点击安装。

接着,生成GetX的模式,目前提供了两种生成方式:

  • Default:默认模式,生成三个文件:state,logic,view
  • Easy:简单模式,生成俩个文件:logic,view

 

同时,插件还提供后缀名修改和数据的持久化。

插件还提供了快捷键功能,如使用【Alt + Enter】快捷键可以调出包裹Widget,有四种可选:GetBuilder、GetBuilder(Auto Dispose),Obx、GetX。

三、其他功能

除了基本的状态管理外,GetX还支持依赖关系管理、路由管理、snackbar、国际化、bottomSheets、上下文相关的API等功能。

3.1 路由管理

如果在项目中有涉及路由、snackbar、对话框、bottomSheets的功能需求,那么GetX也是非常不错的。为了使用GetX的路由功能,我们需要使用GetMaterialApp替换MaterialApp。

GetMaterialApp(  home: MyHome(),
)

然后,要打开一个新页面时,使用Get.to()或者Get.toNamed()。

Get.to(NextScreen());
Get.toNamed('/details');

使用命名路由打开一个新的页面时,需要先在GetMaterialApp中进行申明,比如。

void main() {runApp(GetMaterialApp(initialRoute: '/',getPages: [GetPage(name: '/', page: () => MyHomePage()),GetPage(name: '/second', page: () => Second()),GetPage(name: '/third',page: () => Third(),transition: Transition.zoom  ),],));
}

如果使用的是系统默认的AppBar,使用的是Navigator.pop(context)来关闭页面,如果是自定义的AppBar,并且使用了GetX的路由功能,那么需要使用下面得代码来关闭页面。

Get.back();

有时候,我们打开一个新页面时候,在关闭得时需要返回上一个页面,那么可以使用Get.off()。

Get.off(NextScreen());

如果打开页面并且需要清除路由栈的其他页面,那么可以使用Get.offAll()。

Get.offAll(NextScreen());

同时,GetX的路由提供了获取上下文的功能,这也是GetX路由管理的最大优点之一。有了它,开发者可以从控制器类中获取路由的所有方法。

3.2 依赖关系管理

除了状态管理和路由管理外,GetX还支持依赖关系管理,它能够实现Bloc或Controller相同功能,而实现上只需要1行代码。

Controller controller = Get.put(Controller()); 

同时,Get依赖管理与包的其他部分是分离的,如果你的应用程序已经在使用状态管理器,那么不需要再重写它,比如。

controller.fetchApi();

如果您的项目中使用了很多的路由,并且这些路由已经存在了GetX的路由栈中,那么可以使用Get.find()来获取路由栈的相关信息。

Controller controller = Get.find();
Text(controller.textFromApi);

3.3 工具

除了上面介绍的核心功能外,GetX还提供了很多的工具功能,比如国际化。使用GetX实现国际化时,首先需要创建一个继承自Translations的类。

import 'package:get/get.dart';class Messages extends Translations {@overrideMap<String, Map<String, String>> get keys => {'en_US': {'hello': 'Hello World',},'de_DE': {'hello': 'Hallo Welt',}};
}

然后,只需将.tr附加到指定的键,它将使用GetX的当前值进行转换。系统将会依据地区和Get.fallbackLocale的返回值进行转换。

Text('title'.tr);

除此之外,我们还可以使用参数的方式进行转换。

import 'package:get/get.dart';Map<String, Map<String, String>> get keys => {'en_US': {'logged_in': 'logged in as @name with email @email',},'es_ES': {'logged_in': 'iniciado sesión como @name con e-mail @email',}
};Text('logged_in'.trParams({'name': 'Jhon','email': 'jhon@example.com'}));

最后,还需要在GetMaterialApp中定义语言的环境和翻译内容。

return GetMaterialApp(translations: Messages(),  locale: Locale('en', 'US'),  fallbackLocale: Locale('en', 'UK'), 
);

如果需要改变本地的默认的语言环境,可以使用下面的方式。

var locale = Locale('en', 'US');
Get.updateLocale(locale);

3.4 改变主题

当然,我们可以可以使用GetX来实现默认主题的修改。这种功能类似于使用ThemeProvider来改变应用的主题。首先,您需要创建自定义主题并将其添加到Get中。

Get.changeTheme(ThemeData.light());

比如,我们想在onTap中创建一个按钮来改变主题。

Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark());

3.5 GetConnect

GetConnect是一种可以使用http或websockets实现和服务器通信的方法,可以使用它实现GET/POST/PUT/DELETE/SOCKET网络请求。

class UserProvider extends GetConnect {// Get requestFuture<Response> getUser(int id) => get('http://youapi/users/$id');// Post requestFuture<Response> postUser(Map data) => post('http://youapi/users', body: data);// Post request with FileFuture<Response<CasesModel>> postCases(List<int> image) {final form = FormData({'file': MultipartFile(image, filename: 'avatar.png'),'otherFile': MultipartFile(image, filename: 'cover.png'),});return post('http://youapi/users/upload', form);}GetSocket userMessages() {return socket('https://yourapi/users/socket');}
}

和其他的网络请求库一样,GetConnect也支持自定义基Url、请求头、自定义请求修饰符,以及请求重试,以及解码器和将请求结果转换为model等。

class HomeProvider extends GetConnect {@overridevoid onInit() {// All request will pass to jsonEncode so CasesModel.fromJson()httpClient.defaultDecoder = CasesModel.fromJson;httpClient.baseUrl = 'https://api.covid19api.com';// baseUrl = 'https://api.covid19api.com'; // It define baseUrl to// Http and websockets if used with no [httpClient] instance// It's will attach 'apikey' property on header from all requestshttpClient.addRequestModifier((request) {request.headers['apikey'] = '12345678';return request;});// Even if the server sends data from the country "Brazil",// it will never be displayed to users, because you remove// that data from the response, even before the response is deliveredhttpClient.addResponseModifier<CasesModel>((request, response) {CasesModel model = response.body;if (model.countries.contains('Brazil')) {model.countries.remove('Brazilll');}});httpClient.addAuthenticator((request) async {final response = await get("http://yourapi/token");final token = response.body['token'];// Set the headerrequest.headers['Authorization'] = "$token";return request;});//Autenticator will be called 3 times if HttpStatus is//HttpStatus.unauthorizedhttpClient.maxAuthRetries = 3;}}@overrideFuture<Response<CasesModel>> getCases(String path) => get(path);
}

3.6 GetPage中间件

Priority

GetPage是一个新的属性,它接受一个GetMiddleWare列表,然后按照列表的顺序来执行这些中间件。要运行的中间件的顺序,可以通过GetMiddleware中的顺序来设置。

final middlewares = [GetMiddleware(priority: 2),GetMiddleware(priority: 5),GetMiddleware(priority: 4),GetMiddleware(priority: -8),
];
Redirect

当需要执行路由的重定向时,就可以调用此函数,比如使用它实现强制登录逻辑,即没有登录时跳转登录逻辑。

RouteSettings redirect(String route) {final authService = Get.find<AuthService>();return authService.authed.value ? null : RouteSettings(name: '/login')
}
onPageCalled

有时候,我们需要在页面创建之前调用某个额函数,比如可以使用它来更改页面的某些内容或为其提供新页面。

GetPage onPageCalled(GetPage page) {final authService = Get.find<AuthService>();return page.copyWith(title: 'Welcome ${authService.UserName}');
}
OnBindingsStart

此函数将在初始化绑定之前被调用。在这个函数中,我们可以更改页面的绑定。

List<Bindings> onBindingsStart(List<Bindings> bindings) {final authService = Get.find<AuthService>();if (authService.isAdmin) {bindings.add(AdminBinding());}return bindings;
}
OnPageBuildStart

此函数将在绑定初始化之后被调用。在这个函数中,我们可以在创建绑定之后和创建页面小部件之前执行一些操作。

GetPageBuilder onPageBuildStart(GetPageBuilder page) {print('bindings are ready');return page;
}

3.7 全局设置和手动配置

默认情况下,GetMaterialApp提供了一下默认的配置,但如果你想手动配置GetX,那也是可以得。

MaterialApp(navigatorKey: Get.key,navigatorObservers: [GetObserver()],
);

我们还可以将GetObserver替换成自己的中间件。

MaterialApp(navigatorKey: Get.key,navigatorObservers: [GetObserver(MiddleWare.observer) // Here],
);

当然,我们也可以为GetX创建一个全局设置。比如在打开路由时修改默认的配置。

GetMaterialApp(enableLog: true,defaultTransition: Transition.fade,opaqueRoute: Get.isOpaqueRouteDefault,popGesture: Get.isPopGestureEnable,transitionDuration: Get.defaultDurationTransition,defaultGlobalState: Get.defaultGlobalState,
);Get.config(enableLog = true,defaultPopGesture = true,defaultTransition = Transitions.cupertino
)

3.8 StateMixin

处理UI状态的另一种方法是使用StateMixin,使用前需要使用with将StateMixin添加到T模型的控制器中。

class Controller extends GetController with StateMixin<User>{}

mixin是Dart中一个非常重要的概念,是一种在多个类层次结构中复用类代码的方法。而change()方法是一种可以随时更改状态的方法。

change(data, status: RxStatus.success());

RxStatus支持的状态有如下一些:

RxStatus.loading();
RxStatus.success();
RxStatus.empty();
RxStatus.error('message');

然后,我们使用obx根据状态来加载不同的视图。

class OtherClass extends GetView<Controller> {@overrideWidget build(BuildContext context) {return Scaffold(body: controller.obx((state)=>Text(state.name),onLoading: CustomLoadingIndicator(),onEmpty: Text('No data found'),onError: (error)=>Text(error),),);
}

3.9 GetxService

GetxService的作用类似于GetxController,可以共享相同的生命周期(onInit(), onReady(), onClose()),GetxService可以用来实现后台服务。例如:ApiService, StorageService, CacheService。

Future<void> main() async {await initServices();  runApp(SomeApp());
}void initServices() async {print('starting services ...');await Get.putAsync(() => DbService().init());await Get.putAsync(SettingsService()).init();print('All services started...');
}class DbService extends GetxService {Future<DbService> init() async {print('$runtimeType delays 2 sec');await 2.delay();print('$runtimeType ready!');return this;}
}class SettingsService extends GetxService {void init() async {print('$runtimeType delays 1 sec');await 1.delay();print('$runtimeType ready!');}
}

参考:Get官方文档

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/501172.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

禁用div的写法(自定义disabled)Vue3

因为div 元素本身没有 disabled 属性&#xff0c;所以需要根据JavaScript中的变量、通过动态绑定 class &#xff08;Vue的:class&#xff09;来改变样式。 需要一个变量 isDivDisabled import { ref } from vue; let isDivDisabled ref(false);当 isDivDisabled true &…

大模型系列——旋转位置编码和长度外推

绝对位置编码 旋转位置编码 论文中有个很直观的图片展示了旋转变换的过程&#xff1a; 对于“我”对应的d维向量&#xff0c; 拆分成d/2组以后&#xff0c;每组对应一个角度&#xff0c;若1对应的向量为(x1,x2)&#xff0c;应用旋转位置编码&#xff0c;相当于这个分量旋转了m…

路径规划 | 基于极光PLO优化算法的三维路径规划Matlab程序

效果一览 基本介绍 研究内容 极光优化算法&#xff08;PLO&#xff09;的深入理解&#xff1a; 研究极光优化算法的基本原理&#xff0c;包括模拟带电粒子在地球磁场中的旋转运动、极光椭圆区域内的行走以及粒子间的碰撞等。 分析PLO算法的全局搜索能力和局部开发能力&#xf…

MATLAB画柱状图

一、代码 clear; clc; figure(position,[150,100,900,550])%确定图片的位置和大小&#xff0c;[x y width height] %准备数据 Y1[0.53,7.9,8.3;0.52,6.8,9.2;0.52,5.9,8.6;2.8,5.8,7.9;3.9,5.2,7.8;1.8,5.8,8.4]; % withoutNHC X11:6; %画出4组柱状图&#xff0c;宽度1 h1…

[实用指南]如何将视频从iPhone传输到iPad

概括 将视频从 iPhone 传输到 iPad 时遇到问题&#xff1f;您可能知道一种方法&#xff0c;但不知道如何操作。此外&#xff0c;您要传输的视频越大&#xff0c;完成任务就越困难。那么如何将视频从 iPhone 传输到 iPad&#xff0c;特别是当您需要发送大视频文件时&#xff1f…

Git命令行的使用

目录 一、什么是Git 1、本地仓库 vs 远端仓库 本地仓库 远端仓库 2、.git vs .gitignore .git .gitignore 二、使用Git命令 1、安装git 2、git首次使用需要配置用户邮箱和用户名 3、上传目录/文件到远端仓库步骤 1&#xff09;创建放置文件的目录 2&#xff09;cd…

黑马JavaWeb开发跟学(十五).Maven高级

黑马JavaWeb开发跟学.十五.Maven高级 Maven高级1. 分模块设计与开发1.1 介绍1.2 实践1.2.1 分析1.2.2 实现 1.3 总结 2. 继承与聚合2.1 继承2.1.1 继承关系2.1.1.1 思路分析2.1.1.2 实现 2.1.2 版本锁定2.1.2.1 场景2.1.2.2 介绍2.1.2.3 实现2.1.2.4 属性配置 2.2 聚合2.2.1 介…

十二、Vue 路由

文章目录 一、简介二、安装与基本配置安装 Vue Router创建路由实例在应用中使用路由实例三、路由组件与视图路由组件的定义与使用四、动态路由动态路由参数的定义与获取动态路由的应用场景五、嵌套路由嵌套路由的概念与配置嵌套路由的应用场景六、路由导航<router - link>…

AE RFG 1251 Generator User Manual

AE RFG 1251 Generator User Manual

vue2、element的el-select 选项框的宽度设置、文本过长问题

<el-select v-model"value" placeholder"请选择"><el-optionv-for"item in cities":key"item.value":label"item.label":value"item.value"><el-tooltip class"item" :content"ite…

【Matlab算法】基于改进人工势场法的移动机器人路径规划研究(附MATLAB完整代码)

基于改进人工势场法的移动机器人路径规划研究 结果图摘要1. 引言2. 方法说明2.1 基本原理2.2 改进策略3. 核心函数解释3.1 改进的斥力计算函数3.2 路径规划主函数4. 实验设计4.1 实验环境设置4.2 关键参数选择5. 结果分析5.1 实验结果5.2 性能分析附录:完整代码参考文献结果图…

【MySQL】--- 内置函数

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; MySQL &#x1f3e0; 时间函数 约定&#xff1a;我们在MySQL中说的日期指的是年 月 日&#xff0c;时间指的是时 分 秒。 &#x1f9f7; now() select n…

springboot和vue项目前后端交互

java后端开发常用springboot框架&#xff0c;开发简单不繁琐&#xff0c;容易上手。简简单单配置好一些配置项&#xff0c;整个web项目就能运行起来了。vue前端也是比较流行的前端开发框架&#xff0c;写起来简单&#xff0c;组件也丰富&#xff0c;参考资料多。 这期就应薯薯…

酒店管理系统|Java|SSM|VUE| 前后端分离

【技术栈】 1⃣️&#xff1a;架构: B/S、MVC 2⃣️&#xff1a;系统环境&#xff1a;Windowsh/Mac 3⃣️&#xff1a;开发环境&#xff1a;IDEA、JDK1.8、Maven、Mysql5.7 4⃣️&#xff1a;技术栈&#xff1a;Java、Mysql、SSM、Mybatis-Plus、VUE、jquery,html 5⃣️数据库可…

OkHttp接口自动化测试

文章目录 java环境搭建OkHttp之getOkHttp之POSTPOST发送From表单POST发送jsonPOST上传文件 OkHttp之deleteOkHttp之put java环境搭建 引入依赖 <!--okhttp3--><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</art…

分数阶傅里叶变换代码 MATLAB实现

function Faf myfrft(f, a) %分数阶傅里叶变换函数 %输入参数&#xff1a; %f&#xff1a;原始信号 %a&#xff1a;阶数 %输出结果&#xff1a; %原始信号的a阶傅里叶变换N length(f);%总采样点数 shft rem((0:N-1)fix(N/2),N)1;%此项等同于fftshift(1:N)&#xff0c;起到翻…

【Rust练习】26.Package and Crate

练习题来自&#xff1a;https://practice-zh.course.rs/crate-module/crate.html 建议在命令行下操作完成本节内容&#xff0c;Windows 11/10 首选 Windows 终端&#xff0c;好看&#xff0c;支持渲染中文字体&#xff0c;缺点是功能太少了&#xff1b;其次推荐 mobaxterm&…

Python实现接口签名调用

目录: 1、第三方接口签名调用2、调用结果 1、第三方接口签名调用 import json import requests import hashlib import time import hmac access_key xxxxxxxxxxxxxxx secret_key xxxxxxxxxxxxxxx # 应用信息 def _wps4_sig(method, url, date, body): print(body)if bod…

df.replace({‘b‘: r‘\s*(\.)\s*‘}, {‘b‘: r‘\1ty‘}, regex=True)

这段代码 df.replace({b: r\s*(\.)\s*}, {b: r\1ty}, regexTrue) 用于在 DataFrame 中进行替换操作&#xff0c;具体来说是针对 b 列&#xff0c;匹配并替换符合正则表达式的值。 详细解析&#xff1a; df.replace()&#xff1a;这是 Pandas 中的 replace() 方法&#xff0c;用…

js的一些处理

1.翻转字符串 let str abcdef str str.split().reverse().join() console.log(str) 因此想到了我之前写的截取字符串获取参数跳转&#xff0c;在写一遍 let str nameJack&age18&gender男 let list str.split(&); let obj {} list.forEach((v)>{ …