【Flutter】Getx下篇

🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Flutter学习
🌠 首发时间:2024年5月30日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾

Getx状态管理

状态管理

目前,Flutter 有几种状态管理器。但是,它们中的大多数都涉及到使用 ChangeNotifier 来更新 widget,这对于中大型应用的性能来说是一个很糟糕的方法。你可以在 Flutter 的官方文档中查看到,ChangeNotifier 应该使用 1 个或最多 2 个监听器,这使得它们实际上无法用于任何中等或大型应用。

Get 并不是比任何其他状态管理器更好或更差,而是说你应该分析这些要点以及下面的要点来选择只用 Get,还是与其他状态管理器结合使用。

Get 不是其他状态管理器的敌人,因为 Get 是一个微框架,而不仅仅是一个状态管理器,既可以单独使用,也可以与其他状态管理器结合使用。

Get 有两个不同的状态管理器:响应式状态管理器、简单的状态管理器。

响应式状态管理器

响应式编程可能会让很多人感到陌生,因为它很复杂,但是 Getx 将响应式编程变得非常简单。

  • 你不需要创建 StreamControllers
  • 你不需要为每个变量创建一个 StreamBuilder
  • 你不需要为每个状态创建一个类
  • 你不需要为一个初始值创建一个 get

使用 Get 的响应式编程就像使用 setstate 一样简单。

响应式状态管理器——计数器

让我们想象一下,你有一个名称变量,并且希望每次你改变它时,所有使用它的小组件都会自动刷新。

比如我们自定义的一个计数变量:

int _counter = 0;

要想让它变得可观察,只需要在它的末尾加上 “.obs”:

RxInt _counter = 0.obs;

而在 UI 中,当你想显示该值并在值变化时更新页面,只需这样做:

Obx(() => Text("${_counter.value}"));

演示代码:

import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final RxInt _counter = 0.obs;

  MyHomePage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('响应式状态管理器'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Obx(() => Text(
                  "$_counter",
                  style: const TextStyle(
                    fontSize: 80,
                  ),
                )),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _counter.value++;
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

在这里插入图片描述

声明一个响应式变量三种方式

第一种:使用 Rx{Type}

final RxString name = RxString('zhangsan');

final RxBool isLogged = RxBool(false);

final RxInt count = RxInt(0);

final RxDouble balance = RxDouble(0.0);

final RxList<String> items = RxList<String>([]);

final RxMap<String, int> myMap = RxMap<String, int>({});

第二种:使用 Rx,规定泛型 Rx

final Rx<String> name = Rx<String>('zhangsan');

final Rx<Bool> isLogged = Rx<Bool>(false);

final Rx<Int> count = Rx<Int>(0);

第三种:只需要添加 .obs 作为 value 的属性,更实用、简单(推荐)

final RxString name = 'zhangsan'.obs;

final RxBool islogged = false.obs;

final RxInt count = 0.obs;

简单的状态管理(依赖管理)GetxController

Getx 依赖管理简介

Get 有一个简单而强大的依赖管理器,它允许你只用 1 行代码就能检索到与你的BlocController 相同的类,无需 Provider context,无需 inheritedWidget

Controller controller = Get.put(Controller());
//而不是 Controller controller = Controller();

想象一下,你已经浏览了无数条路由,现在你需要拿到一个被遗留在控制器中的数据,那你需要一个状态管理器与 ProviderGet_it 一起使用来拿到它,对吗?

Get 则不然,Get 会自动为你的控制器找到你想要的数据,而你甚至不需要任何额外的依赖关系。

Controller controller = Get.find();
//是的,它看起来像魔术,Get会找到你的控制器,并将其提供给你,你可以实例化100万个控制器,Get总会给你正确的控制器

多页面之间的数据共享

为了展示 Get 的强大功能,我们使用 Getx 重写一个 “计数器 Plus版”,实现:

  • 每次点击都能改变状态
  • 在不同页面之间切换
  • 在不同页面之间共享状态
  • 将业务逻辑与界面分离

lib 新建 controller 文件夹存放控制类 countController

import 'package:get/get.dart';

class CountController extends GetxController {
  var count = 0.obs;

  //加
  void inc() {
    count++;
    update();
  }

  //减
  void dec() {
    count--;
    update();
  }
}

回到前面的仿闲鱼界面,将 home.dartcategory.dart 稍微修改一下,实现我们想要的效果:

home.dart

import 'package:flutter/material.dart';
import '../../controller/countController.dart';
import 'package:get/get.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  CountController countController = Get.put(CountController());

  
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Obx(() => Text(
                "${countController.count}",
                style: const TextStyle(fontSize: 60),
              )),
          ElevatedButton(
            onPressed: () {
              countController.inc();
            },
            child: const Text('加'),
          ),
        ],
      ),
    );
  }
}

category.dart

import 'package:flutter/material.dart';
import '../../controller/countController.dart';
import 'package:get/get.dart';

class CategoryPage extends StatefulWidget {
  const CategoryPage({super.key});

  
  State<CategoryPage> createState() => _CategoryPageState();
}

class _CategoryPageState extends State<CategoryPage> {
  CountController countController = Get.find();

  
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Obx(() => Text(
                "${countController.count}",
                style: const TextStyle(fontSize: 60),
              )),
          ElevatedButton(
            onPressed: () {
              countController.dec();
            },
            child: const Text('减'),
          ),
        ],
      ),
    );
  }
}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

Binding

需求:所有页面都要使用状态管理

在我们使用 Getx 状态管理器的时候,往往每次都是用需要手动实例化一个控制器,这样的话基本页面都需要实例化一次,这样就太麻烦了,而 Binding 能解决上述问题,可以在项目初始化时把所有需要进行状态管理的控制器进行统一初始化。

在前面,我们经常使用 Get.put(Mycontroller()) 来进行控制器实例的创建,这样我们就算不使用控制器实例也会被创建,其实 Getx 还提供很多创建实例的方法,可根据不同的业务来进行创建,接下来我们简单介绍一下几个最常用的:

  • Get.put():不使用控制器实例也会被创建
  • Get.lazyPut():懒加载方式创建实例,只有在使用时才创建
  • Get.putAsync()Get.put() 的异步版版本
  • Get.create():每次使用都会创建一个新的实例

第一步:声明需要进行的绑定控制器类

新建文件夹 binding,在其中新建 binding.dart

import 'package:get/get.dart';
import '../controller/countController.dart';

class AllControllerBinding implements Bindings {
  
  void dependencies() {
    Get.lazyPut<CountController>(() => CountController());
  }
}

第二步:在项目启动时进行初始化绑定

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      debugShowCheckedModeBanner: false,
      initialRoute: "/",
      defaultTransition: Transition.rightToLeft,
      getPages: AppPage.routes,
      initialBinding: AllControllerBinding(), //绑定
    );
  }
}

第三步:在页面中使用状态管理器

import 'package:flutter/material.dart';
import '../../controller/countController.dart';
import 'package:get/get.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  CountController countController = Get.find<CountController>();

  
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Obx(() => Text(
                "${countController.count}",
                style: const TextStyle(fontSize: 60),
              )),
          ElevatedButton(
            onPressed: () {
              countController.inc();
            },
            child: const Text('加'),
          ),
        ],
      ),
    );
  }
}
import 'package:flutter/material.dart';
import '../../controller/countController.dart';
import 'package:get/get.dart';

class CategoryPage extends StatefulWidget {
  const CategoryPage({super.key});

  
  State<CategoryPage> createState() => _CategoryPageState();
}

class _CategoryPageState extends State<CategoryPage> {
  CountController countController = Get.find<CountController>();

  
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Obx(() => Text(
                "${countController.count}",
                style: const TextStyle(fontSize: 60),
              )),
          ElevatedButton(
            onPressed: () {
              countController.dec();
            },
            child: const Text('减'),
          ),
        ],
      ),
    );
  }
}

GetView介绍

Getview 只是对已注册的 controller 有一个名为 controllergetterconst statelessWidget,如果我们的一个页面中只有单个控制器作为依赖项,那我们就可以使用 Getview,而不是使用 statelesswidget,并避免了写 Get.find()

GetView 的使用方法非常简单,只是要将你的视图层继承自 Getview 并传入需要注册的控制器并 Get.put() 即可。

GetView结合Getxcontroller使用

  1. 定义一个控制器,如 countController

  2. 继承 GetView 并使用状态管理

    import 'package:flutter/material.dart';
    import '../../controller/countController.dart';
    import 'package:get/get.dart';
    
    class HomePage extends GetView<CountController> {
      const HomePage({super.key});
    
      
      Widget build(BuildContext context) {
        Get.put(CountController()); //如果第一次使用还需要put
    
        return Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Obx(() => Text(
                    "${controller.count}",
                    style: const TextStyle(fontSize: 60),
                  )),
              ElevatedButton(
                onPressed: () {
                  controller.inc();
                },
                child: const Text('加'),
              ),
            ],
          ),
        );
      }
    }
    

依旧可以实现我们前面的功能。

GetView Binding结合Getxcontroller使用

在上面这种方式中,我们依旧需要手动进行 put,如果想去掉 put 这一步骤,我们可以使用 Binding

注意,依旧是当页面的依赖项为单个控制器才可以这样使用。我们写一个 binding,然后在 router.dart 中设置其 binding 即可:

import 'package:get/get.dart';
import '../binding/binding.dart';
import '../middleware/homeMiddleware.dart';
import '../pages/tabs/category.dart';
import '../pages/tabs/home.dart';
import '../pages/tabs.dart';

class AppPage {
  static final routes = [
    GetPage(name: "/", page: () => const Tabs()),
    GetPage(
      name: "/home",
      page: () => const HomePage(),
      transition: Transition.circularReveal, //也可以单独配置页面的跳转动画
      binding: AllControllerBinding(),
      middlewares: [HomeMiddleware()], //中间件
    ),
    GetPage(name: "/category", page: () => const CategoryPage()),
  ];
}

然后,就可以将对应页面中的 put 去掉,依旧可以实现相同的功能。

Getxcontroller生命周期

GetX 框架中,GetxController 是用于状态管理的核心类之一。它具有自己的生命周期,包括初始化、关闭等阶段。由于 GetView 是继承自 StatelessWidget,没有 initState() 方法和 dispose() 方法,所以我们可以用 GetxController 的生命周期来代替。

下面是 GetxController 的生命周期概述:

  1. onStart:在 GetX 框架中,onStart 是在控制器第一次绑定到页面或实例化时调用的方法。它被视为控制器的构造函数,并且可以用来进行一些初始化操作。例如,你可以在这里绑定其他依赖的控制器、初始化变量等。

  2. onInitonInit 是在 onStart 之后调用的方法。当控制器被绑定到页面或实例化时,会调用 onInit 方法。你可以重写 onInit 方法,在这里执行一些进一步的初始化操作,例如订阅数据流、初始化服务等。通常,将一些与组件生命周期无关的初始化工作放在这里。

  3. onReadyonReady 是在控制器初始化完成后调用的方法。它表示控制器已准备好使用。你可以重写 onReady 方法,在这里执行一些在控制器完全初始化后需要进行的操作,例如加载数据、显示UI等。注意,onReady 是在控制器的 onInit 方法完成后立即调用的。

  4. onCloseonClose 是在页面销毁或控制器不再需要时调用的方法。你可以在 onClose 方法中释放资源、取消订阅等清理操作,以避免内存泄漏。通常,你需要手动调用控制器的 close 方法来触发 onClose 方法的调用。

下面是一个示例,展示了 GetxController 的完整生命周期:

import 'package:get/get.dart';

class UserController extends GetxController {
  var name = 'Guest'.obs;

  
  void onStart() {
    super.onStart();
    print('UserController started');
  }

  
  void onInit() {
    super.onInit();
    print('UserController initialized');
  }

  
  void onReady() {
    super.onReady();
    print('UserController ready');
  }

  
  void onClose() {
    super.onClose();
    print('UserController closed');
  }

  void changeName(String newName) {
    name.value = newName;
  }
}

在上述示例中,我们定义了一个名为 UserControllerGetxController,并重写了所有的生命周期方法。每个生命周期方法都打印了相应的信息。此外,我们还定义了一个可观察变量 name 和一个修改 name 的方法 changeName

Getx国际化多语言配置

在我们使用系统自带 MaterialApp 来实现国际化配置,需要进行很多配置,而且还需要手动去依赖第三方组件,而使用 Getx 来实现国际化配置,你只需要一行代码即可实现切换。

官方文档:https://github.com/jonataslaw/getx/blob/master/README.zh-cn.md#%E5%9B%BD%E9%99%85%E5%8C%96

  1. 自定义一个语言包 language.dart

    import 'package:get/get.dart';
    
    class Messages extends Translations {
      
      Map<String, Map<String, String>> get keys => {
            'zh_CN': {
              'hello': '你好 世界',
              'title': '标题',
            },
            'en_US': {
              'hello': 'Hello World',
              'title': 'title',
            }
          };
    }
    
  2. 应用程序入口配置

    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      // This widget is the root of your application.
      
      Widget build(BuildContext context) {
        return GetMaterialApp(
          title: 'Flutter Demo',
          translations: Messages(), // 你的翻译
          locale: const Locale('zh', 'CN'), // 将会按照此处指定的语言翻译
          fallbackLocale: const Locale('en', 'US'), // 添加一个回调语言选项,以备上面指定的语言翻译不存在
          theme: ThemeData(
            colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
            useMaterial3: true,
          ),
          home: const MyHomePage(),
        );
      }
    }
    
  3. 调用语言包

    只要将 .tr 追加到指定的键上,就会使用 Get.locale 和 Get.fallbackLocale 的当前值进行翻译。

    Text('hello'.tr);
    
  4. 改变语言

    调用 Get.updateLocale(locale) 来更新语言环境,然后翻译会自动使用新的 locale,更新后所有页面都会生效。

    var locale = const Locale('zh', 'CN');
    Get.updateLocale(locale);
    

main.dart 完整代码:

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import './language.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Flutter Demo',
      translations: Messages(), // 你的翻译
      locale: const Locale('zh', 'CN'), // 将会按照此处指定的语言翻译
      fallbackLocale: const Locale('en', 'US'), // 添加一个回调语言选项,以备上面指定的语言翻译不存在
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('国际化'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('hello'.tr),
            Text('title'.tr),
            const SizedBox(height: 20),
            ElevatedButton(
                onPressed: () {
                  var locale = const Locale('zh', 'CN');
                  Get.updateLocale(locale);
                },
                child: const Text('切换到中文')),
            const SizedBox(height: 20),
            ElevatedButton(
                onPressed: () {
                  var locale = const Locale('en', 'US');
                  Get.updateLocale(locale);
                },
                child: const Text('切换到英文')),
          ],
        ),
      ),
    );
  }
}

在这里插入图片描述

切换到英文:

在这里插入图片描述

GetX GetUtils工具类

GetUtilsgetx 为我们提供一些常用的工具类库,包括值是否为空、是否是数字、是否是视频、图片、音频、PPTWordAPK、邮箱、手机号码、日期、MD5SHA1 等等。

import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final TextEditingController _editingController = TextEditingController();
  MyHomePage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('GetUtils'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Padding(
              padding: const EdgeInsets.all(10),
              child: TextField(
                controller: _editingController,
              ),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                if (GetUtils.isEmail(_editingController.text)) {
                  Get.snackbar("正确", "恭喜你, 完全正确",
                      backgroundColor: Colors.greenAccent);
                } else {
                  {
                    Get.snackbar("邮箱错误", "请输入正确的邮箱",
                        backgroundColor: Colors.pinkAccent);
                  }
                }
              },
              child: const Text('验证邮箱'),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                if (GetUtils.isPhoneNumber(_editingController.text) &&
                    _editingController.text.length == 11) {
                  Get.snackbar("正确", "恭喜你, 完全正确",
                      backgroundColor: Colors.greenAccent);
                } else {
                  {
                    Get.snackbar("手机号错误", "请输入正确的手机号",
                        backgroundColor: Colors.pinkAccent);
                  }
                }
              },
              child: const Text('验证手机号'),
            ),
          ],
        ),
      ),
    );
  }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序喵正在路上

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值