主题
GetX 状态与路由
一、安装
bash
flutter pub add get二、初始化
将默认的 MaterialApp 替换为 GetMaterialApp
三、状态管理
1. 定义响应式变量
只需在变量的末尾加上一个 .obs 就可将变量定义为响应式变量
dart
var count = 0.obs;响应式变量可以用在任何类型上
dart
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;2. 更新响应式变量
更新基础数据类型
dart
count.value = 2更新 List 数据类型
dart
class Controller extends GetxController {
List list1 = [1];
var count = [].obs;
increment() {
count.addAll(list1);
}
}更新 Map 数据类型某个值
dart
user.update((value) {
value?.name = "123";
});更新 Map 数据类型
dart
user({'name': 'zhangsan', 'age': 20});注意:如果没有使用 obs,则需手动调用 update() 进行更新,并且在 view 中使用 GetBuilder
dart
class Controller extends GetxController {
int count = 0;
increment() {
count++;
update();
}
}dart
//view
GetBuilder<Controller>(
builder: (_) => Text("Clicks: ${c.count}"),
)3. 状态管理器
GetX 提供了使用 Controller 来管理状态,实现一个自定义 Controller 类继承自 GetxController
dart
class Controller extends GetxController {
var count = 0.obs;
increment() => count++; // increment() => count.value = 2
}4. 注册依赖 Get.put
将状态管理器注入到 GetX 中,方便自身或子路由调用
dart
//可通过c访问存储的状态
final CounterController controller = Get.put(CounterController());
Get.put<CounterController>(CounterController(), permanent: true);
Get.put<CounterController>(CounterController, tag: "counter");permanent:是否永久,默认 false 当实例不再使用时会进行销毁,true 则会一直保留
tag:标签,用于区分同一个类不同实例
5. 异步注册依赖 Get.putAsync
dart
Get.putAsync(() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('counter', 12345);
return prefs;
});跟 put 一样,同样拥有 permanent 和 tag 参数,且作用一样。
6. 懒注入 Get.lazyPut
延迟初始化,在需要用到的时候才会初始化实例对象,即第一次 find 某一个类的时候才会进行初始化
dart
///只有当第一次使用Get.find<CounterController>时,CounterController才会被调用。
Get.lazyPut<CounterController>(() => CounterController());
Get.lazyPut<CounterController>(
() {
return CounterController();
},
tag: Math.random().toString(),
fenix: true
)7. 界面更新
在界面上使用响应式变量只需在使用变量的控件上包裹 Obx 即可实现响应式更新
dart
//先注册
final Controller c = Get.put(Controller());
//后更新
Obx(() => Text("Clicks: ${c.count}"))除了使用 Obx 实现界面数据自动刷新外,GetX 提供了多种手动方式对响应式变量进行数据变化监听
dart
///每次`count`变化时调用。
ever(count, (newValue) => print("$newValue has been changed"));
///只有在变量count在第一次被改变时才会被调用。
once(count, (newValue) => print("$newValue was changed once"));
///防DDos - 每当用户停止输入1秒时调用,例如。
debounce(count, (newValue) => print("debouce$newValue"), time: Duration(seconds: 1));
///忽略1秒内的所有变化,只有最后一次会触发回调。
interval(count, (newValue) => print("interval $newValue"), time: Duration(seconds: 1));8. 获取实例 Get.find
在子路由中,可以使用 Get.find 找到对应的控制器
dart
final Controller c = Get.find();
//通过 tag 获取
final Controller c = Get.find("counter");9. 生命周期
通知 GetX 依赖注入系统:
onInit()、onReady()、onClose()
dart
import 'package:get/get.dart';
class SplashController extends GetxController {
var opacityLevel = 0.0.obs;
@override
void onInit() {
super.onInit();
Future.delayed(const Duration(milliseconds: 200), () {
opacityLevel.value = 1.0;
});
}
@override
void onReady() {}
@override
void onClose() {}
}10. 计时器示例
dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Welcome to Flutter',
home: Home(),
);
}
}
class Home extends StatelessWidget {
@override
Widget build(context) {
// 使用Get.put()实例化Controller类,对所有子路由有效
final Controller c = Get.put(Controller());
return Scaffold(
// Use Obx(()=> to update Text() whenever count is changed.
appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
body: Center(
child: ElevatedButton(
child: Text("Go to Other"), onPressed: () => Get.to(Other()))),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add), onPressed: c.increment));
}
}
class Other extends StatelessWidget {
// 查找另一个页面正在使用的控制器并重定向到该控制器
final Controller c = Get.find();
@override
Widget build(context) {
// 访问更新后的count变量
return Scaffold(appBar: AppBar(), body: Center(child: Text("${c.count}")));
}
}
class Controller extends GetxController {
var count = 0.obs;
increment() {
count.value++;
}
}四、路由管理
1. 普通路由
dart
// 路由跳转 to
Get.to(CounterPage());
// 路由传参 arguments
Get.to(CounterPage(), arguments: count);
// 获取参数 Get.arguments
dynamic args = Get.arguments;
// 没有返回的路由跳转 off
Get.off(CounterPage());
// 进入下一个界面并取消之前的所有路由 offAll
Get.offAll(CounterPage());
// 路由返回 back
Get.back();
// 返回传参
Get.back(result: 'success');
// 获取返回参数
var data = await Get.to(CounterPage());2. 别名路由
首先创建一个自定义的类,用于统一配置路由映射关系:
dart
class RouteGet {
/// page name
static const String counter = "/counter";
static const String other = "/other";
///pages map
static final List<GetPage> getPages = [
GetPage(
name: counter,
page: () => CounterPage(),
),
GetPage(
name: other,
page: () => Other(),
),
];
}然后在 GetMaterialApp 进行 initialRoute 和 getPages 的配置,即初始页面和路由映射集合
dart
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
initialRoute: RouteGet.counter, //初始页面
getPages: RouteGet.getPages, //路由集合
theme: ThemeData(
primarySwatch: Colors.blue,
));
}
}路由跳转:使用方法与普通路由基本相同,只是方法上多了 Named
dart
// 路由跳转
Get.toNamed(RouteGet.other)
// 传参
Get.toNamed(RouteGet.other, arguments: {"name": "aaaa"})
// 接参
final dynamic args = Get.arguments;3. Bindings
Bindings 主要是配合路由进行使用,当通过 GetX 路由进入页面时,会自动调用 dependencies 方法,可以在这里进行依赖关系的注册等
dart
import 'package:get/get.dart';
import 'splash_controller.dart';
class SplashBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => SplashController());
}
}在配置路由的时候使用
dart
GetPage(
name: splashPage,
page: () => const SplashPage(),
binding: SplashBinding()),4. routingCallback
在跳转前做些事情,比如判断是否登录
dart
GetMaterialApp(
routingCallback: (routing) {
if (routing.current == '/second') {
// 如果登录。。。
}
}
)5. 路由懒加载
定义一个 Future 组件
dart//定义一个包裹组件 class ContainerAsyncRouterPage extends StatelessWidget { final Future libraryFuture; final WidgetBuilder child; ContainerAsyncRouterPage(this.libraryFuture, this.child); @override Widget build(BuildContext context) { return FutureBuilder( future: libraryFuture, builder: (c, s) { if (s.connectionState == ConnectionState.done) { if (s.hasError) { return Scaffold( appBar: AppBar(), body: Container( alignment: Alignment.center, child: Text( 'Error: ${s.error}', style: TextStyle(color: Colors.red), ), ), ); } return child.call(context); } return Scaffold( appBar: AppBar(), body: Container( alignment: Alignment.center, child: CircularProgressIndicator(), ), ); }); } }修改引入方式
dartimport 'package:flutter_application_1/pages/counterPage.dart' deferred as counter_page; import 'package:flutter_application_1/pages/other.dart' deferred as other_page; class RouteGet { /// page name static const String counter = "/counter"; static const String other = "/other"; ///pages map static final List<GetPage> getPages = [ GetPage( name: counter, page: () { return ContainerAsyncRouterPage(counter_page.loadLibrary(), (context) { return counter_page.CounterPage(); }); }, ), GetPage( name: other, page: () { return ContainerAsyncRouterPage(other_page.loadLibrary(), (context) { return other_page.Other(); }); }), ]; }
运行 flutter build web 查看打包效果
五、小部件
1. SnackBars
dart
Get.snackbar('Hi', 'i am a modern snackbar');2. Dialogs
打开一个默认的 Dialog:
dart
Get.defaultDialog(
onConfirm: () => print("Ok"),
middleText: "Dialog made in 3 lines of code"
);打开自定义的 Dialog:
dart
Get.dialog(YourDialogWidget());3. BottomSheets
Get.bottomSheet 类似于 showModalBottomSheet,但不需要 context:
dart
Get.bottomSheet(
Container(
child: Wrap(
children: <Widget>[
ListTile(
leading: Icon(Icons.music_note),
title: Text('Music'),
onTap: () => {}
),
ListTile(
leading: Icon(Icons.videocam),
title: Text('Video'),
onTap: () => {},
),
],
),
)
);六、GetUtils
GetX 还提供了很多工具方法,可以使用 GetUtils 调用,比如判断是否是邮箱,判断文件格式类型等
除此之外 GetX 还提供了一些扩展方法
dart
//检查应用程序在哪个平台上运行。
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>()七、国际化
1. 定义一个专门存放字符串 key 的类
方便做 key 值提示
dart
// 新建i18/str_res_keys.dart
class SR{
static const hello = 'hello';
}2. 新建语言资源文件
新建 i18/str_res_en.dart 与 i18/str_res_zh.dart 用于存放两种语言
dart
//str_res_en.dart
import './str_res_keys.dart';
const en_US_res = {
SR.hello: 'Hello World',
};dart
//str_res_zh.dart
import './str_res_keys.dart';
const zh_CN_res = {
SR.hello: '你好 世界',
};3. 创建一个语言资源类
创建一个语言资源类继承自 GetX 的 Translations,实现 get keys
dart
//main.dart
import 'i18/str_res_en.dart';
import 'i18/str_res_zh.dart';
class StringRes extends Translations {
@override
Map<String, Map<String, String>> get keys =>
{'zh_CN': zh_CN_res, 'en_US': en_US_res};
}4. 在 GetMaterialApp 中进行配置
dart
GetMaterialApp(
translations: StringRes(),
locale: const Locale('zh', 'CN'), //默认使用的语言
fallbackLocale: Locale('en', 'US') //没有默认语言时使用的资源
...
);5. 使用
dart
import '../i18/str_res_keys.dart';
Text(SR.hello.tr),6. 更改语言
dart
Get.updateLocale(const Locale('en', 'US'))