- 作者:老汪软件技巧
- 发表时间:2024-11-18 17:04
- 浏览量:
1. 介绍
GetX 是 Flutter 的超轻量级强大解决方案。它快速、实用地结合了高性能状态管理、智能依赖注入和路由管理。
状态管理:GetX 的旗舰功能之一是其直观的状态管理功能。GetX 中的状态管理几乎不需要样板代码即可实现。
路线管理:GetX 提供了用于在 Flutter 应用程序内导航的 API。此 API 非常简单,所需代码较少。
依赖管理:GetX 提供了一种智能方法来管理 Flutter 应用程序中的依赖项,例如视图控制器。GetX 将从内存中删除任何当前未使用的控制器。这是您作为开发人员必须手动完成的任务,但 GetX 可以自动为您完成。
2. 展示使用 [状态管理]
下面我们将创建一个项目, 演示Getx的使用
创建项目+启动项目
flutter create project_name
cd /project_name
flutter run
如果使用vscode 可以直接在左边侧边栏进行调试启动, Android Studio 可以点击右上角的启动按钮 进行启动
2.1 基本页面搭建
创建一个目录
counter
--> state
counter_screen.dart
main.dart
import 'package:flutter/material.dart';
import 'package:getx_study/counter/counter_screen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: CounterScreen(),
);
}
}
counter_screen.dart
里面编写页面结构,代码里面用到的widget,将不会讲解,本文主要针对Getx的使用讲解,不明白的组件可以去浏览器搜一下. 另外代码里面也有注解.
class CounterScreen extends StatelessWidget {
const CounterScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Gex 学习'),
),
body: Center(
child: Container(
width: MediaQuery.of(context).size.width, // 盒子的宽度 为设备的宽度
height: MediaQuery.of(context).size.height, // 盒子的高度 为设备的高度
decoration: BoxDecoration(color: Colors.grey.withOpacity(0.3), ), // 盒子的背景色
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
GestureDetector(onTap: () {}, child: _buildContainer()),
GestureDetector(onTap: () {}, child: _buildContainer()),
GestureDetector(onTap: () {}, child: _buildContainer()),
GestureDetector(onTap: () {}, child: _buildContainer()),
],
),
),
),
);
}
}
接着完成了我们抽离出来的_buildContainer组件,该方法 返回一个Container组件
_buildContainer() {
return Container(
decoration: BoxDecoration(
color: const Color.fromARGB(255, 243, 207, 219),
borderRadius: BorderRadius.circular(10.0), // 圆角
boxShadow: [const BoxShadow(color: Colors.black12,offset: Offset(6.0, 2.0),blurRadius: 10.0,spreadRadius: 4.0),]), // 盒子阴影
alignment: Alignment.center,
width: 100.0,
height: 80.0,
margin: const EdgeInsets.all(10),
child: const Text("点击++"),
);
}
此时基本页面搭建好了
2.2 数据状态(state定义)
打开根目录下的pubspec.yaml文件,并填入以下内容
dependencies:
get: ^4.6.6
vscode 会自动执行pub get 命令 获取最新的依赖
在counter_controller.dart 文件中写入
import 'package:get/get_state_manager/src/simple/get_controllers.dart';
class CounterController extends GetxController {
int _x=0;
int get x=>_x;
void increment(){_x++}
}
GetxController是GetX的控制器,它帮助管理状态的更新。继承GetxController意味着你可以在这个类中使用GetX的各种功能,如依赖注入和状态管理。
在这个文件里面我们定义一个_私有变量 x , 并且提供了get方法来获取这个私有变量. 同时定义一个一个increment方法去增加私有变量_x的值.
2.3 依赖注入
下一步是进行依赖注入。依赖注入(Dependency Injection,简称 DI)是 GetX 提供的一项功能,用于将控制器(如CounterController)注入到视图层,使得它们可以在不同的地方方便地被获取和使用。
Widget build(BuildContext context) {
// 注入控制器
CounterController controller = Get.put(CounterController());
在下面的交互里面,调用controller身上的属性/方法
GestureDetector(onTap: () {controller.increment();}, child: _buildContainer()),
控制输出
我们的控制器已经创建并且初始化完毕
[GETX] Instance "CounterController" has been created
[GETX] Instance "CounterController" has been initialized
下面我们接着在控制器CounterController 的方法increment中进行print 打印,查看x的值的变化
void increment(){
print("x的值为${_x}");
_x++;
}
我们可以看到_x的值开始发生了变化
2.4 视图更新
目前我们已经定义好我们的控制器, 并且在UI部分已经实例化控制器,能够调用控制器的方法, 那么下面我们将控制器里面的数据进行一个UI展示,并且希望数据更新的时候UI也能得到改变, 就和react里面使用setState,vue里面的ref一样. 下面就看看如何实现吧
2.4.1 GetBuilder
GetBuilder是 GetX 框架中的一种用于手动控制状态更新的工具. 我们先看下如何使用吧
GetBuilder(builder: (_) => Text(
controller.x.toString(),
style: const TextStyle(fontSize: 22, color: Colors.black),
),
),
GestureDetector(onTap: () {controller.increment();},child: _buildContainer()),
但是此时我们发现点击之后并没有得到更新, 这是因为GetBuilder在状态改变时,你需要手动调用controller.update(),这样才会触发GetBuilder重建包裹的 UI。
所以我们需要在我们的counter_controller.dart文件里面在increase方法的后面添加update() 方法
class CounterController extends GetxController {
int _x = 0;
int get x => _x;
void increment() {
print("x的值为${_x}");
_x++;
update();
}
}
此时点击之后就ui也跟着发生变化.
总结一下:
使用Get.put()注入控制器
包裹需要更新的 UI
手动调用controller.update()更新 UI
2.4.2 Obx第一步 声明响应式变量
声明响应式变量(状态) , 即把一个变量变成是 "可观察的"。
1 - 第一种是使用Rx{Type} 。
// 建议使用初始值,但不是强制性的
final name = RxString('');
final isLogged = RxBool(false);
final count = RxInt(0);
final balance = RxDouble(0.0);
final items = RxList<String>([]);
final myMap = RxMap<String, int>({});
2 - 第二种是使用Rx,规定泛型Rx。
final name = Rx<String>('');
final isLogged = Rx(false);
final count = Rx(0);
final balance = Rx(0.0);
final number = Rx(0)
final items = Rx<List<String>>([]);
final myMap = Rx<Map<String, int>>({});
// 自定义类 - 可以是任何类
final user = Rx();
3 - 第三种更实用、更简单、更可取的方法,只需添加 .obs作为value的属性。
final name = ''.obs;
final isLogged = false.obs;
final count = 0.obs;
final balance = 0.0.obs;
final number = 0.obs;
final items = <String>[].obs;
final myMap = <String, int>{}.obs;
// 自定义类 - 可以是任何类
final user = User().obs;
第二步 在ui中使用响应式变量
Obx概念Obx 是 GetX 中用于实现响应式 UI 的小部件。它的主要作用是监听可观察变量(如 .obs 创建的变量)的变化,并在变化时自动更新其子树。
工作原理
可观察变量:通过将变量定义为.obs,GetX 会将其转化为可观察对象。例如,var count = 0.obs;。自动重建:当可观察变量发生变化时,Obx会自动重新构建其内部的 UI。这意味着你不需要手动调用setState来更新界面。简化代码:使用Obx可以减少样板代码,使得状态管理更加直观。
不过这里说一句,Get.find()方法, 之前我们都是通过注入依赖.但是如果我们每个文件需要使用都注册一遍的话, 可能会导致错误或不必要的性能开销。Get.put()会每次创建一个新的实例,
// 注入控制器
CounterController controller = Get.put(CounterController());
所以我们使用Get.find() 方法,GetX 提供的依赖注入机制的一部分,允许你在需要的地方轻松访问已创建的控制器。
对于使用到响应式变量的widget,我们需要使用Obx 进行一个包裹, 这样才会在变量更新的时候,该widget -> rebuild.
3. controller的生命周期钩子
如果你想在控制器第一次被调用的那一刻启动一个方法,你不需要为此使用构造函数,使用像Get这样面向性能的包,这样做反而是糟糕的做法,
因为它偏离了控制器被创建或分配的逻辑(如果你创建了这个控制器的实例,构造函数会立即被调用,你会在控制器还没有被使用之前就填充了一个控制器,你在没有被使用的情况下就分配了内存,这绝对违背这个库的原则)。
onInit();和onClose();方法就是为此而创建的,它们会在Controller被创建,或者第一次使用时被调用,这取决于你是否使用Get.lazyPut。例如,如果你想调用你的API来填充数据,你可以忘掉老式的initState/dispose方法,只需在onInit中开始调用api,如果你需要执行任何命令,如关闭流,使用onClose()来实现。
4. 总结
只更新需要的小部件。
不使用changeNotifier,状态管理器使用较少的内存(接近0mb)。
忘掉StatefulWidget! 使用Get你永远不会需要它。对于其他的状态管理器,你可能需要使用StatefulWidget来获取你的Provider、BLoC、MobX控制器等的实例。但是你有没有停下来想一想,你的appBar,你的脚手架,以及你的类中的大部分widget都是无状态的?那么如果你只能保存有状态的Widget的状态,为什么要保存整个类的状态呢?Get也解决了这个问题。创建一个无状态类,让一切都成为无状态。如果你需要更新单个组件,就用GetBuilder把它包起来,它的状态就会被维护。
真正的解耦你的项目! 控制器一定不要在你的UI中,把你的TextEditController,或者你使用的任何控制器放在你的Controller类中。
你是否需要触发一个事件来更新一个widget,一旦它被渲染?GetBuilder有一个属性 "initState",就像StatefulWidget一样,你可以从你的控制器中调用事件,直接从控制器中调用,不需要再在你的initState中放置事件。
你是否需要触发一个动作,比如关闭流、定时器等?GetBuilder也有dispose属性,只要该widget被销毁,你就可以调用事件。
仅在必要时使用流。你可以在你的控制器里面正常使用你的StreamControllers,也可以正常使用StreamBuilder,但是请记住,一个流消耗合理的内存,响应式编程很美,但是你不应该滥用它。30个流同时打开会比changeNotifier更糟糕(而且changeNotifier非常糟糕)。
更新widgets而不需要为此花费ram。Get只存储GetBuilder的创建者ID,必要时更新该GetBuilder。get ID存储在内存中的消耗非常低,即使是成千上万的GetBuilders。当你创建一个新的GetBuilder时,你实际上是在共享拥有创建者ID的GetBuilder的状态。不会为每个GetBuilder创建一个新的状态,这为大型应用节省了大量的内存。基本上你的应用程序将是完全无状态的,而少数有状态的Widgets(在GetBuilder内)将有一个单一的状态,因此更新一个状态将更新所有的状态。状态只是一个。
Get是全知全能的,在大多数情况下,它很清楚地知道从内存中取出一个控制器的时机,你不需要担心什么时候移除一个控制器,Get知道最佳的时机。