GetX 是 Flutter 的超轻量级强大解决方案。它快速、实用地结合了高性能状态管理、智能依赖注入和路由管理。
状态管理: GetX 的旗舰功能之一是其直观的状态管理功能。GetX 中的状态管理几乎不需要样板代码即可实现。
路线管理: GetX 提供了用于在 Flutter 应用程序内导航的 API。此 API 非常简单,所需代码较少。
依赖管理: GetX 提供了一种智能方法来管理 Flutter 应用程序中的依赖项,例如视图控制器。GetX 将从内存中删除任何当前未使用的控制器。这是您作为开发人员必须手动完成的任务,但 GetX 可以自动为您完成。
下面我们将创建一个项目, 演示Getx的使用
创建项目+启动项目
flutter create project_name
cd /project_name
flutter run
如果使用vscode 可以直接在左边侧边栏进行调试启动, Android Studio 可以点击右上角的启动按钮
进行启动
创建一个目录
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("点击++"),
);
}
此时基本页面搭建好了
打开根目录下的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的值.
CounterController
中,状态由私有变量 _x
表示。x
这个 getter 公开了 _x
的值,允许外部读取。increment()
方法,状态可以被修改(即 _x
的值递增)。下一步是进行依赖注入。依赖注入(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的值开始发生了变化
目前我们已经定义好我们的控制器, 并且在UI部分已经实例化控制器,能够调用控制器的方法, 那么下面我们将控制器里面的数据进行一个UI展示,并且希望数据更新的时候UI也能得到改变, 就和react里面使用setState,vue里面的ref一样
. 下面就看看如何实现吧
GetBuilder
是 GetX 框架中的一种用于手动控制状态更新的工具. 我们先看下如何使用吧
GetBuilder<CounterController>(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()
注入控制器
GetBuilder
的地方,通过 Get.put()
来注入控制器,确保在视图中可以访问它。GetBuilder
中,指定控制器类型。controller.update()
更新 UI
controller.update()
,这将触发 GetBuilder
重建包裹的 UI。声明响应式变量(状态) , 即把一个变量变成是 "可观察的"。
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<Type>
。
final name = Rx<String>('');
final isLogged = Rx<Bool>(false);
final count = Rx<Int>(0);
final balance = Rx<Double>(0.0);
final number = Rx<Num>(0)
final items = Rx<List<String>>([]);
final myMap = Rx<Map<String, int>>({});
// 自定义类 - 可以是任何类
final user = Rx<User>();
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;
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.
如果你想在控制器第一次被调用的那一刻启动一个方法,你不需要为此使用构造函数,使用像Get这样面向性能的包,这样做反而是糟糕的做法, 因为它偏离了控制器被创建或分配的逻辑(如果你创建了这个控制器的实例,构造函数会立即被调用,你会在控制器还没有被使用之前就填充了一个控制器,你在没有被使用的情况下就分配了内存,这绝对违背这个库的原则)。 onInit();和onClose();方法就是为此而创建的,它们会在Controller被创建,或者第一次使用时被调用,这取决于你是否使用Get.lazyPut。例如,如果你想调用你的API来填充数据,你可以忘掉老式的initState/dispose方法,只需在onInit中开始调用api,如果你需要执行任何命令,如关闭流,使用onClose()来实现。