状态管理框架 Get的使用
目录
[状态管理框架 Get的使用](#状态管理框架 Get的使用)
[组件使用 defaultDialog bottomSheet snackbar](#组件使用 defaultDialog bottomSheet snackbar)
[Obx 基础使用](#Obx 基础使用)
[是时候引入GetxController, 也是Get里面的常用的](#是时候引入GetxController, 也是Get里面的常用的)
[优化控制器的使用 put](#优化控制器的使用 put)
[优化控制器的使用 懒加载lazyput](#优化控制器的使用 懒加载lazyput)
不得不说,GetX使你的代码量减少了许多, 一个obs. 一个obx. 搞定你的数据监听刷新, 给你不一样的简洁。
GetMaterialApp
Dart
return GetMaterialApp(
title: 'Flutter Demo',
getPages: MyRoutes.routes, //注册路由
// initialRoute: MyHomePage.route, //该属性, 设置有有问题, 在自定义转场动画的时候, 跳转的时候右边会黑屏
unknownRoute: MyRoutes.notFoundPage, //默认404的路由地址
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(),
);
// initialRoute: MyHomePage.route,//该属性,在自定义转场动画的时候, 跳转的时候右边会黑屏
路由的注册
Dart
class MyRoutes {
static List<GetPage> routes = [
//正常注册
GetPage(name: MySettingPage.route, page: () => const MySettingPage()),
GetPage(name: MyHomePage.route, page: () => const MyHomePage(),
children: [
//children 这里可以注册子页面, 一级, 二级, 三级, 页面都可以
GetPage(name: MyProductPage.route, page: () => const MyProductPage(),
children: [
GetPage(name: MyProductDetailPage.route, page: () => const MyProductDetailPage()),
GetPage(name: MySettingPage.route, page: () => const MySettingPage()),
GetPage(name: MySettingPage.routeID, page: () => const MySettingPage()),
]),
]),
];
}
路由的跳转
Dart
//使用路由String跳转, 这样写, 注册的时候 /MyProductPage 必须要在 MyHomePage 的children: [ 里面] 如上展示
Get.toNamed("/MyHomePage/MyProductPage");
Get.toNamed("/MyHomePage/MyProductPage/MySettingPage");
//直接跳转对应的页面
Get.to(const MyProductPage());
//使用路由跳转并传值
Get.toNamed("/MyHomePage/MyProductPage/MySettingPage?id==345");
//返回上一个页面, 并传值, 接收参数的 var result = await Get.toNamed("/MyHomePage/MyProductPage/MySettingPage?id==345");
Get.back(result: {"success": true}),
//特殊路由传值 需要定义路由: "/MySettingPage/:id";, 才能使用下方跳转传值
Get.toNamed("/MyHomePage/MyProductPage/MySettingPage/8910");
//跳转后, 消除上页面的堆栈
Get.off(const MyProductDetailPage());
Get.offNamed(MyProductDetailPage.route);
//跳转后, 消除所有的堆栈
Get.offAll(const MyProductDetailPage());
Get.offAllNamed(MyProductDetailPage.route);
middlewares的使用
一个类似拦截器的功能,可以传入多个,需要自定研究下优先级。
Dart
GetPage(name: MyMinePage.route, page: () => const MyMinePage(), middlewares: [ MyRouteAuthMiddleware()]),
class MyRouteAuthMiddleware extends GetMiddleware {
@override
RouteSettings? redirect(String? route) {
// 加入 AuthService 这里可以判断下用户是否登录, 如果true
return super.的方法
//否则去都登陆页面
Future.delayed(const Duration(seconds: 1), () => Get.snackbar("提示", "请先登录APP"));
return const RouteSettings(name: MyLoginPage.route);
}
}
组件使用 defaultDialog bottomSheet snackbar
Dart
Get.snackbar
Get.bottomSheet
Get.defaultDialog
自行调用, 没有难度
状态刷新有很多种方式
ValueBuilder
爱了, 爱了, 直接导入头文件, 只刷新该作用域的.
Dart
ValueBuilder<List<String>?>(
initialValue: const ["A", "B", "C"],
builder: (value, updateFn) {
return Column(children: [
Text("List -> $value"),
ElevatedButton(
onPressed: () {
List<String> newList = [...value!]; // 使用扩展运算符来创建列表的浅拷贝
newList.add("${newList.length}");
// newList.removeAt(1); // 移除索引为1的元素
newList.shuffle();//元素随机
updateFn(newList); // 使用新列表更新状态
},
child: const Text('ValueBuilder -> add'),
)
]);
}),
Obx 基础使用
比如在Widget, 声明一个属性, 使用的时候使用Obx(()=>{}) 包裹一下,然后在其他地方点击,修改count值就OK了, 因为增加了obs, 就成了, count.value++ 来修改值
Dart
//Int
RxInt count = 0.obs;
var count1 = 0.obs;
Obx(() => Column(
children: [
Text("int -> $count"),
Text("int -> $count1"),
])
//刷新
updateCount() {
count.value++;
count1.value++;
}
在监听list. map. 枚举 的时候要注意, 一定要从新赋值才会刷新
//刷新枚举
void updateViewState(ViewState newValue) {
viewState.value = newValue; // 更新枚举值
}
//刷新list
updateList() {
list[0] = "BMW";
list1[0] = "BMW";
}
//刷新Map
updateMap() {
map["Audi"] = "BMW";
map1["Audi"] = "BMW";
}
//刷新List中的Model
updateModels() {
models[0] = GetXModels(name: "BMW");
models1[0] = GetXModels(name: "BMW");
}
//刷新Model
updateModel() {
model.value = GetXModels(name: "BMW");
model1.value = GetXModels(name: "BMW");
}
使用的地方一地个要加上Obx()
是时候引入GetxController, 也是Get里面的常用的
GetX<MyGetxController>
可以包裹引用Controller值的地方, 进行局部刷新, 还有声明周期的回调 推荐 ☆☆☆
Dart
//要创建控制器Controller
class MyPutController extends GetxController {}
//在widget的build方法中
final MyGetxController getxController = MyGetxController();
//使用例子
ListTile(
title: const Text("update list"),
subtitle: GetX<MyGetxController>(
init: getxController,
initState: (_) {},
builder: (_) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('我要买${getxController.list.toString()}'),
Text('我要买${getxController.list1.toString()}'),
],
);
},
),
onTap: () {
//list, 要修改list里面的元素, 才会去更新UI
getxController.updateList();
},
),
优化控制器的使用 put
//在Widget的build方法里面我们注册一个controller 推荐 ☆☆☆
Dart
final MyPutController countController = Get.put<MyPutController>(MyPutController());
在使用过程中使用Obx(())包裹即可,
ListTile(
title: const Text("update Count"),
subtitle: Obx(() => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('count = ${countController.count.value}'),
Text('count = ${countController.count1.value}'),
],
)),
onTap: () {
countController.updateCount();
},
),
优化控制器的使用find
//在put过MyProductController()的Widget的build方法里面即二级页面, 三级页面....子页面
Dart
final MyProductController productController = Get.find<MyProductController>();
在使用过程中使用Obx(())包裹即可, 也可以使用GetX<MyProductController>build 也可以
ListTile(
title: Text("传值 Get.arguments; = ${details.toString()} parameters == ${parameters.toString()}"),
subtitle: Obx(() => Text(productController.myProductList.length.toString())),
onTap: () {
productController.addProduct(MyProduct(name: 'iPhone 16', description: 'APPle 设备', price: 8199));
productController.addCount();
}),
优化控制器的使用 懒加载lazyput
这里就要引出Binding的使用了
Dart
class MyGetLazyPutBinding implements Bindings {
@override
void dependencies() {
Get.lazyPut(() => MyGetLazyPutController());
}
}
// MyGetLazyPutController 上面说的controller. 必须创建, 切继承GetxController
//注册路由的时候
GetPage(name: MyLazyPutPage.route, page: () => const MyLazyPutPage(),binding: MyGetLazyPutBinding()),
//在Widget页面的时候就,不用调用Getx.put的方法, 会自动Put
//直接去find找到该Controller
final MyGetLazyPutController getLazyPutController = Get.find<MyGetLazyPutController>();
//在使用过程中使用Obx(())包裹即可, 也可以使用GetX<MyProductController>build 也可以
//建议自己撸一遍代码, 会印象更加深刻
GetView
这个更加简单, 省略put, find 的步骤, 不过还是要一个Controller的
Dart
//创建Widget的时候,需要增加一个泛型
class MyGetViewPage extends GetView<MyGetLazyPutController>
//上面的路由注册, 还是要注册一个Controller
GetPage(name: MyLazyPutPage.route, page: () => const MyLazyPutPage(),binding: MyGetLazyPutBinding()),
//在widget中, 就自带了controller的变量 ,可以翻看源码中
abstract class GetView<T> extends StatelessWidget {
const GetView({Key? key}) : super(key: key);
final String? tag = null;
T get controller => GetInstance().find<T>(tag: tag)!;
@override
Widget build(BuildContext context);
}
//就会知道, 内部帮忙find_Controller
接下来GetConnect就是网络请求的使用
GetConnect
这里跟官网的不太一样, 官网我感觉有点麻烦, 创建那么多的文件, 其实就是请求层, 数据层, 页面布局划分就好了
Dart
//1.我们先继承GetConnect 设置下你的请求相关配置
//如: baseUrl headers 以及其他设置
class MyBaseHttp extends GetConnect {
@override
void onInit() {
httpClient.baseUrl = "xxxxxx";
// 请求拦截
httpClient.addRequestModifier<void>((request) {
Map<String, String> headerMap = {
"os-type": GetPlatform.isIOS ? "ios" : "android",
"timestamp": DateTime.now().microsecondsSinceEpoch.toString(),
};
request.headers.addAll(headerMap);
return request;
});
// 响应拦截
httpClient.addResponseModifier((request, response) {
return response;
});
}
}
//2.然后继承 MyBaseHttp, 创建你的serviceController
class MyServiceController extends MyBaseHttp {
//获取数据, 自定义数据
Future<MyGetConnectModel> getContent() async {
Response response = await get('/route/external_link.json'); //这里去请求的, 有post. put. 自己看下API
if (response.statusCode == 200) {
debugPrint(response.bodyString);
return MyGetConnectModel(userId: 1, id: 2, title: 'title', body: 'body');
} else {
debugPrint(response.bodyString);
return MyGetConnectModel(userId: 1, id: 2, title: 'title', body: 'body');
}
}
}
//3.创建页面的controller
class MyGetConnectController extends GetxController {
// 获取实例
final MyServiceController serviceController = Get.find<MyServiceController>();
// model
late MyGetConnectModel getConnectModel;
//监听状态, 这里是一个枚举, 这里你也可以监听其他的,比如model的变化
Rx<ViewState> viewState = ViewState.normal.obs;
getData() async {
updateViewState(ViewState.loading);
getConnectModel = await serviceController.getContent();
updateViewState(ViewState.normal);
}
void updateViewState(ViewState newValue) {
viewState.value = newValue; // 更新枚举值
}
}
//4.注册
class MyGetContentBuinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => MyGetConnectService());
Get.lazyPut(() => MyGetConnectController());
}
}
//路由binding
GetPage(name: MyGetContentPage.route, page: () => const MyGetContentPage(),binding: MyGetContentBuinding()),
//5.创建Widget,布局
方式1: class MyGetContentPage extends GetView<MyGetConnectController> {}
方式2: class MyHomePage extends StatelessWidget{
final controller = Get.find<MyGetConnectController>();}
//数据刷新
GetX<MyGetConnectController>(initState: (state) {
controller.getData();
}, init: controller,
builder: ((_) {
debugPrint("controller.viewState.value: ${controller.viewState.value}")
switch (controller.viewState.value) {
case ViewState.loading:
return const Center(
child: CircularProgressIndicator(),
);
default:
return _buildListView(controller.getConnectModel);
}
})),
或者
Obx(()=>)
更新值的时候, 需要 controller哟
GetConnectStatemixin
Dart
//跟上面基本一致, 这里说下不同点
//创建页面控制器的时候需要增加监听的类型数据
class MyGetConnectStateMixinController extends GetxController with StateMixin<List<MyGetConnectModel>> { }
//List<MyGetConnectModel>就是我要监听的数据类型
//创建页面方式
class MyGetConnectStateMixinPage extends GetView<MyGetConnectStateMixinController> {}
也可以使用find的方式找到该 MyGetConnectStateMixinController
然后通过controller.obx 进行监听处理, 有多种状态,
final bool isLoading;
final bool isError;
final bool isSuccess;
final bool isEmpty;
final bool isLoadingMore;
final String? errorMessage;
// 是在页面的MyGetConnectStateMixinController 控制器里面进行定义的.相关代码
@override
void onInit() {
fetchList(); //获取数据
super.onInit();
}
// 拉取数据列表
Future<void> fetchList() async {
// 获取数据, 也可以处理好处理传过来,
final Response response = await connectStateMixinService.getContent(); //通过find找到该控制器 connectStateMixinService, 是继承上面的MyBaseHttp的请求类控制器
// 判断请求是否有错误
if (response.hasError) {
// 改变数据,传入错误状态,在ui中会处理这些错误
//返回状态的定义
// change(null, status: RxStatus.error(response.statusText));
// change(null, status: RxStatus.empty());
} else {
// 成功,自定义数据,改变状态为成功
List<MyGetConnectModel> dataList = [];
for (var i = 0; i < 10; i++) {
MyGetConnectModel getConnectModel = MyGetConnectModel(userId: 12312, id: 231231, title: "title", body: "body");
dataList.add(getConnectModel);
}
change(dataList, status: RxStatus.success());
}
//页面布局Widget, 根据不同的状态, 可以写不同的结果
controller.obx(
(state) {
return ListView.separated(
itemCount: state!.length,
itemBuilder: (context, index) {
final MyGetConnectModel getConnectModel = state[index];
return ListTile(
onTap: () {},
title: Text(getConnectModel.title),
trailing: Text("\$${getConnectModel.body}"),
);
},
separatorBuilder: (BuildContext context, int index) {
return const Divider();
},
);
},
onError: (error) {
return Center(
child: Text(error.toString()),
);
},
onLoading: const SizedBox(
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 10),
Text(
"疯狂加载中...",
style: TextStyle(color: Colors.blue, fontSize: 16),
),
],
),
),
onEmpty: const Center(
child: Text("没有数据"),
),
));
GetConnectDio
需要自行导入Dio的框架
把上面的 GetConnect 部分中的请求部分, 更换为Dio请求, 然后使用Rx定义,监听的变量。
Dart
//在Widget中, 通过Obx, 或者GetX<MyGetConnectController> 去刷新数据即可
class MyGetConnectDioPage extends GetView<MyGetConnectDioController>
切换主题
Dart
return Scaffold(
appBar: AppBar(
title: const Text("切换主题"),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Get.changeTheme(Get.isDarkMode ? ThemeData.light() : ThemeData.dark());
},
child: Text("是否黑色主题 -> ${Get.isDarkMode}"),
)),
);
多语言切换
Dart
class MyTranslationService extends Translations {
static Locale? get locale => Get.deviceLocale;
static const fallbackLocale = Locale('en', 'US');
@override
Map<String, Map<String, String>> get keys => {
'en_US': enUS,
'zh_Hans': zhHans,
'zh_HK': zhHK,
};
}
//enUS zhHans zhHK 要去创建文件 如:
const Map<String, String> zhHans = {
'title': '这是标题',
'login': '登录用户 @name,邮箱账号 @email',
};
GetMaterialApp下:
locale: MyTranslationService.locale,
fallbackLocale: MyTranslationService.fallbackLocale,
translations: MyTranslationService(),
// 使用
Text(
"title -> ${'title'.tr}"),
Text(
"login -> ${'login'.trParams({'name': 'xxx', 'email': 'xxx@gmail.com'})}"),
//使用tr. trParams 来处理多语言
//设置多语音
var locale = const Locale('zh', 'HK');
Get.updateLocale(locale);
常使用的Getx自带的API
Dart
Get.arguments
//给出以前的路由名称
Get.previousRoute
// 给出要访问的原始路由,例如,rawRoute.isFirst()
Get.rawRoute
// 允许从GetObserver访问Rounting API。
Get.routing
// 检查 snackbar 是否打开
Get.isSnackbarOpen
// 检查 dialog 是否打开
Get.isDialogOpen
// 检查 bottomsheet 是否打开
Get.isBottomSheetOpen
// 删除一个路由。
Get.removeRoute()
//反复返回,直到表达式返回真。
Get.until()
// 转到下一条路由,并删除所有之前的路由,直到表达式返回true。
Get.offUntil()
// 转到下一个命名的路由,并删除所有之前的路由,直到表达式返回true。
Get.offNamedUntil()
//检查应用程序在哪个平台上运行。
GetPlatform.isAndroid
GetPlatform.isIOS
GetPlatform.isMacOS
GetPlatform.isWindows
GetPlatform.isLinux
GetPlatform.isFuchsia
//检查设备类型
GetPlatform.isMobile
GetPlatform.isDesktop
//所有平台都是独立支持web的!
//你可以知道你是否在浏览器内运行。
//在Windows、iOS、OSX、Android等系统上。
GetPlatform.isWeb
// 相当于.MediaQuery.of(context).size.height,
//但不可改变。
Get.height
Get.width
// 提供当前上下文。
Get.context
// 在你的代码中的任何地方,在前台提供 snackbar/dialog/bottomsheet 的上下文。
Get.contextOverlay
// 注意:以下方法是对上下文的扩展。
// 因为在你的UI的任何地方都可以访问上下文,你可以在UI代码的任何地方使用它。
// 如果你需要一个可改变的高度/宽度(如桌面或浏览器窗口可以缩放),你将需要使用上下文。
context.width
context.height
// 让您可以定义一半的页面、三分之一的页面等。
// 对响应式应用很有用。
// 参数: dividedBy (double) 可选 - 默认值:1
// 参数: reducedBy (double) 可选 - 默认值:0。
context.heightTransformer()
context.widthTransformer()
/// 类似于 MediaQuery.of(context).size。
context.mediaQuerySize()
/// 类似于 MediaQuery.of(context).padding。
context.mediaQueryPadding()
/// 类似于 MediaQuery.of(context).viewPadding。
context.mediaQueryViewPadding()
/// 类似于 MediaQuery.of(context).viewInsets。
context.mediaQueryViewInsets()
/// 类似于 MediaQuery.of(context).orientation;
context.orientation()
///检查设备是否处于横向模式
context.isLandscape()
///检查设备是否处于纵向模式。
context.isPortrait()
///类似于MediaQuery.of(context).devicePixelRatio。
context.devicePixelRatio()
///类似于MediaQuery.of(context).textScaleFactor。
context.textScaleFactor()
///查询设备最短边。
context.mediaQueryShortestSide()
///如果宽度大于800,则为真。
context.showNavbar()
///如果最短边小于600p,则为真。
context.isPhone()
///如果最短边大于600p,则为真。
context.isSmallTablet()
///如果最短边大于720p,则为真。
context.isLargeTablet()
///如果当前设备是平板电脑,则为真
context.isTablet()
///根据页面大小返回一个值<T>。
///可以给值为:
///watch:如果最短边小于300
///mobile:如果最短边小于600
///tablet:如果最短边(shortestSide)小于1200
///desktop:如果宽度大于1200
context.responsiveValue<T>()
状态管理框架 Provider 和 Get 的Likes, 各有千秋