当前位置 : 主页 > 手机开发 > android >

详解Flutter 响应式状态管理框架GetX

来源:互联网 收集:自由互联 发布时间:2023-02-01
目录 一、状态管理框架对比 Provider BLoC GetX 二、基本使用 2.1 安装与引用 2.2 使用GetX改造Counter App 2.3 GetX代码插件 三、其他功能 3.1 路由管理 3.2 依赖关系管理 3.3 工具 3.4 改变主题 3.5
目录
  • 一、状态管理框架对比
    • Provider
    • BLoC
    • GetX
  • 二、基本使用
    • 2.1 安装与引用
    • 2.2 使用GetX改造Counter App
    • 2.3 GetX代码插件
  • 三、其他功能
    • 3.1 路由管理
    • 3.2 依赖关系管理
    • 3.3 工具
    • 3.4 改变主题
    • 3.5 GetConnect
    • 3.6 GetPage中间件
      • Priority
      • Redirect
      • onPageCalled
      • OnBindingsStart
      • OnPageBuildStart
    • 3.7 全局设置和手动配置
      • 3.8 StateMixin
        • 3.9 GetxService

        一、状态管理框架对比

        在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 {
          @override
          Widget 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();
          @override
          Widget 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 {
          @override
          Map<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 request
          Future<Response> getUser(int id) => get('http://youapi/users/$id');
          // Post request
          Future<Response> postUser(Map data) => post('http://youapi/users', body: data);
          // Post request with File
          Future<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 {
          @override
          void 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 requests
            httpClient.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 delivered
            httpClient.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 header
              request.headers['Authorization'] = "$token";
              return request;
            });
            //Autenticator will be called 3 times if HttpStatus is
            //HttpStatus.unauthorized
            httpClient.maxAuthRetries = 3;
          }
          }
          @override
          Future<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&lt;User&gt;{}
        

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

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

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

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

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

        class OtherClass extends GetView<Controller> {
          @override
          Widget 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官方文档

        以上就是详解Flutter 响应式状态管理框架GetX的详细内容,更多关于Flutter响应式状态管理GetX的资料请关注自由互联其它相关文章!

        上一篇:Kotlin扩展函数超详细介绍
        下一篇:没有了
        网友评论