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

宁睿  论坛元老 | 2025-3-16 16:07:15 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1039|帖子 1039|积分 3127

一、状态管理框架对比

在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之前和之后的版本。
  1. #非空安全最后一个版本(flutter 2.0之前版本)
  2. get: ^3.26.0
  3.    
  4. #空安全版本
  5. get: ^4.6.5
复制代码
然后,在需要使用的地方引入get。
  1. import 'package:get/get.dart';
复制代码
2.2 使用GetX改造Counter App

为了展示GetX的强盛功能,我们将对Flutter官方的计数器示例使用GetX举行改造。而且,使用GetX之后,业务逻辑和屏幕之间的共享状态的管理将会发生变革,经过改造之后,本来需要近100行代码才能使用的功能,现在26行就可以或许实现。
第1步,将项目的MaterialApp变成GetMaterialApp,如下所示。
  1. void main() => runApp(GetMaterialApp(home: Home()));
复制代码
需要说明的是,在应用的最顶层使用GetMaterialApp会创建路由,假如您只是想使用GetX举行状态管理或依赖项管理,则没有须要将MaterialApp修改为GetMaterialApp。假如项目中需要使用诸如路由、snackbar、国际化、bottomSheets、对话框和上下文干系的高级api,那么可以使用GetMaterialApp。
同时,假如使用了GetMaterialApp的路由功能,那么可以使用Get.to(), Get.back()等函数来管理路由时。假如不筹划使用它,那么就没有须要实行第一步。
第2步,创建业务逻辑类,并将所有变量、方法和控制器放在里面。然后使用.obs创建可观察变量。
  1. class Controller extends GetxController{
  2.   var count = 0.obs;
  3.   increment() => count++;
  4. }
复制代码
第3步,创建视图,然后GetX的Controller获取状态,在创建视图时使用StatelessWidget即可,不再需要使用Statfulwidget,更加节约内存和性能开销。
  1. class Home extends StatelessWidget {
  2.   @override
  3.   Widget build(context) {
  4.     final Controller c = Get.put(Controller());
  5.     return Scaffold(
  6.       appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
  7.       body: Center(child: ElevatedButton(
  8.               child: Text("Go to Other"), onPressed: () => Get.to(Other()))),
  9.       floatingActionButton:
  10.           FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));
  11.   }
  12. }
  13. class Other extends StatelessWidget {
  14.   final Controller c = Get.find();
  15.   @override
  16.   Widget build(context){
  17.      return Scaffold(body: Center(child: Text("${c.count}")));
  18.   }
  19. }
复制代码
从示例可以看到,引入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。
  1. GetMaterialApp(  
  2.   home: MyHome(),
  3. )
复制代码
然后,要打开一个新页面时,使用Get.to()大概Get.toNamed()。
  1. Get.to(NextScreen());
  2. Get.toNamed('/details');
复制代码
使用命名路由打开一个新的页面时,需要先在GetMaterialApp中举行说明,比如。
  1. void main() {
  2.   runApp(
  3.     GetMaterialApp(
  4.       initialRoute: '/',
  5.       getPages: [
  6.         GetPage(name: '/', page: () => MyHomePage()),
  7.         GetPage(name: '/second', page: () => Second()),
  8.         GetPage(
  9.           name: '/third',
  10.           page: () => Third(),
  11.           transition: Transition.zoom  
  12.         ),
  13.       ],
  14.     )
  15.   );
  16. }
复制代码
假如使用的是体系默认的AppBar,使用的是Navigator.pop(context)来关闭页面,假如是自界说的AppBar,而且使用了GetX的路由功能,那么需要使用下面得代码来关闭页面。
  1. Get.back();
复制代码
偶然候,我们打开一个新页面时候,在关闭得时需要返回上一个页面,那么可以使用Get.off()。
  1. Get.off(NextScreen());
复制代码
假如打开页面而且需要清除路由栈的其他页面,那么可以使用Get.offAll()。
  1. Get.offAll(NextScreen());
复制代码
同时,GetX的路由提供了获取上下文的功能,这也是GetX路由管理的最大长处之一。有了它,开辟者可以从控制器类中获取路由的所有方法。
3.2 依赖关系管理

除了状态管理和路由管理外,GetX还支持依赖关系管理,它可以或许实现Bloc或Controller雷同功能,而实现上只需要1行代码。
  1. Controller controller = Get.put(Controller());
复制代码
同时,Get依赖管理与包的其他部分是分离的,假如你的应用步调已经在使用状态管理器,那么不需要再重写它,比如。
  1. controller.fetchApi();
复制代码
假如您的项目中使用了许多的路由,而且这些路由已经存在了GetX的路由栈中,那么可以使用Get.find()来获取路由栈的干系信息。
  1. Controller controller = Get.find();
  2. Text(controller.textFromApi);
复制代码
3.3 工具

除了上面介绍的核心功能外,GetX还提供了许多的工具功能,比如国际化。使用GetX实现国际化时,起首需要创建一个继续自Translations的类。
  1. import 'package:get/get.dart';
  2. class Messages extends Translations {  @override  Map<String, Map<String, String>> get keys => {        'en_US': {          'hello': 'Hello World',        },        'de_DE': {          'hello': 'Hallo Welt',        }      };}
复制代码
然后,只需将.tr附加到指定的键,它将使用GetX的当前值举行转换。体系将会依据地区和Get.fallbackLocale的返回值举行转换。
  1. Text('title'.tr);
复制代码
除此之外,我们还可以使用参数的方式举行转换。
  1. import 'package:get/get.dart';
  2. 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中界说语言的环境和翻译内容。
  1. return GetMaterialApp(
  2.     translations: Messages(),  
  3.     locale: Locale('en', 'US'),  
  4.     fallbackLocale: Locale('en', 'UK'),
  5. );
复制代码
假如需要改变本地的默认的语言环境,可以使用下面的方式。
  1. var locale = Locale('en', 'US');
  2. Get.updateLocale(locale);
复制代码
3.4 改变主题

固然,我们可以可以使用GetX来实现默认主题的修改。这种功能类似于使用ThemeProvider来改变应用的主题。起首,您需要创建自界说主题并将其添加到Get中。
  1. Get.changeTheme(ThemeData.light());
复制代码
比如,我们想在onTap中创建一个按钮来改变主题。
  1. Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark());
复制代码
3.5 GetConnect

GetConnect是一种可以使用http或websockets实现和服务器通讯的方法,可以使用它实现GET/POST/PUT/DELETE/SOCKET网络请求。
  1. class UserProvider extends GetConnect {
  2.   // Get request
  3.   Future<Response> getUser(int id) => get('http://youapi/users/$id');
  4.   // Post request
  5.   Future<Response> postUser(Map data) => post('http://youapi/users', body: data);
  6.   // Post request with File
  7.   Future<Response<CasesModel>> postCases(List<int> image) {
  8.     final form = FormData({
  9.       'file': MultipartFile(image, filename: 'avatar.png'),
  10.       'otherFile': MultipartFile(image, filename: 'cover.png'),
  11.     });
  12.     return post('http://youapi/users/upload', form);
  13.   }
  14.   GetSocket userMessages() {
  15.     return socket('https://yourapi/users/socket');
  16.   }
  17. }
复制代码
和其他的网络请求库一样,GetConnect也支持自界说基Url、请求头、自界说请求修饰符,以及请求重试,以及解码器和将请求效果转换为model等。
  1. class HomeProvider extends GetConnect {
  2.   @override
  3.   void onInit() {
  4.     // All request will pass to jsonEncode so CasesModel.fromJson()
  5.     httpClient.defaultDecoder = CasesModel.fromJson;
  6.     httpClient.baseUrl = 'https://api.covid19api.com';
  7.     // baseUrl = 'https://api.covid19api.com'; // It define baseUrl to
  8.     // Http and websockets if used with no [httpClient] instance
  9.     // It's will attach 'apikey' property on header from all requests
  10.     httpClient.addRequestModifier((request) {
  11.       request.headers['apikey'] = '12345678';
  12.       return request;
  13.     });
  14.     // Even if the server sends data from the country "Brazil",
  15.     // it will never be displayed to users, because you remove
  16.     // that data from the response, even before the response is delivered
  17.     httpClient.addResponseModifier<CasesModel>((request, response) {
  18.       CasesModel model = response.body;
  19.       if (model.countries.contains('Brazil')) {
  20.         model.countries.remove('Brazilll');
  21.       }
  22.     });
  23.     httpClient.addAuthenticator((request) async {
  24.       final response = await get("http://yourapi/token");
  25.       final token = response.body['token'];
  26.       // Set the header
  27.       request.headers['Authorization'] = "$token";
  28.       return request;
  29.     });
  30.     //Autenticator will be called 3 times if HttpStatus is
  31.     //HttpStatus.unauthorized
  32.     httpClient.maxAuthRetries = 3;
  33.   }
  34.   }
  35.   @override
  36.   Future<Response<CasesModel>> getCases(String path) => get(path);
  37. }
复制代码
3.6 GetPage中心件

Priority

GetPage是一个新的属性,它担当一个GetMiddleWare列表,然后按照列表的顺序来实行这些中心件。要运行的中心件的顺序,可以通过GetMiddleware中的顺序来设置。
  1. final middlewares = [
  2.   GetMiddleware(priority: 2),
  3.   GetMiddleware(priority: 5),
  4.   GetMiddleware(priority: 4),
  5.   GetMiddleware(priority: -8),
  6. ];
复制代码
Redirect

当需要实行路由的重定向时,就可以调用此函数,比如使用它实现强制登录逻辑,即没有登录时跳转登录逻辑。
  1. RouteSettings redirect(String route) {
  2.   final authService = Get.find<AuthService>();
  3.   return authService.authed.value ? null : RouteSettings(name: '/login')
  4. }
复制代码
onPageCalled

偶然候,我们需要在页面创建之前调用某个额函数,比如可以使用它来更改页面的某些内容或为其提供新页面。
  1. GetPage onPageCalled(GetPage page) {
  2.   final authService = Get.find<AuthService>();
  3.   return page.copyWith(title: 'Welcome ${authService.UserName}');
  4. }
复制代码
OnBindingsStart

此函数将在初始化绑定之前被调用。在这个函数中,我们可以更改页面的绑定。
  1. List<Bindings> onBindingsStart(List<Bindings> bindings) {
  2.   final authService = Get.find<AuthService>();
  3.   if (authService.isAdmin) {
  4.     bindings.add(AdminBinding());
  5.   }
  6.   return bindings;
  7. }
复制代码
OnPageBuildStart

此函数将在绑定初始化之后被调用。在这个函数中,我们可以在创建绑定之后和创建页面小部件之前实行一些操纵。
  1. GetPageBuilder onPageBuildStart(GetPageBuilder page) {
  2.   print('bindings are ready');
  3.   return page;
  4. }
复制代码
3.7 全局设置和手动设置

默认环境下,GetMaterialApp提供了一下默认的设置,但假如你想手动设置GetX,那也是可以得。
  1. MaterialApp(
  2.   navigatorKey: Get.key,
  3.   navigatorObservers: [GetObserver()],
  4. );
复制代码
我们还可以将GetObserver替换成自己的中心件。
  1. MaterialApp(
  2.   navigatorKey: Get.key,
  3.   navigatorObservers: [
  4.     GetObserver(MiddleWare.observer) // Here
  5.   ],
  6. );
复制代码
固然,我们也可以为GetX创建一个全局设置。比如在打开路由时修改默认的设置。
  1. GetMaterialApp(
  2.   enableLog: true,
  3.   defaultTransition: Transition.fade,
  4.   opaqueRoute: Get.isOpaqueRouteDefault,
  5.   popGesture: Get.isPopGestureEnable,
  6.   transitionDuration: Get.defaultDurationTransition,
  7.   defaultGlobalState: Get.defaultGlobalState,
  8. );
  9. Get.config(
  10.   enableLog = true,
  11.   defaultPopGesture = true,
  12.   defaultTransition = Transitions.cupertino
  13. )
复制代码
3.8 StateMixin

处理UI状态的另一种方法是使用StateMixin,使用前需要使用with将StateMixin添加到T模子的控制器中。
  1. class Controller extends GetController with StateMixin<User>{}
复制代码
mixin是Dart中一个非常重要的概念,是一种在多个类层次结构中复用类代码的方法。而change()方法是一种可以随时更改状态的方法。
  1. change(data, status: RxStatus.success());
复制代码
RxStatus支持的状态有如下一些:
  1. RxStatus.loading();
  2. RxStatus.success();
  3. RxStatus.empty();
  4. RxStatus.error('message');
复制代码
然后,我们使用obx根据状态来加载不同的视图。
  1. class OtherClass extends GetView<Controller> {
  2.   @override
  3.   Widget build(BuildContext context) {
  4.     return Scaffold(
  5.       body: controller.obx(
  6.         (state)=>Text(state.name),
  7.         onLoading: CustomLoadingIndicator(),
  8.         onEmpty: Text('No data found'),
  9.         onError: (error)=>Text(error),
  10.       ),
  11.     );
  12. }
复制代码
3.9 GetxService

GetxService的作用类似于GetxController,可以共享雷同的生命周期(onInit(), onReady(), onClose()),GetxService可以用来实现背景服务。比方:ApiService, StorageService, CacheService。
  1. Future<void> main() async {
  2.   await initServices();  
  3.   runApp(SomeApp());
  4. }
  5. void initServices() async {
  6.   print('starting services ...');
  7.   await Get.putAsync(() => DbService().init());
  8.   await Get.putAsync(SettingsService()).init();
  9.   print('All services started...');
  10. }
  11. class DbService extends GetxService {
  12.   Future<DbService> init() async {
  13.     print('$runtimeType delays 2 sec');
  14.     await 2.delay();
  15.     print('$runtimeType ready!');
  16.     return this;
  17.   }
  18. }
  19. class SettingsService extends GetxService {
  20.   void init() async {
  21.     print('$runtimeType delays 1 sec');
  22.     await 1.delay();
  23.     print('$runtimeType ready!');
  24.   }
  25. }
复制代码
参考:Get官方文档

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

宁睿

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表