Getx基本使用
官网:https://pub.dev/packages/get
中文文档:https://github.com/jonataslaw/getx/blob/master/README.zh-cn.md
状态管理
当我们想在多个页面(组件/Widget)之间共享状态(数据),或者一个页面(组件/Widget)中的多个子组件之间共享状态(数据),这个时候我们就可以用Flutter中的状态管理来管理统一的状态(数据),实现不同组件之间的传值和数据共享。
现在Flutter的状态管理方案很多,redux、bloc、state、provider、Getx。
- provider是官方提供的状态管理解决方案,主要功能就是状态管理。
- Getx是第三方的状态管理插件,不仅具有状态管理的功能,还具有路由管理、主题管理、国际化多语言管理、Obx局部更新、网络请求、数据验证等功能,相比其他状态管理插件Getx 简单、功能强大并且高性能。
Flutter Getx介绍
GetX 是 Flutter 上的一个轻量且强大的解决方案,Getx为我们提供了高性能的状态管理、智能的依赖注入和便捷的路由管理。
GetX 有3个基本原则:
性能: GetX 专注于性能和最小资源消耗。GetX 打包后的apk占用大小和运行时的内存占用与其他状态管理插件不相上下。
效率: GetX 的语法非常简捷,并保持了极高的性能,能极大缩短你的开发时长。
结构: GetX 可以将界面、逻辑、依赖和路由完全解耦,用起来更清爽,逻辑更清晰,代码更容易维护
GetX 并不臃肿,却很轻量。如果你只使用状态管理,只有状态管理模块会被编译,其他没用到的东西都不会被编译到你的代码中。它拥有众多的功能,但这些功能都在独立的容器中,只有在使用后才会启动。
Getx有一个庞大的生态系统,能够在Android、iOS、Web、Mac、Linux、Windows和你的服务器上用同样的代码运行。 通过Get Server 可以在你的后端完全重用你在前端写的代码。
Gext安装
将 Get 添加到你的 pubspec.yaml 文件中。
dependencies:
get: ^4.6.6
在需要用到的文件中导入,它将被使用。
import 'package:get/get.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) {
//顶层MaterialApp改为GetMaterialApp
return GetMaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
....
}
}
Getx使用Dialog
我们可以通过Get.defaultDialog()
来显示 dialog
,如下所示
ElevatedButton(
onPressed: () {
Get.defaultDialog(
title: "提示信息!",
middleText: "您确定要删除吗?",
confirm: ElevatedButton(
onPressed: () {
print("确定");
// Navigator.of(context).pop();
Get.back();
},
child: const Text("确定")),
cancel: ElevatedButton(
onPressed: () {
print("取消");
Get.back();
},
child: const Text("取消")));
},
child: const Text("Getx defaultDialog")),
Dialog属性和说明
总共25个属性
字段 | 属性 | 描述 |
---|---|---|
title | String | 弹出的标题,默认(Alert) |
titlePadding | EdgeInsetsGeometry | 标题的内边距,默认(EdgeInsets.all(8)) |
titleStyle | TextStyle | 标题的样式 |
middleText | String | 中间内容区域显示的文字 |
middleTextStyle | TextStyle | 中间内容区域显示的文字样式 |
content | Widget | 弹出的内容,该值设置后middleText将无效 |
contentPadding | EdgeInsetsGeometry | 内容的内边距,默认(EdgeInsets.all(8)) |
onConfirm | VoidCallback | 确认按钮回调 |
onCancel | VoidCallback | 取消按钮回调 |
onCustom | VoidCallback | 自定义按钮回调 |
cancelTextColor | Color | 取消按钮文字的颜色 |
confirmTextColor | Color | 确认按钮文字的颜色 |
textConfirm | String | 确认按钮的文字 |
textCancel | String | 取消按钮的文字 |
textCustom | String | 自定义按钮的文字 |
confirm | Widget | 确认按钮的组件 |
cancel | Widget | 取消按钮的组件 |
custom | Widget | 自定义按钮的组件 |
backgroundColor | Color | 弹出框的背景颜色 |
barrierDismissible | bool | 是否可以通过点击背景关闭弹窗 |
buttonColor | Color | 按钮的文字颜色,根据按钮类型来设定不同的位置 |
radius | double | 弹出框的圆角大小,默认20 |
actions | List | 增加额外的子组件 |
onWillPop | WillPopCallback | 拦截关闭之前做一些操作 |
navigatorKey | GlobalKey | 用于打开对话框的key |
Getx snackbar提示信息
Snackbar 和toast有点相似, 如果想在应用程序中触发某些特定的事件后,需要弹出快捷消息,那么使用Snackbar 则是最佳的选择。
ElevatedButton(
onPressed: () {
Get.snackbar("提示?", "您还有没有登录",
snackPosition: SnackPosition.BOTTOM);//显示位置
},
child: const Text("Getx snackbar")),
Snackbar属性和说明
总共38个属性
字段 | 属性 | 描述 |
---|---|---|
title | String | 弹出的标题文字 |
message | String | 弹出的消息文字 |
colorText | Color | title和message的文字颜色 |
duration | Duration | Snackbar弹出的持续时间(默认3秒) |
instantInit | bool | 当false可以把snackbar 放在initState,默认true |
snackPosition | SnackPosition | 弹出时的位置,有两个选项【TOP,BOTTOM】默认TOP |
titleText | Widget | 弹出标题的组件,设置该属性会导致title属性失效 |
messageText | Widget | 弹出消息的组件,设置该属性会导致messageText属性失效 |
icon | Widget | 弹出时图标,显示在title和message的左侧 |
shouldIconPulse | bool | 弹出时图标是否闪烁,默认false |
maxWidth | double | Snackbar最大的宽度 |
margin | EdgeInsets | Snackbar外边距,默认zero |
padding | EdgeInsets | Snackbar内边距,默认EdgeInsets.all(16) |
borderRadius | double | 边框圆角大小,默认15 |
borderColor | Color | 边框的颜色,必须设置borderWidth,否则无效果 |
borderWidth | double | 边框的线条宽度 |
backgroundColor | Color | Snackbar背景颜色,默认Colors.grey.withOpacity(0.2) |
leftBarIndicatorColor | Color | 左侧指示器的颜色 |
boxShadows | List | Snackbar阴影颜色 |
backgroundGradient | Gradient | 背景的线性颜色 |
mainButton | TextButton | 主要按钮,一般显示发送、确认按钮 |
onTap | OnTap | 点击Snackbar事件回调 |
isDismissible | bool | 是否开启Snackbar手势关闭,可配合dismissDirection使用 |
showProgressIndicator | bool | 是否显示进度条指示器,默认false |
dismissDirection | SnackDismissDirection | Snackbar关闭的方向 |
progressIndicatorController | AnimationController | 进度条指示器的动画控制器 |
progressIndicatorBackgroundColor | Color | 进度条指示器的背景颜色 |
progressIndicatorValueColor | Animation | 进度条指示器的背景颜色,Animation |
snackStyle | SnackStyle | Snackbar是否会附加到屏幕边缘 |
forwardAnimationCurve | Curve | Snackbar弹出的动画,默认Curves.easeOutCirc |
reverseAnimationCurve | Curve | Snackbar消失的动画,默认Curves.easeOutCirc |
animationDuration | Duration | Snackbar弹出和小时的动画时长,默认1秒 |
barBlur | double | Snackbar背景的模糊度 |
overlayBlur | double | 弹出时的毛玻璃效果值,默认0 |
snackbarStatus | SnackbarStatusCallback | Snackbar弹出或消失时的事件回调(即将打开、已打开、即将关闭、已关闭) |
overlayColor | Color | 弹出时的毛玻璃的背景颜色 |
userInputForm | Form | 用户输入表单 |
BottomSheet改变主题
我们可以通过GetX 很轻松的调用bottomSheet() ,而且无需传入context ,下面我给出一个例子,使用GetX 弹出bottomSheet 并很轻松的实现切换主题 。我们可以通过Get.bottomSheet() 来显示 BottomSheet ,通过Get.back() 实现路由返回,通过Get.changeTheme(ThemeData.dark()) 切换皮肤主题,通过Get.isDarkMode判断主题样式。
ElevatedButton(
onPressed: () {
Get.bottomSheet(Container(
color: Get.isDarkMode ? Colors.black26 : Colors.white,
height: 200,
child: Column(
children: [
ListTile(
leading: Icon(Icons.wb_sunny_outlined,
color: Get.isDarkMode
? Colors.white
: Colors.black87),
onTap: () {
//切换主题
Get.changeTheme(ThemeData.light());
Get.back();
},
title: Text(
"白天模式",
style: TextStyle(
color: Get.isDarkMode
? Colors.white
: Colors.black87),
),
),
ListTile(
leading: Icon(Icons.wb_sunny,
color: Get.isDarkMode
? Colors.white
: Colors.black87),
onTap: () {
//切换主题
Get.changeTheme(ThemeData.dark());
Get.back();
},
title: Text(
"夜晚模式",
style: TextStyle(
color: Get.isDarkMode
? Colors.white
: Colors.black87),
),
)
],
),
));
},
child: const Text("Getx bottomSheet切换主题"))
BottomSheet属性和说明
字段 | 属性 | 描述 |
---|---|---|
bottomsheet | Widget | 弹出的Widget组件 |
backgroundColor | Color | bottomsheet的背景颜色 |
elevation | double | bottomsheet的阴影 |
persistent | bool | 是否添加到路由中 |
shape | ShapeBorder | 边框形状,一般用于圆角效果 |
clipBehavior | Clip | 裁剪的方式 |
barrierColor | Color | 弹出层的背景颜色 |
ignoreSafeArea | bool | 是否忽略安全适配 |
isScrollControlled | bool | 是否支持全屏弹出,默认false |
useRootNavigator | bool | 是否使用根导航 |
isDismissible | bool | 点击背景是否可关闭,默认ture |
enableDrag | bool | 是否可以拖动关闭,默认true |
settings | RouteSettings | 路由设置 |
enterBottomSheetDuration | Duration | bottomsheet进入时的动画时间 |
exitBottomSheetDuration | Duration | bottomsheet退出时的动画时间 |
路由管理
GetX 为我们封装了Navigation ,无需context 可进行跳转,使用GetX 进行路由跳转非常的简单,只需要调用Get.to() 即可进行路由跳转, GetX 路由跳转简化了跳转动画设置 、动画时长定义、动画曲线设置。
Get.to()实现普通路由跳转
ElevatedButton(
onPressed: () async {
Get.to(Home());
},
child: Text("跳转到首页")
)
Get.toNamed()跳转到命名路由
原生路由跳转
Navigator.pushNamed(context, "/login");
使用Getx后
Get.toNamed("/login");
Get.toNamed("/shop",arguments: {
"id":20
});
Get.back();返回到上一级页面
原生路由跳转
Navigator.of(context).pop();
使用Getx后
Get.back();
Get.offAll()返回到根
原生路由跳转
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (BuildContext context) {
return const Tabs(index: 4);
})
, (route) => false);
使用Getx后
Get.offAll( const Tabs(index: 4)); //返回到导航页面
Get.off(NextScreen())
进入下一个页面,但没有返回上一个页面的选项(用于闪屏页,登录页面等)
Get.off(NextScreen());
Getx配置路由及动画
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
appBarTheme: const AppBarTheme(
centerTitle: true,
)),
initialRoute: "/",
// onGenerateRoute: onGenerateRoute,
defaultTransition: Transition.rightToLeft,//全局路由动画,rightToLeft是ios风格动画
getPages:AppPage.routes,
);
}
}
某一页面动画
GetPage(
name: "/registerFirst",
page: () => const RegisterFirstPage(),
transition: Transition.rightToLeft),
),
路由传参及收参
跳转传值
Get.toNamed("/shop",arguments: {
"id":20
});
接受数据
print(Get.arguments);
Getx 中间件配置
参考文档:https://github.com/jonataslaw/getx/blob/master/README.zh-cn.md#redirect
新建shopMiddleware.dart
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
class ShopMiddleWare extends GetMiddleware {
@override
// 优先级越低越先执行
int? get priority => -1;
@override
RouteSettings redirect(String ? route){
return const RouteSettings(name: '/login');
}
}
路由配置
return GetMaterialApp(
...
initialRoute: "/",
defaultTransition: Transition.rightToLeftWithFade,
getPages: [
GetPage(name: "/", page: () => const Tabs()),
...
GetPage(
name: "/shop",
page: () => const ShopPage(),
middlewares: [ShopMiddleWare()]),
],
);
Getx状态管理
目前,Flutter有几种状态管理器。但是,它们中的大多数都涉及到使用ChangeNotifier来更新widget,这对于中大型应用的性能来说是一个很糟糕的方法。你可以在Flutter的官方文档中查看到,ChangeNotifier应该使用1个或最多2个监听器,这使得它们实际上无法用于任何中等或大型应用。
Get 并不是比任何其他状态管理器更好或更差,而是说你应该分析这些要点以及下面的要点来选择只用Get,还是与其他状态管理器结合使用。
Get不是其他状态管理器的敌人,因为Get是一个微框架,而不仅仅是一个状态管理器,既可以单独使用,也可以与其他状态管理器结合使用。
Get有两个不同的状态管理器:响应式状态管理器、简单的状态管理器。
响应式状态管理器
响应式编程可能会让很多人感到陌生,因为它很复杂,但是GetX将响应式编程变得非常简单。
你不需要创建StreamControllers
你不需要为每个变量创建一个StreamBuilder
你不需要为每个状态创建一个类
你不需要为一个初始值创建一个get。
使用 Get 的响应式编程就像使用 setState 一样简单。
让我们想象一下,你有一个名称变量,并且希望每次你改变它时,所有使用它的小组件都会自动刷新。
声明一个响应式变量三种方式
第一种 使用 Rx
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>({});
第二种是使用 Rx,规定泛型 Rx。
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>();
第三种 更实用、更简单、更可取的方法,只需添加 .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;
简单的状态管理
Get有一个简单而强大的依赖管理器,它允许你只用1行代码就能检索到与你的Bloc或Controller相同的类,无需Provider context,无需inheritedWidget。
Controller controller = Get.put(Controller());
// 而不是 Controller controller = Controller();
想象一下,你已经浏览了无数条路由,现在你需要拿到一个被遗留在控制器中的数据,那你需要一个状态管理器与Provider或Get_it一起使用来拿到它,对吗?用Get则不然,Get会自动为你的控制器找到你想要的数据,而你甚至不需要任何额外的依赖关系。
Controller controller = Get.find();
//是的,它看起来像魔术,Get会找到你的控制器,并将其提供给你。你可以实例化100万个控制器,Get总会给你正确的控制器。
多页面之间的数据共享
Flutter默认创建的 "计数器 "项目有100多行(含注释),为了展示Get的强大功能,我将使用 GetX 重写一个"计数器 Plus版",实现:
- 每次点击都能改变状态
- 在不同页面之间切换
- 在不同页面之间共享状态
- 将业务逻辑与界面分离
声明绑定的控制器
import 'package:get/get.dart';
class CounterController extends GetxController{
RxInt counter=0.obs;
void inc(){
counter.value++;
update();
}
void dec(){
counter.value--;
update();
}
}
home.dart执行inc方法
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../controllers/counter.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
//创建控制器实例
CounterController counterController = Get.put(CounterController());
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Obx(() => Text("${counterController.counter}",
style: Theme.of(context).textTheme.headlineSmall)),
const SizedBox(height: 40),
ElevatedButton(
onPressed: () {
counterController.inc();
},
child: const Text("计数器加1")),
const SizedBox(height: 40),
ElevatedButton(
onPressed: () {
Get.toNamed("/shop");
},
child: const Text("跳转到shop"))
],
),
);
}
}
Category.dart执行dec方法
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../controllers/counter.dart';
class CategoryPage extends StatefulWidget {
const CategoryPage({super.key});
@override
State<CategoryPage> createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
//获取计数器实例
CounterController counterController = Get.find();
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Obx(() => Text("${counterController.counter}",
style: Theme.of(context).textTheme.headlineSmall)),
const SizedBox(height: 40),
ElevatedButton(
onPressed: () {
counterController.dec();
},
child: const Text("计数器-1"))
],
),
);
}
}
Getx Controller绑定数据的几种方法
方法一
CountController countController = Get.put(CountController());
或者
final CountController countController = Get.find();
Obx(()=>Text("${countController.count}",style:
Theme.of(context).textTheme.headline1)),
方法二
只是绑定数据无需调用Get.put(CountController());
GetX<CountController>(
init: CountController(),
builder: (controller) {
return Text(
"${controller.count}",
style: const TextStyle(color: Colors.green, fontSize: 30),
);
},
),
方法三
CountController countController = Get.put(CountController());
或者
final CountController countController = Get.find();
GetBuilder<CountController>(
init: countController,
builder: (controller) {
return Text(
"${controller.count}",
style: const TextStyle(color: Colors.green, fontSize: 30),
);
},
)
GetX Binding
GetX 其他高级API
// 给出当前页面的args。
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>()
GetX Get Service
这个类就像一个 GetxController ,它共享相同的生命周期onInit() 、onReady() 、onClose() 。但里面没有 “逻辑”。它只是通知GetX的依赖注入系统,这个子类不能从内存中删除。所以如果你需要在你的应用程序的生命周期内对一个类实例进行绝对的持久化,那么就可以使用GetxService 。
所以这对保持你的 "服务 "总是可以被Get.find() 获取到并保持运行是超级有用的。比如ApiService, StorageService, CacheService 。
get_cli的使用
安装
提示:电脑上面需要安装git
flutter3.16.9之后推荐使用下面方法安装get-cli
dart pub global deactivate get_cli
dart pub global activate -s git https://github.com/knottx/get_cli.git --git-ref knottx
flutter3.22.2安装
dart pub global deactivate get_cli
flutter pub global activate -s git https://github.com/jonataslaw/get_cli
参考:https://github.com/jonataslaw/get_cli/issues/243
安装完成后配置环境变量
mac
vim ~/.zshrc添加以下信息,安装完成后可以看到环境变量提示
dart# Flutter的get-cli配置 export PATH="$PATH":"$HOME/.pub-cache/bin"
source ~/.zshrc让配置生效
windows
开发菜单输入cmd右键管理员运行,输入到
dartdart pub global deactivate get_cli flutter pub global activate -s git https://github.com/jonataslaw/get_cli
安装完成后提示环境变量
win+r输入sysdm.cpl打开系统属性找到高级的环境变量设置
关闭命令行窗口重新打开,你输入get或者getx查看是否安装成功