官网地址:https://pub.flutter-io.cn/packages/get
关于状态管理,目前主流有四个: GetX,BLoC、MobX、Provider。
BLoC 非常安全和高效,但是非常复杂,理解与运用不适合初学者。
MobX 比 BLoC 更容易,而且是响应式的,但是需要使用一个代码生成器,需要时间等待,对开发效率有影响。
BloC(全局管理,event,形式类似mvvm):
- widget 触发event 事件
- bloc 接收event 事件并作出逻辑处理
3.并把逻辑处理结果给返回出来 -
UI展示数据
所以,目前市场最常见的是Provider和GetX。
但是,目前在pub上来看,GetX可能更受青睐。
相对于provide ,我觉得GetX更有优势的地方在于:GetX 不需要上下文,突破了InheritedWidget的限制,我们可以在全局和模块间共享状态,而Provider 在遇到非父子组件的状态管理问题,需要借助别的手段(eventbus,全局,单例)。
针对context做个简单的对比:provide中路由需要对 context 的依赖。
///原始
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return NextScreen();
},
));
///provider封装后
Navigator.pushNamed(context, RouteName.nextName);
///返回
Navigator.pop(context);
///GetX
Get.to(NextScreen())
///返回
Get.back();
///打开新页面,并且用新页面替换旧页面(删除旧页面)
Get.off(NextScreen());
///打开新页面并删除之前的所有路由
Get.offAll(NextScreen());
///导航到新页面,在返回时接收返回数据
var data = await Get.to(NextScreen());
///带返回值返回前一个路由,配合上面使用
Get.back(result: 'success');
通过Snackbar的使用来感受下getx
安装
dependencies:
get:
引入
import 'package:get/get.dart';
Snackbar使用
导入依赖后,在应用程序顶层把GetMaterialApp 作为顶层,然后通过Get.snackbar() 来显示 snackbar
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: "GetX",
home: Scaffold(
appBar: AppBar(
title: Text("GetX Title"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Get.snackbar("Snackbar 标题", "欢迎使用Snackbar");
},
child: Text("显示 Snackbar"))
],
),
),
),
);
}
}
效果图
将
Get.snackbar("Snackbar 标题", "欢迎使用Snackbar");
替换成下方代码
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Have a snack!'),
),
);
运行下会报一个错误
///报错原因是 : 在Scaffold子组件里的build方法可以才可以调用Scaffold.of方法
Scaffold.of() called with a context that does not contain a Scaffold.
上门这个错误有很多解决办法,例如抽离出一个子控件,使用GlobalKey存储ScaffoldState ,但是无疑都会增加代码量。
谈一下被动状态管理
被动状体管理 :通俗的讲就是当你改变一个值,相关小控件随之变化。
现有项目中我通常是采用mvvm加provider的方式,现在看下getx的实现
class ObxCountExample extends StatelessWidget {
///声明Rx变量以及改变计数器的方法
//StringX RxString
//IntX RxInt
//MapX RxMap
//列表X RxList
//NumX RxNum
//DoubleX RxDouble
RxInt count = RxInt(0);
void increment() {
count++;
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
///使用Obx监听值的改变
Obx(() => Text(
"count的值为: $count",
style: TextStyle(color: Colors.red, fontSize: 30),
)),
SizedBox(height: 20,),
ElevatedButton(
onPressed: () {
increment();
},
child: Text("点我加1"))
],
),
);
}
}
如果是一个类里的值发生改变
class Programmer {
// rx 变量
var name = "mabo".obs;
var age = 30.obs;
}
class ObxCustomClassExample extends StatelessWidget {
var programmer = Programmer();
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Obx(() => Text(
"我的名字是 ${programmer.name.value}",
style: TextStyle(color: Colors.red, fontSize: 30),
)),
SizedBox(height: 20,),
ElevatedButton(
onPressed: () {
programmer.name.value = programmer.name.value.toUpperCase();
},
child: Text("转换为大写"))
],
),
);
}
}
上述 .obs 和 Rx([]) ,基本等价,都是obs状态管理的创建属性的方式
UI和逻辑分离-GetxController
核心思想:
定义一个继承GetxController的类: 逻辑- Controller 层
定义一个GetBuilder类:界面- *View 层
controlelr 里面调用update() 刷新UI
首先,定义控制器继承自GetxController(生命周期(onInit()),onReady()),onClose()))
import 'package:get/get.dart';
class Teacher {
// rx 变量
var name = "mabo".obs;
var age = 30.obs;
}
class MyController extends GetxController {
// 第一种
// var teacher = Teacher();
//
// void convertToUpperCase() {
// teacher.name.value = teacher.name.value.toUpperCase();
// }
// 第二种
// var teacher = Teacher(name: "Jimi", age: 18).obs;
// void convertToUpperCase() {
// teacher.update((val) {
// teacher.value.name = teacher.value.name.toString().toUpperCase();
// });
// }
// 第三种
var teacher = Teacher();
void convertToUpperCase() {
teacher.name.value = teacher.name.value.toUpperCase();
update();
}
}
之后,实例化控制器并使用
import 'package:flutter/material.dart';
import 'package:flutter_getx_example/GetXControllerExample/MyController.dart';
import 'package:get/get.dart';
class GetXControllerExample extends StatelessWidget {
// 第一种
// MyController myController = Get.put(MyController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("GetX Obx---GetXController"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// 第一种
// Obx(() => Text(
// "我的名字是 ${myController.teacher.name}",
// style: TextStyle(color: Colors.red, fontSize: 30),
// )),
// 第二种
// GetX(
// init: MyController(),
// builder: (controller) {
// return Text(
// "我的名字是 ${controller.teacher.name}",
// style: TextStyle(color: Colors.green, fontSize: 30),
// );
// },
// ),
// 第三种
GetBuilder(
init: myController,
builder: (controller) {
return Text(
"我的名字是 ${controller.teacher.name}",
style: TextStyle(color: Colors.green, fontSize: 30),
);
},
),
SizedBox(height: 20,),
ElevatedButton(
onPressed: () {
// 第一种
myController.convertToUpperCase();
// 第二种
// Get.find().convertToUpperCase();
},
child: Text("转换为大写"))
],
),
),
);
}
}
实现效果
GetxController UniqueID
开发的过程中会碰到一种情况,就是多个地方引用了同一个属性,但我只想单独更新某一个地方,那么就可以用UniqueID来进行区分。
首先,定义控制器继承自GetxController,并且定义uniqueID
import 'package:get/get.dart';
class CountController extends GetxController {
var count = 0;
void increment() {
count++;
update(['jimi_count']);
}
}
class GetXControllerUniqueIDExample extends StatelessWidget {
CountController countController = Get.put(CountController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("GetX Obx---GetXController"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
GetBuilder(
builder: (controller) {
return Text(
"计数器值为: ${controller.count}",
style: TextStyle(color: Colors.red, fontSize: 30),
);
},
),
GetBuilder(
id: 'jimi_count',
builder: (controller) {
return Text(
"计数器值为: ${controller.count}",
style: TextStyle(color: Colors.green, fontSize: 30),
);
},
),
SizedBox(height: 20,),
ElevatedButton(
onPressed: () => countController.increment(),
child: Text("增加"))
],
),
),
);
}
}
红色的没有改变,绿色的改变了
GetxService
GetView
除了状态管理、路由管理、依赖管理之外,还有网络数据的管理,有待补充。