Skip to content

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 文件中。

yaml
dependencies:
  get: ^4.6.6

在需要用到的文件中导入,它将被使用。

dart
import 'package:get/get.dart';

设置应用程序入口

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,如下所示

dart
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个属性

字段属性描述
titleString弹出的标题,默认(Alert)
titlePaddingEdgeInsetsGeometry标题的内边距,默认(EdgeInsets.all(8))
titleStyleTextStyle标题的样式
middleTextString中间内容区域显示的文字
middleTextStyleTextStyle中间内容区域显示的文字样式
contentWidget弹出的内容,该值设置后middleText将无效
contentPaddingEdgeInsetsGeometry内容的内边距,默认(EdgeInsets.all(8))
onConfirmVoidCallback确认按钮回调
onCancelVoidCallback取消按钮回调
onCustomVoidCallback自定义按钮回调
cancelTextColorColor取消按钮文字的颜色
confirmTextColorColor确认按钮文字的颜色
textConfirmString确认按钮的文字
textCancelString取消按钮的文字
textCustomString自定义按钮的文字
confirmWidget确认按钮的组件
cancelWidget取消按钮的组件
customWidget自定义按钮的组件
backgroundColorColor弹出框的背景颜色
barrierDismissiblebool是否可以通过点击背景关闭弹窗
buttonColorColor按钮的文字颜色,根据按钮类型来设定不同的位置
radiusdouble弹出框的圆角大小,默认20
actionsList增加额外的子组件
onWillPopWillPopCallback拦截关闭之前做一些操作
navigatorKeyGlobalKey用于打开对话框的key

Getx snackbar提示信息

Snackbar 和toast有点相似, 如果想在应用程序中触发某些特定的事件后,需要弹出快捷消息,那么使用Snackbar 则是最佳的选择。

dart
ElevatedButton(
  onPressed: () {
    Get.snackbar("提示?", "您还有没有登录",
                 snackPosition: SnackPosition.BOTTOM);//显示位置
  },
  child: const Text("Getx snackbar")),

Snackbar属性和说明

总共38个属性

字段属性描述
titleString弹出的标题文字
messageString弹出的消息文字
colorTextColortitle和message的文字颜色
durationDurationSnackbar弹出的持续时间(默认3秒)
instantInitbool当false可以把snackbar 放在initState,默认true
snackPositionSnackPosition弹出时的位置,有两个选项【TOP,BOTTOM】默认TOP
titleTextWidget弹出标题的组件,设置该属性会导致title属性失效
messageTextWidget弹出消息的组件,设置该属性会导致messageText属性失效
iconWidget弹出时图标,显示在title和message的左侧
shouldIconPulsebool弹出时图标是否闪烁,默认false
maxWidthdoubleSnackbar最大的宽度
marginEdgeInsetsSnackbar外边距,默认zero
paddingEdgeInsetsSnackbar内边距,默认EdgeInsets.all(16)
borderRadiusdouble边框圆角大小,默认15
borderColorColor边框的颜色,必须设置borderWidth,否则无效果
borderWidthdouble边框的线条宽度
backgroundColorColorSnackbar背景颜色,默认Colors.grey.withOpacity(0.2)
leftBarIndicatorColorColor左侧指示器的颜色
boxShadowsListSnackbar阴影颜色
backgroundGradientGradient背景的线性颜色
mainButtonTextButton主要按钮,一般显示发送、确认按钮
onTapOnTap点击Snackbar事件回调
isDismissiblebool是否开启Snackbar手势关闭,可配合dismissDirection使用
showProgressIndicatorbool是否显示进度条指示器,默认false
dismissDirectionSnackDismissDirectionSnackbar关闭的方向
progressIndicatorControllerAnimationController进度条指示器的动画控制器
progressIndicatorBackgroundColorColor进度条指示器的背景颜色
progressIndicatorValueColorAnimation进度条指示器的背景颜色,Animation
snackStyleSnackStyleSnackbar是否会附加到屏幕边缘
forwardAnimationCurveCurveSnackbar弹出的动画,默认Curves.easeOutCirc
reverseAnimationCurveCurveSnackbar消失的动画,默认Curves.easeOutCirc
animationDurationDurationSnackbar弹出和小时的动画时长,默认1秒
barBlurdoubleSnackbar背景的模糊度
overlayBlurdouble弹出时的毛玻璃效果值,默认0
snackbarStatusSnackbarStatusCallbackSnackbar弹出或消失时的事件回调(即将打开、已打开、即将关闭、已关闭)
overlayColorColor弹出时的毛玻璃的背景颜色
userInputFormForm用户输入表单

BottomSheet改变主题

我们可以通过GetX 很轻松的调用bottomSheet() ,而且无需传入context ,下面我给出一个例子,使用GetX 弹出bottomSheet 并很轻松的实现切换主题 。我们可以通过Get.bottomSheet() 来显示 BottomSheet ,通过Get.back() 实现路由返回,通过Get.changeTheme(ThemeData.dark()) 切换皮肤主题,通过Get.isDarkMode判断主题样式。

dart
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属性和说明

字段属性描述
bottomsheetWidget弹出的Widget组件
backgroundColorColorbottomsheet的背景颜色
elevationdoublebottomsheet的阴影
persistentbool是否添加到路由中
shapeShapeBorder边框形状,一般用于圆角效果
clipBehaviorClip裁剪的方式
barrierColorColor弹出层的背景颜色
ignoreSafeAreabool是否忽略安全适配
isScrollControlledbool是否支持全屏弹出,默认false
useRootNavigatorbool是否使用根导航
isDismissiblebool点击背景是否可关闭,默认ture
enableDragbool是否可以拖动关闭,默认true
settingsRouteSettings路由设置
enterBottomSheetDurationDurationbottomsheet进入时的动画时间
exitBottomSheetDurationDurationbottomsheet退出时的动画时间

路由管理

GetX 为我们封装了Navigation ,无需context 可进行跳转,使用GetX 进行路由跳转非常的简单,只需要调用Get.to() 即可进行路由跳转, GetX 路由跳转简化了跳转动画设置 、动画时长定义、动画曲线设置。

Get.to()实现普通路由跳转

dart
ElevatedButton(
  onPressed: () async {
 	 Get.to(Home());
  },
  child: Text("跳转到首页")
)

Get.toNamed()跳转到命名路由

原生路由跳转

dart
Navigator.pushNamed(context, "/login");

使用Getx后

dart
Get.toNamed("/login");
Get.toNamed("/shop",arguments: {
	"id":20
});

Get.back();返回到上一级页面

原生路由跳转

dart
Navigator.of(context).pop();

使用Getx后

dart
Get.back();

Get.offAll()返回到根

原生路由跳转

dart
Navigator.of(context).pushAndRemoveUntil(
  MaterialPageRoute(builder: (BuildContext context) {
 	 return const Tabs(index: 4);
  })
, (route) => false);

使用Getx后

dart
Get.offAll( const Tabs(index: 4)); //返回到导航页面

Get.off(NextScreen())

进入下一个页面,但没有返回上一个页面的选项(用于闪屏页,登录页面等)

dart
Get.off(NextScreen());

Getx配置路由及动画

dart
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,
    );
  }
}

某一页面动画

dart
GetPage(
  name: "/registerFirst",
  page: () => const RegisterFirstPage(),
  transition: Transition.rightToLeft),
),

路由传参及收参

跳转传值

dart
Get.toNamed("/shop",arguments: {
	"id":20
});

接受数据

dart
print(Get.arguments);

Getx 中间件配置

参考文档:https://github.com/jonataslaw/getx/blob/master/README.zh-cn.md#redirect

新建shopMiddleware.dart

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');
}
}

路由配置

dart
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

dart
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。

dart
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的属性。(推荐)

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;

简单的状态管理

Get有一个简单而强大的依赖管理器,它允许你只用1行代码就能检索到与你的Bloc或Controller相同的类,无需Provider context,无需inheritedWidget。

dart
Controller controller = Get.put(Controller());
// 而不是 Controller controller = Controller();

想象一下,你已经浏览了无数条路由,现在你需要拿到一个被遗留在控制器中的数据,那你需要一个状态管理器与Provider或Get_it一起使用来拿到它,对吗?用Get则不然,Get会自动为你的控制器找到你想要的数据,而你甚至不需要任何额外的依赖关系。

dart
Controller controller = Get.find();
//是的,它看起来像魔术,Get会找到你的控制器,并将其提供给你。你可以实例化100万个控制器,Get总会给你正确的控制器。

多页面之间的数据共享

Flutter默认创建的 "计数器 "项目有100多行(含注释),为了展示Get的强大功能,我将使用 GetX 重写一个"计数器 Plus版",实现:

  • 每次点击都能改变状态
  • 在不同页面之间切换
  • 在不同页面之间共享状态
  • 将业务逻辑与界面分离

声明绑定的控制器

dart
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方法

dart
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方法

dart
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绑定数据的几种方法

方法一

dart
CountController countController = Get.put(CountController());
或者
final CountController countController = Get.find();

Obx(()=>Text("${countController.count}",style:
Theme.of(context).textTheme.headline1)),

方法二

只是绑定数据无需调用Get.put(CountController());

dart
GetX<CountController>(
init: CountController(),
builder: (controller) {
return Text(
"${controller.count}",
style: const TextStyle(color: Colors.green, fontSize: 30),
);
},
),

方法三

dart
CountController countController = Get.put(CountController());
或者
final CountController countController = Get.find();
dart
GetBuilder<CountController>(
  init: countController,
  builder: (controller) {
    return Text(
    "${controller.count}",
    style: const TextStyle(color: Colors.green, fontSize: 30),
    );
  },
)

GetX Binding

GetX 其他高级API

dart
// 给出当前页面的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
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安装

bash
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右键管理员运行,输入到

    dart
    dart pub global deactivate get_cli
    
    flutter pub global activate -s git https://github.com/jonataslaw/get_cli

    image-20240802161834809

    安装完成后提示环境变量

    image-20240802162010888

    win+r输入sysdm.cpl打开系统属性找到高级的环境变量设置

    image-20240802162337456

    image-20240802162406736

    关闭命令行窗口重新打开,你输入get或者getx查看是否安装成功

    image-20240802162712279