Getx介绍
状态管理
通俗的讲:当我们想在多个页面(组件/Widget)之间共享状态(数据),或者一个页面(组
件/Widget)中的多个子组件之间共享状态(数据),这个时候我们就可以用 Flutter
中的状态管理来管
理统一的状态(数据),实现不同组件之间的传值和数据共享。
现在Flutter的状态管理方案很多,redux
、bloc
、state
、provider
、Getx
。
provider
是官方提供的状态管理解决方案,主要功能就是状态管理。Getx
是第三方的状态管理插件,
不仅具有状态管理的功能,还具有路由管理、主题管理、国际化多语言管理、Obx
局部更新、网络请
求、数据验证等功能,相比其他状态管理插件 Getx
简单、功能强大并且高性能。
Getx 介绍
GetX
是 Flutter
上的一个轻量且强大的解决方案,Getx
为我们提供了高性能的状态管理、智能的依赖入和便捷的路由管理。
- `GetX` 有3个基本原则:
- 性能: `GetX` 专注于性能和最小资源消耗。`GetX` 打包后的apk占用大小和运行时的内存占用与其他状态管理插件不相上下。
- 效率: `GetX` 的语法非常简捷,并保持了极高的性能,能极大缩短你的开发时长。
- 结构: `GetX` 可以将界面、逻辑、依赖和路由完全解耦,用起来更清爽,逻辑更清晰,代码更容易维护。
- `GetX` 并不臃肿,却很轻量。如果你只使用状态管理,只有状态管理模块会被编译,其他没用到的东西都不会被编译到你的代码中。
它拥有众多的功能,但这些功能都在独立的容器中,只有在使用后才会启动。
- `Getx` 有一个庞大的生态系统,能够在 `Android` 、`iOS` 、`Web` 、`Mac` 、`Linux` 、`Windows` 和你的服务器上用同样的代码运行。
通过 `Get Server` 可以在你的后端完全重用你在前端写的代码。
官网:https://pub.dev/packages/get
中文文档:https://github.com/jonataslaw/getx/blob/master/README.zh-cn.md
Getx 中的 Dialog 以及改变主题
Getx 安装
dependencies:
get: ^4.6.5
在需要用到的文件中导入,它将被使用。
import 'package:get/get.dart';
Getx 使用 Dialog
设置应用程序入口
当我们导入依赖后,在应用程序顶层把 GetMaterialApp
作为顶层,如下所示:
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"),
),
),
);
}
}
调用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.black12 : Colors.white,
height: 200,
child: Column(
children: [
ListTile(
leading: Icon(Icons.wb_sunny_outlined,
color: Get.isDarkMode ? Colors.white : Colors.black
),
title: Text("白天模式",
style: TextStyle(
color: Get.isDarkMode
? Colors.white
: Colors.black)
),
onTap: () {
Get.changeTheme(
ThemeData.light()
);
Get.back();
},
),
ListTile(
leading: Icon(Icons.wb_sunny,
color: Get.isDarkMode ? Colors.white : Colors.black
),
title: Text("黑夜模式",
style: TextStyle(
color: Get.isDarkMode
? Colors.white
: Colors.black
)
),
onTap: () {
Get.changeTheme(ThemeData.dark());
Get.back();
},
)
],
),
)
);
},
child: const Text("Show 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 退出时的动画时间 |
调用 snackbar
Snackbar
和我们前面讲的 toast
有点相似, 如果想在应用程序中触发某些特定的事件后,需要弹出快捷消息,那么使用 Snackbar
则是最佳的选择。
我们可以通过 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"),
), // AppBar
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Get.snackbar("Snackbar 标题", "欢迎使用Snackbar");
},
child: Text("显示 Snackbar")
), // ElevatedButton
],
), // Column
), // Center
), // Scaffold
); // GetMaterialApp
}
}
Snackbar 属性和说明
字段 | 属性 | 描述 |
---|---|---|
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 | 用户输入表单 |
调用 defaultDialog
ElevatedButton(
onPressed: () {
Get.defaultDialog(
title: "提示",
middleText: "您确定退出登录?",
confirm: ElevatedButton(
onPressed: () {
print("确定");
Get.back();
},
child: const Text("确定")
),
cancel: ElevatedButton(
onPressed: () {
print("取消");
Get.back();
},
child: const Text("取消")
)
);
},
child: const Text("显示默认的Dialog")
)
Dialog 属性和说明
字段 | 属性 | 描述 |
---|---|---|
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 路由管理
GetX
为我们封装了 Navigation
,无需 context
可进行跳转,使用 GetX
进行路由跳转非常的简单,
只需要调用 Get.to()
即可进行路由跳转, GetX
路由跳转简化了跳转动画设置 、动画时长定义、动画曲线设置。
设置应用程序入口
当我们导入依赖后,在应用程序顶层把 GetMaterialApp
作为顶层,如下所示
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"),
),
),
);
}
}
调用to方法切换路由
import './Home.dart';
ElevatedButton(
onPressed: () async {
Get.to(Home());
},
child: Text("跳转到首页")
)
调用 Get.toNamed() 跳转到命名路由
Navigator.pushNamed(context, "/login");
Get.toNamed("/login");
Get.toNamed("/shop",arguments: {
"id":20
});
Get.back(); 返回到上一级页面
Navigator.of(context).pop();
Get.back();
Get.offAll(); 返回到根
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
return const Tabs(index: 4);
}
),
(route) => false
);
Get.offAll(const Tabs(index: 4));
Get.off(NextScreen());
进入下一个页面,但没有返回上一个页面的选项(用于闪屏页,登录页面等)。
Get.off(NextScreen());
Getx 配置路由以及动画
defaultTransition 可以配置默认动画
GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
appBarTheme: const AppBarTheme(
centerTitle: true,
)
),
initialRoute: "/",
defaultTransition: Transition.rightToLeftWithFade,
getPages: [
],
);
GetPage 可以配置动态路由
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import './pages/tabs.dart';
import './pages/shop.dart';
import './pages/user/login.dart';
import './pages/user/registerFirst.dart';
import './pages/user/registerSecond.dart';
import './pages/user/registerThird.dart';
void main() {
runApp(const MyApp());
}
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: "/",
defaultTransition: Transition.rightToLeftWithFade,
getPages: [
GetPage(
name: "/",
page: () => const Tabs()
),
GetPage(
name: "/login",
page: () => const LoginPage()
),
GetPage(
name: "/registerFirst",
page: () => const RegisterFirstPage(),
transition: Transition.rightToLeft
),
GetPage(
name: "/registerSecond",
page: () => const RegisterSecondPage()
),
GetPage(
name: "/registerThird",
page: () => const RegisterThirdPage()
),
GetPage(
name: "/shop",
page: () => const ShopPage()
),
],
);
}
}
Getx 路由跳转传值以及接受数据
getPages: [
...
GetPage(name: "/shop", page: () => const ShopPage()),
...
],
Get.toNamed("/shop",
arguments: {
"id":20
}
);
print(Get.arguments);
Getx 中间件配置
更多详情参考:https://github.com/jonataslaw/getx/blob/master/README.zh-cn.md#redirect
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');
}
}
GetPage 配置路由
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
一样简单。
让我们想象一下,你有一个名称变量,并且希望每次你改变它时,所有使用它的小组件都会自动刷新。
响应式状态管理器-计数器
让我们想象一下,你有一个名称变量,并且希望每次你改变它时,所有使用它的小组件都会自动刷新。 这就是你的计数变量。
int _counter = 0;
RxInt _counter = 0.obs;
Obx(() => Text("$_counter"));
这就是全部,就这么简单。
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
RxInt _counter = 0.obs;
@overrideWidget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Gex 计数器"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:',),
Obx(() => Text("$_counter"))
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){
_counter++;
},
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
声明一个响应式变量三种方式
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;
监听自定义类数据的变化
应用程序入口设置
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: MyHomePage(),
);
}
}
创建 Person 类
import 'package:get/get.dart';
class Person {
// rx 变量
RxString name = "Jimi".obs;
RxInt age = 18.obs;
}
获取类属性值以及改变值
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import './person.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var person = Person();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Getx Obx"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Obx(() => Text("我的名字是 ${person.name.value}",
style: const TextStyle(color: Colors.red, fontSize: 30),
))
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){
person.name.value = person.name.value.toUpperCase();
},
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Getx 简单的状态管理(依赖管理) GetxController
Getx 依赖管理简介
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:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(const MyApp());
}
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(
....
);
}
}
新建 CountController
import 'package:get/get.dart';
class CountController extends GetxController{
var count = 0.obs;
void inc(){
count++;
update();
}
void dec(){
count--;
update();
}
}
Home.dart 执行 inc 方法
import 'package:flutter/material.dart';
import '../../controller/count.dart';
import 'package:get/get.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
CountController countController = Get.put(CountController());
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Obx(()=>Text("${countController.count}",
style: Theme.of(context).textTheme.headline1)
),
ElevatedButton(
onPressed: (){
countController.inc();
},
child: const Text("数值+1")
)
],
),
);
}
}
Category.dart执行dec方法
你可以让Get找到一个正在被其他页面使用的Controller,并将它返回给你。
final countController= Get.find<CountController>();
或者
final CountController countController = Get.find();
import 'package:flutter/material.dart';
import '../../controller/count.dart';
import 'package:get/get.dart';
class CategoryPage extends StatefulWidget {
const CategoryPage({super.key});
@override
State<CategoryPage> createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
final CountController countController = Get.find();
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Obx(() => Text("${countController.count}",
style: Theme.of(context).textTheme.headline1)
),
ElevatedButton(
onPressed: () {
countController.dec();
},
child: const Text("数值-1")
)
],
),
);
}
}
GetxController 绑定数据的几种方法
方法1:
CountController countController = Get.put(CountController());
或者
final CountController countController = Get.find();
Obx(()=>Text("${countController.count}",
style: Theme.of(context).textTheme.headline1)),
方法2:只是绑定数据无需调用 Get.put(CountController());
GetX<CountController>(
init: CountController(),
builder: (controller) {
return Text(
"${controller.count}",
style: const TextStyle(
color: Colors.green,
fontSize: 30
),
);
},
),
方法3:
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
状态管理器的时候,往往每次都是用需要手动实例化一个控制器,这样的话基本页面 都需要实例化一次,这样就太麻烦了,而Binding
能解决上述问题,可以在项目初始化时把所有需要 进行状态管理的控制器进行统一初始化,接下来看代码演示: 在前面的文章中,我们经常使用Get.put(MyController())
来进行控制器实例的创建,这样我们就算 不使用控制器实例也会被创建,其实GetX
还提供很多创建实例的方法,可根据不同的业务来进行创 建,接下来我们简单介绍一下几个最常用的:- Get.put(): 不使用控制器实例也会被创建
- Get.lazyPut(): 懒加载方式创建实例,只有在使用时才创建
- Get.putAsync(): Get.put() 的异步版版本
- Get.create(): 每次使用都会创建一个新的实例
声明需要进行的绑定控制器类
import 'package:get/get.dart';
class BindingHomeController extends GetxController {
var count = 0.obs;
void increment() {
count++;
}
}
import 'package:get/get.dart';
class BindingMyController extends GetxController {
var count = 0.obs;
void increment() {
count++;
}
}
import 'package:get/get.dart';
class AllControllerBinding implements Bindings {
@override
void dependencies() {
// TODO: implement dependencies
Get.lazyPut<BindingMyController>(() => BindingMyController());
Get.lazyPut<BindingHomeController>(() => BindingHomeController());
}
}
在项目启动时进行初始化绑定
return GetMaterialApp(
title: "GetX",
initialBinding: AllControllerBinding(),
home: GetXBindingExample(),
);
在页面中使用状态管理器
Obx(() => Text(
"计数器的值为 ${Get.find<BindingMyController>().count}",
style: TextStyle(color: Colors.red, fontSize: 30),
)),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Get.find<BindingMyController>().increment();
},
child: Text("点击加1")
),
GetX 自定义语言包 国际化配置
在我们使用系统自带 MaterialApp
来实现国际化配置,需要进行很多配置,而且还需要手动去依赖第
三方组件,而使用 GetX
来实现国际化配置,你只需要一行代码即可实现切换,接下来我们看一下具体
实现。
import 'package:get/get.dart';
class Messages extends Translations {
@override
Map<String, Map<String, String>> get keys => {
'zh_CN': {
'hello': '你好 世界',
},
'de_DE': {
'hello': 'Hallo Welt',
}
};
}
- 应用程序入口配置
- translations: 国际化配置文件
- locale: 设置默认语言,不设置的话为系统当前语言
- fallbackLocale:添加一个回调语言选项,以备上面指定的语言翻译不存在
return GetMaterialApp(
translations: Messages(), // 你的翻译
locale: const Locale('zh', 'CN'), // 将会按照此处指定的语言翻译
fallbackLocale: const Locale('en', 'US'), // 添加一个回调语言选项,以备上面指定的语言翻译不存在
);
调用语言包
只要将 .tr 追加到指定的键上,就会使用 Get.locale 和 Get.fallbackLocale 的当前值进行翻译。
Text('hello'.tr);
改变语言
调用 Get.updateLocale(locale)
来更新语言环境。然后翻译会自动使用新的 locale
。
更新后所有页面生效
var locale = Locale('en', 'US');
Get.updateLocale(locale);
完整代码
import 'package:get/get.dart';
class Messages extends Translations {
@override
Map<String, Map<String, String>> get keys => {
'zh_CN': {
'hello': '你好 世界',
},
'de_DE': {
'hello': 'Hallo Welt',
}
};
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import './language/message.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
translations: Messages(), // 你的翻译
locale: const Locale('zh', 'CN'), //设置默认语言
fallbackLocale: const Locale('en', 'US'), // 添加一个回调语言选项,以备上面指定
的语言翻译不存在
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Title'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('title'.tr),
const SizedBox(),
Text('hello'.tr),
ElevatedButton(
onPressed: () {
var locale = const Locale('zh', 'CN');
Get.updateLocale(locale);
},
child: const Text("切换到中文")
),
const SizedBox(
height: 20,
),
ElevatedButton(
onPressed: () {
var locale = const Locale('en', 'US');
Get.updateLocale(locale);
},
child: const Text("切换到英文")
),
],
),
),
);
}
}
GetX GetConnect(了解)
GetConnect可以便捷的通过 http
或 websockets
进行前后台通信。
你能轻松的通过 extend GetConnect
就能使用 GET
/ POST
/ PUT
/ DELETE
/ SOCKET
方法与你的 Rest API
或
websockets
通信。
默认配置
你能轻松的通过 extend GetConnect
就能使用 GET
/ POST
/ PUT
/ DELETE
/ SOCKET
方法与你的 Rest API
或 websockets
通信。
class UserProvider extends GetConnect {
// Get request
Future<Response> getUser(int id) => get('http://youapi/users/$id');
// Post request
Future<Response> postUser(Map data) => post('http://youapi/users', body:
data);
// Post request with File
Future<Response<CasesModel>> postCases(List<int> image) {
final form = FormData({
'file': MultipartFile(image, filename: 'avatar.png'),
'otherFile': MultipartFile(image, filename: 'cover.png'),
});
return post('http://youapi/users/upload', form);
}
GetSocket userMessages() {
return socket('https://yourapi/users/socket');
}
}
自定义配置
GetConnect
具有多种自定义配置。你可以配置 base Url
,配置响应,配置请求,添加权限验证,甚至
是尝试认证的次数,除此之外,还可以定义一个标准的解码器,该解码器将把您的所有请求转换为您的
模型,而不需要任何额外的配置。
class HomeProvider extends GetConnect {
@override
void onInit() {
// All request will pass to jsonEncode so CasesModel.fromJson()
httpClient.defaultDecoder = CasesModel.fromJson;
httpClient.baseUrl = 'https://api.covid19api.com';
// baseUrl = 'https://api.covid19api.com'; // It define baseUrl to
// Http and websockets if used with no [httpClient] instance
// It's will attach 'apikey' property on header from all requests
httpClient.addRequestModifier((request) {
request.headers['apikey'] = '12345678';
return request;
});
// Even if the server sends data from the country "Brazil",
// it will never be displayed to users, because you remove
// that data from the response, even before the response is delivered
httpClient.addResponseModifier<CasesModel>((request, response) {
CasesModel model = response.body;
if (model.countries.contains('Brazil')) {
model.countries.remove('Brazilll');
}
});
httpClient.addAuthenticator((request) async {
final response = await get("http://yourapi/token");
final token = response.body['token'];
// Set the header
request.headers['Authorization'] = "$token";
return request;
});
//Autenticator will be called 3 times if HttpStatus is
//HttpStatus.unauthorized
httpClient.maxAuthRetries = 3;
}
}
@override
Future<Response<CasesModel>> getCases(String path) => get(path);
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
。
定义并使用 ApiService
import 'package:get/get.dart';
class ApiService extends GetxService {
String api() {
return "https://reqres.in/api/users?page=2";
}
}
import './services/api.dart';
void main() async{
await initServices();
runApp(const MyApp());
}
Future<void> initServices() async {
print("初始化服务");
await Get.putAsync(() async => ApiService());
print("所有服务启动");
}
import './services/api.dart';
Get.find<ApiService>().api()
定义并使用 StorageService
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
class StorageService extends GetxService {
Future<void> getCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int count = (prefs.getInt("counter") ?? 0) + 1;
print("count 的值为: $count");
await prefs.setInt("counter", count);
}
}
注册服务
import './services/api.dart';
import './services/storage.dart';
void main() async{
await initServices();
runApp(const MyApp());
}
Future<void> initServices() async {
print("初始化服务");
await Get.putAsync(() async => ApiService());
await Get.putAsync(() async => StorageService());
print("所有服务启动");
}
调用服务
import './services/storage.dart';
Get.find<StorageService>().getCounter();
GetView 介绍 以及 GetxController 生命周期
GetView
只是对已注册的 Controller
有一个名为 controller
的 getter
的 const Stateless
的
Widget
,如果我们只有单个控制器作为依赖项,那我们就可以使用 GetView
,而不是使用
StatelessWidget
,并且避免了写 Get.Find()
。
GetView如何使用
GetView
的使用方法非常简单,只是要将你的视图层继承自 GetView
并传入需要注册的控制器并 Get.put()
即可,我们来看下代码演示
GetView 结合 GetxController 使用
import 'package:get/get.dart';
class CountController extends GetxController{
var count = 0.obs;
@override
void onInit() {
super.onInit();
print("onInit");
}
@override
void onReady() {
super.onReady();
print("onReady");
}
@override
void onClose() {
print("onClose");
}
void inc(){
count++;
update(['first_count']);
}
void dec(){
count--;
update();
}
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../controller/count.dart';
class ShopPage extends GetView<CountController> {
const ShopPage({super.key});
@override
Widget build(BuildContext context) {
//如果第一次使用还需要put
Get.put(CountController());
return Scaffold(
appBar: AppBar(
title: const Text("shop"),
),
body: Center(
child: Column(
children: [
Obx(()=>Text("${controller.count}")),
ElevatedButton(
onPressed: (){
controller.inc();
},
child: const Text("加1")
)
],
),
),
);
}
}
GetView Binding 结合 GetxController 使用
import 'package:get/get.dart';
class shopController extends GetxController{
var count = 0.obs;
@override
void onInit() {
super.onInit();
print("onInit");
}
@override
void onReady() {
super.onReady();
print("onReady");
}
@override
void onClose() {
print("onClose");
}
void inc(){
count++;
update(['first_count']);
}
}
import 'package:get/get.dart';
import '../controllers/shop.dart';
class ShopBinding implements Bindings{
@override
void dependencies() {
// TODO: implement dependencies
Get.lazyPut<ShopController>(() => ShopController());
}
}
import 'package:get/get.dart';
import '../pages/tabs.dart';
import '../pages/shop.dart';
import '../middlewares/shopMiddleware.dart';
import "../binding/shop.dart";
class AppPage {
static final routes = [
GetPage(
name: "/",
page: () => const Tabs()
),
GetPage(
name: "/shop",
page: () => const ShopPage(),
binding: ShopBinding(),
middlewares: [
ShopMiddleWare()
]
),
];
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/shop.dart';
class ShopPage extends GetView<ShopController> {
const ShopPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Title'),
),
body: Center(
child: Obx(() {
return Text("${controller.counter}");
})
),
);
}
}
GetX GetUtils
GetUtils
是 getx
为我们提供一些常用的 工具类库
,包括 值是否为空
、是否是数字
、是否是视频
、图片
、音频
、PPT
、Word
、APK
、邮箱
、手机号码
、日期
、MD5
、SHA1
等等。
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
var textFieldController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Title'),
), // AppBar
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(20),
child: TextField(
controller: textFieldController,
),
), // Padding
const SizedBox(height: 10,),
Padding(
padding: const EdgeInsets.all(10),
child: ElevatedButton(
child: const Text("判断是否是邮箱"),
onPressed: () async {
if (GetUtils.isEmail(textFieldController.text)) {
Get.snackbar("正确", "恭喜你, 完全正确",
backgroundColor: Colors.greenAccent
);
} else {
Get.snackbar(
"邮箱错误",
"请输入正确的邮箱",
backgroundColor: Colors.pink
);
}
},
), // ElevatedButton
), // Padding
Padding(
padding: const EdgeInsets.all(10),
child: ElevatedButton(
child: const Text("判断是否是手机号"),
onPressed: () async {
if (GetUtils.isPhoneNumber(textFieldController.text)) {
Get.snackbar("正确", "恭喜你, 完全正确",
backgroundColor: Colors.greenAccent
);
} else {
Get.snackbar(
"手机号错误",
"请输入正确的手机号",
backgroundColor: Colors.pink
);
}
},
), // ElevatedButton
), // Padding
Padding(
padding: EdgeInsets.all(10),
child: ElevatedButton(
child: Text("判断是否是IPv4"),
onPressed: () async {
if (GetUtils.isIPv4(textFieldController.text)) {
Get.snackbar("正确", "恭喜你, 完全正确",
backgroundColor: Colors.greenAccent
);
} else {
Get.snackbar(
"地址错误",
"请输入正确的IPv4地址",
backgroundColor: Colors.pink
);
}
},
), // ElevatedButton
), // Padding
],
), // Column
), // Center
); // Scaffold
}
}