Flutter GetX系列教程---介绍、Snackbar、Dialog、BottomSheet、Navigation、Obx

7,777 阅读12分钟

前言

本文是基于官方最新稳定版本get: ^4.3.8来进行开发

本系列课程将会以最简单的方式来进行讲解,对于零基础也可以轻松掌握,全文深入浅出,每一个章节都会有对于案例来展示效果以及如何运用。

整一个课程系列都是极简的,如果你愿意花两个小时来学习,那么你将很容易掌握GetX并且用GetX应用到企业项目中。

视频教程地址

视频教程地址

学完本系列课程你将学会:
  • 知道什么是GetX
  • 会使用Snackbar
  • 会使用Dialog
  • 会使用BottomSheet
  • 会用Navigation
  • 会用Obx响应式状态管理
  • 会使用GetXController(三种使用方式GetBuilder、事件监听、生命周期、UniqueID)
  • 语言国际化配置
  • 依赖注入
  • GetX Service
  • GetX Binding(绑定)
  • GetX获取API接口数据并显示
  • 获取存储和邮件验证
  • GetView和GetWidget
  • Get Cli 脚手架使用以及常用命令
  • GetX 中使用 GetConnect 和 StateMixin 获取 API 数据

为什么讲解此系列课程?

目前我在市面上找不到一个好的对GetX全面的教程,如果纯看官方文档去自行理解,学习周期长。而视频方面要么就是篇幅太长,学习成本高。要么就是比较零散,学起来像一只无头的苍蝇。所以我干脆直接自己出一个课程,该课程系列从简至深,方便大家系统的对GetX的认识。虽然讲解该系列课程耗费了我大量的时间,但是只要对你有帮助并且能够从中受益,这样就很好。

什么是GetX ?

GetX中文官方文档 pub地址

  • GetX 是 Flutter 上的一个轻量且强大的解决方案:高性能的状态管理、智能的依赖注入和便捷的路由管理。
  • GetX 有3个基本原则:
    • 性能: GetX 专注于性能和最小资源消耗。GetX 打包后的apk占用大小和运行时的内存占用与其他状态管理插件不相上下。如果你感兴趣,这里有一个性能测试
    • 效率: GetX 的语法非常简捷,并保持了极高的性能,能极大缩短你的开发时长。
    • 结构: GetX 可以将界面、逻辑、依赖和路由完全解耦,用起来更清爽,逻辑更清晰,代码更容易维护。

为什么使用GetX?

我们知道状态管理的框架有很多,使用原生的相对复杂,都是用ChangeNotifier来更新Widget,如果对于业务逻辑比较复杂还使用这种方式无疑是致命的。

其他的状态管理器也不错,但有其细微的差别。

  • BLoC非常安全和高效,但是对于初学者来说非常复杂,这使得人们无法使用Flutter进行开发。
  • MobX比BLoC更容易,而且是响应式的,几乎是完美的,但是你需要使用一个代码生成器,对于大型应用来说,这降低了生产力,因为你需要喝很多咖啡,直到你的代码在flutter clean之后再次准备好(这不是MobX的错,而是codegen真的很慢!)。
  • Provider使用InheritedWidget来传递相同的监听器,以此来解决上面报告的ChangeNotifier的问题,这意味着对其ChangeNotifier类的任何访问都必须在widget树内。

GetX响应式状态管理器

响应式编程可能会让很多人感到陌生,因为它很复杂,但是GetX将响应式编程变得非常简单。使用 Get 的响应式编程就像使用 setState 一样简单。

  • 你不需要创建StreamControllers.
  • 你不需要为每个变量创建一个StreamBuilder。
  • 你不需要为每个状态创建一个类。
  • 你不需要为一个初始值创建一个get。

安装

将 GetX 添加到你的 pubspec.yaml 文件中。

dependencies:
  get: ^4.3.8

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

import 'package:get/get.dart';

Snackbar介绍

如果想在应用程序中触发某些特定的事件后,需要弹出一则快捷消息,那么使用Snackbar则是最佳的选择,接下来我们看一下GetX如何来联调Snackbar来使用。

Snackbar基本使用

第一步:应用程序入口设置

当我们导入依赖后,在应用程序顶层把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"),),
      ),
    );
  }
}

第二步:调用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"),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () {
                  Get.snackbar("Snackbar 标题", "欢迎使用Snackbar");
                },
                child: Text("显示 Snackbar"))
            ],
          ),
        ),
      ),
    );
  }
}

效果展示

如果您运行了代码,那么恭喜你,你已经会用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用户输入表单

Dialog介绍

Dialog 底层其实是对AlertDialog进行了封装, 一般用于二次确认的弹出框,比如当点击某个按钮提交资料时,需要用户二次确认,以防止误操作。

Dialog使用

第一步:应用程序入口设置

当我们导入依赖后,在应用程序顶层把GetMaterialApp 作为顶层,如下所示

import 'package:flutter/material.dart';
import 'package:flutter_getx_example/DialogExample/DialogExample.dart';
import 'package:get/get.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: "GetX",
      home: DialogExample(),
    );
  }
}

第二步:调用Dialog

我们可以通过Get.defaultDialog() 来显示 dialog ,如下所示

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

class DialogExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("GetX Title"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () {
                Get.defaultDialog();
              },
              child: Text("显示 Dialog"))
          ],
        ),
      ),
    );
  }
}

效果展示

如果您运行了代码,那么恭喜你,你已经会用GetX 来展示dialog 了。你将得到下面的结果:

img

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

BottomSheet介绍

BottomSheet 是底部弹出的一个组件,常用于单选、验证码二次校验弹窗等,GetXBottomSheet底部弹出是自定义通过路由push的方法实现底部弹窗的一个效果。

BottomSheet使用

我们可以通过GetX很轻松的调用bottomSheet(),而且无需传入context,下面我给出一个例子,使用GetX弹出bottomSheet并很轻松的实现切换主题

第一步:应用程序入口设置

当我们导入依赖后,在应用程序顶层把GetMaterialApp 作为顶层,如下所示

import 'package:flutter/material.dart';
import 'package:flutter_getx_example/DialogExample/DialogExample.dart';
import 'package:get/get.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: "GetX",
      home: DialogExample(),
    );
  }
}

第二步:调用BottomSheet

我们可以通过Get.bottomSheet() 来显示 BottomSheet ,如下所示

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

class BottomSheetExample extends StatelessWidget {
  GlobalKey<NavigatorState> _navKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("GetX Title"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            ElevatedButton(onPressed: () {
              Get.bottomSheet(
                Container(
                  child: Wrap(
                    children: [
                      ListTile(
                        leading: Icon(Icons.wb_sunny_outlined),
                        title: Text("白天模式"),
                        onTap: () {
                          Get.changeTheme(ThemeData.light());
                        },
                      ),
                      ListTile(
                        leading: Icon(Icons.wb_sunny),
                        title: Text("黑夜模式"),
                        onTap: () {
                          Get.changeTheme(ThemeData.dark());
                        },
                      )
                    ],
                  ),
                )
              );
            }, child: Text("Bottom Sheet"))
          ],
        ),
      ),
    );
  }
}

效果展示

如果您运行了代码,那么恭喜你,你已经会用GetX 来展示BottomSheet 了。你将得到下面的结果:

img

BottomSheet属性和说明

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

Navigation路由跳转

使用GetX 进行路由跳转非常的简单,只需要调用Get.to()即可进行路由跳转,而系统的路由跳转需要写八行代码,这是不能忍受的事情,而且涉及到跳转动画设置动画时长定义动画曲线 等设置那就更加的复杂,而GetX为我们封装了Navigation,无需context可进行跳转,并且能很方便的使用跳转动画等。

Navigation---通过to方法进行路由跳转

第一步:应用程序入口设置

import 'package:flutter/material.dart';
import 'package:flutter_getx_example/NavigationForNamedExample/NavigationForNamedExample.dart';
import 'package:get/get.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: "GetX",
      home: NavigationForNamedExample(),
    );
  }
}

第二步:调用to方法

import 'package:flutter/material.dart';
import 'package:flutter_getx_example/NavigationExample/home.dart';
import 'package:get/get.dart';

class NavigationExample extends StatelessWidget {
  GlobalKey<NavigatorState> _navKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("GetX Navigation"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () async {
                Get.to(Home());
              },
              child: Text("跳转到首页"))
          ],
        ),
      ),
    );
  }
}

效果展示

Navigation---通过toNamed进行路由跳转

第一步:应用程序入口设置

import 'package:flutter/material.dart';
import 'package:flutter_getx_example/NavigationForNamedExample/NavigationForNamedExample.dart';
import 'package:get/get.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: "GetX",
      initialRoute: "/",
      defaultTransition: Transition.zoom,
      getPages: [
        GetPage(name: "/", page: () => MyApp()),
        GetPage(name: "/home", page: () => Home()),
        GetPage(name: "/my", page: () => My(), transition: Transition.rightToLeft)
      ],
      home: NavigationForNamedExample(),
    );
  }
}

第二步:调用toNamed

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

class NavigationForNamedExample extends StatelessWidget {
  GlobalKey<NavigatorState> _navKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("GetX NavigationForNamed"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () async {
                Get.toNamed("/my");
              },
              child: Text("跳转到首页"))
          ],
        ),
      ),
    );
  }
}

效果展示

Obx响应式状态管理

介绍

响应式编程可能会让很多人感到陌生,因为它很复杂,但是GetX将响应式编程变得非常简单。

  • 你不需要创建StreamControllers.
  • 你不需要为每个变量创建一个StreamBuilder。
  • 你不需要为每个状态创建一个类。
  • 你不需要为一个初始值创建一个get。

使用 Get 的响应式编程就像使用 setState 一样简单。

定义Obx变量的三种方式

// 第一种 使用 Rx{Type}
// 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<Type>。
// 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:flutter_getx_example/ObxCountExample/ObxCountExample.dart';
import 'package:get/get.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: "GetX",
      home: ObxCountExample(),
    );
  }
}

第二步:声明Rx变量以及改变计数器的方法

RxInt count = RxInt(0);
// var count = Rx<double>(0);
// var count = 0.obs;

void increment() {
  count++;
}

第三步:使用Obx监听值的改变

Obx(() => Text(
  "count的值为: $count",
  style: TextStyle(color: Colors.red, fontSize: 30),
)),

完整代码

import 'dart:ffi';

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

class ObxCountExample extends StatelessWidget {
  RxInt count = RxInt(0);
  // var count = Rx<double>(0);
  // var count = 0.obs;

  void increment() {
    count++;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("GetX Obx---计数器"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Obx(() => Text(
              "count的值为: $count",
              style: TextStyle(color: Colors.red, fontSize: 30),
            )),
            SizedBox(height: 20,),
            ElevatedButton(
              onPressed: () {
                increment();
              },
              child: Text("点我加1"))
          ],
        ),
      ),
    );
  }
}

效果展示

自定义类案例

第一步:应用程序入口设置

import 'package:flutter/material.dart';
import 'package:flutter_getx_example/ObxCustomClassExample/ObxCustomClassExample.dart';
import 'package:get/get.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: "GetX",
      home: ObxCustomClassExample(),
    );
  }
}

第二步:创建Teacher类

import 'package:get/get.dart';

class Teacher {

  // rx 变量
  var name = "Jimi".obs;
  var age = 18.obs;

  // 构造函数创建
  // var name;
  // var age;
  // Teacher({this.name, this.age});
}

第三步:监听Teacher状态改变


import 'package:flutter/material.dart';
import 'package:flutter_getx_example/ObxCustomClassExample/Teacher.dart';
import 'package:get/get.dart';

class ObxCustomClassExample extends StatelessWidget {

  var teacher = Teacher();

  // final teacher = Teacher(name: "Jimi", age: 18).obs;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("GetX Obx---自定义类"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Obx(() => Text(
              "我的名字是 ${teacher.name.value}",
              style: TextStyle(color: Colors.red, fontSize: 30),
            )),
            SizedBox(height: 20,),
            ElevatedButton(
              onPressed: () {
                teacher.name.value = teacher.name.value.toUpperCase();

                // teacher.update((val) {
                //   teacher.value.name = teacher.value.name.toString().toUpperCase();
                // });
              },
              child: Text("转换为大写"))
          ],
        ),
      ),
    );
  }
}

效果展示

总结

通过这个章节知道了GetX是一个高性能的状态管理、智能的依赖注入和便捷的路由管理,我们介绍了什么GetX以及如果使用,包括SnackbarDialogBottomSheetNavigationObx 等进行了一个简单的介绍,使用GetX后能极简的缩减我们的代码。