【Flutter】GetX

前言

状态管理 / 路由管理 / 依赖管理

这三部分之间存在联系

参考文章

建议看官网文章,很详细 ,pub.dev搜索get
pub.dev的文档
状态管理文章相关链接

状态管理

案例

实现一个计算器,运用GetX去管理它

构建界面

构建一个计算器界面

yaml 复制代码
flutter_screenutil: ^5.9.0
dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

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

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ScreenUtilInit(
      builder: (_, child) {
        return MaterialApp(
          debugShowCheckedModeBanner: false,
          home: child,
        );
      },
      child: Main(),
    );
  }
}

class Main extends StatelessWidget {
  final Color operatorColor = const Color.fromRGBO(93, 93, 93, 1);
  final Color numberColor = const Color.fromRGBO(119, 119, 119, 1);
  final Color operatorColorTwo = const Color.fromRGBO(242, 163, 60, 1);
  final Color borderColor = const Color.fromRGBO(76, 76, 76, 1);

  const Main({super.key});

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Column(
          children: [
            buildUserButton(
              text: '0',
              bgColor: operatorColor,
              width: 1.sw,
              alignment: Alignment.centerRight,
              fontSize: 60.r,
              padding: EdgeInsets.symmetric(horizontal: 25.r),
            ),
            Row(
              children: [
                buildUserButton(text: 'AC', bgColor: operatorColor),
                buildUserButton(text: '±', bgColor: operatorColor),
                buildUserButton(text: '%', bgColor: operatorColor),
                buildUserButton(text: '÷', bgColor: operatorColorTwo),
              ],
            ),
            Row(
              children: [
                buildUserButton(text: '7', bgColor: numberColor),
                buildUserButton(text: '8', bgColor: numberColor),
                buildUserButton(text: '9', bgColor: numberColor),
                buildUserButton(text: '×', bgColor: operatorColorTwo),
              ],
            ),
            Row(
              children: [
                buildUserButton(text: '4', bgColor: numberColor),
                buildUserButton(text: '5', bgColor: numberColor),
                buildUserButton(text: '6', bgColor: numberColor),
                buildUserButton(text: '-', bgColor: operatorColorTwo),
              ],
            ),
            Row(
              children: [
                buildUserButton(text: '1', bgColor: numberColor),
                buildUserButton(text: '2', bgColor: numberColor),
                buildUserButton(text: '3', bgColor: numberColor),
                buildUserButton(text: '+', bgColor: operatorColorTwo),
              ],
            ),
            Row(
              children: [
                buildUserButton(
                  text: '0',
                  bgColor: numberColor,
                  width: 1.sw / 2,
                ),
                buildUserButton(text: '.', bgColor: numberColor),
                buildUserButton(text: '=', bgColor: operatorColorTwo),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget buildUserButton({
    required String text,
    required Color bgColor,
    double? width,
    Alignment? alignment,
    double? fontSize,
    EdgeInsetsGeometry? padding,
  }) {
    return Container(
      alignment: alignment ?? Alignment.center,
      width: width ?? 1.sw / 4,
      height: 1.sw / 4,
      decoration: BoxDecoration(
        color: bgColor,
        border: Border.all(color: borderColor),
      ),
      padding: padding ?? EdgeInsets.all(10.r),
      child: Text(
        text,
        style: TextStyle(
          color: const Color.fromRGBO(235, 235, 235, 1),
          fontSize: fontSize ?? 30.r,
        ),
      ),
    );
  }
}

采用GetX实现

引入getX

yaml 复制代码
get: ^4.6.6

.obs 和 Obx(() => View())

计算器输入数字

说明:需要跟踪的变量加上后缀.obs。这样的写法,我说明一下。其实是源于dart语言的extension,即扩展。比如:对文本进行扩展,那么文本类型的数据就会有你定义的功能。见下

dart 复制代码
extension MyStringExtension on String {
  String get sayHello {
    String words = "$this say hello!!!";
    return words;
  }
}

void main() {
  print("阿笙".sayHello);
  /// 阿笙 say hello!!!
}

同样的.obs,就是对各种数据类型进行扩展

显然,不止文本一个,还有很多,这里一一列举/使用,可以参考官网的说明文档

String / bool / double / int 这四个写法类似。

响应式变量相关
  • Rx{类型}
  • 传入泛型
  • 利用扩展函数(推荐👍)
dart 复制代码
  /// 文本类型
  final stringType1 = RxString('');
  final stringType2 = Rx<String>('');
  final stringType3 = ''.obs;
dart 复制代码
            Obx(() {
              return Text(stringType1.value);
              // return Text(stringType2.value);
              // return Text(stringType3.value);
            }),
            ElevatedButton(
              onPressed: () {
                stringType1.value = DateTime.timestamp().toString();
                // stringType2.value = DateTime.timestamp().toString();
                // stringType3.value = DateTime.timestamp().toString();
              },
              child: const Text("update"),
            )

只需在需要变化的界面中,嵌套Obx()

这里提一句Obx()是自动刷新界面,即当响应式变量变化时,重构包裹内的界面;区别于之后会提到的GetBuilder(),GetBuilder()是手动更新界面。具体使用哪一个,请根据应用场景。

写法都类似

dart 复制代码
  /// 布尔
  final boolType1 = RxBool(true);
  final boolType2 = Rx<bool>(true);
  final boolType3 = true.obs;

  /// 整型
  final intType1 = RxInt(0);
  final intType2 = Rx<int>(0);
  final intType3 = 0.obs;

  /// 浮点
  final doubleType1 = RxDouble(0.0);
  final doubleType2 = Rx<double>(0.0);
  final doubleType3 = 0.0.obs;
dart 复制代码
Obx(() {
              return Checkbox(
                value: boolType3.value,
                onChanged: (v) {},
              );
            }),
            Obx(() {
              return Text("${intType3.value}");
            }),
            Obx(() {
              return Text("${doubleType3.value}");
            }),
            ElevatedButton(
              onPressed: () {
                boolType3.value = !boolType3.value;
                intType3.value += 1;
                doubleType3.value += 0.1;
              },
              child: const Text("update"),
            )

List的写法稍有不同

dart 复制代码
  /// 列表
  final listType1 = RxList<String>(<String>[]);
  final listType3 = <String>[].obs;
dart 复制代码
Obx(() {
              return ListView.builder(
                shrinkWrap: true,
                itemBuilder: (ctx, index) {
                  return Text(listType1.elementAt(index));
                  // return Text(listType3.elementAt(index));
                },
                itemCount: listType1.length,
                // itemCount: listType3.length,
              );
            }),
            ElevatedButton(
              onPressed: () {
                listType1.add(DateTime.timestamp().toString());
                // listType3.add(DateTime.timestamp().toString());
              },
              child: const Text("add"),
            )

第二种申明方式

dart 复制代码
            Obx(() {
              return ListView.builder(
                shrinkWrap: true,
                itemBuilder: (ctx, index) {
                  return Text(listType2.value.elementAt(index));
                  // return Text(listType3.elementAt(index));
                },
                itemCount: listType2.value.length,
                // itemCount: listType3.length,
              );
            }),
            ElevatedButton(
              onPressed: () {
                listType2.update((val) {
                  val?.add(DateTime.timestamp().toString());
                });
                // listType3.add(DateTime.timestamp().toString());
              },
              child: const Text("add"),
            )

Map响应式变量,用法也各有不同,写的不对的话,Obx()内的界面不会更新

dart 复制代码
  /// 键值对
  final mapType1 = RxMap<String, int>({});
  final mapType2 = Rx<Map<String, int>>({});
  final mapType3 = <Map<String, int>>{}.obs;
dart 复制代码
/// RxMap<String, int>({})
            Obx(() {
              return Text("${mapType1["count"] ?? "无"}");
            }),
            ElevatedButton(
              onPressed: () {
                if (mapType1.isEmpty) {
                  mapType1["count"] = 0;
                }
                mapType1["count"] = mapType1["count"]! + 1;
              },
              child: const Text("add"),
            ),

            /// Rx<Map<String, int>>({})
            Obx(() {
              return Text("${mapType2.value["count"] ?? "无"}");
            }),
            ElevatedButton(
              onPressed: () {
                if (mapType2.value.isEmpty) {
                  mapType2.value["count"] = 0;
                }
                mapType2.update((val) {
                  mapType2.value["count"] = mapType2.value["count"]! + 1;
                });
              },
              child: const Text("add"),
            ),

            /// <Map<String, int>>{}.obs
            Obx(() {
              return Text("${mapType3.singleOrNull?["count"] ?? "无"}");
            }),
            ElevatedButton(
              onPressed: () {
                if (mapType3.isEmpty) {
                  mapType3.add({"count": 0});
                }
                mapType3.update((value) {
                  mapType3.single["count"] = mapType3.single["count"]! + 1;
                });
              },
              child: const Text("add"),
            )

申明响应式对象,使用update更新

dart 复制代码
  /// 对象
  final cusClassType1 = Rx<CustomClass>(CustomClass(""));
  final cusClassType2 = CustomClass("").obs;
dart 复制代码
Obx(() {
              return Text(cusClassType1.value.property);
              // return Text(cusClassType2.value.property);
            }),
            ElevatedButton(
              onPressed: () {
                cusClassType1.update((val) {
                  val?.property += "hello\n";
                });
                // cusClassType2.update((val) {
                //   val?.property += "hello\n";
                // });
              },
              child: const Text("say hello"),
            )

输入数字

继续案例

在声明好响应式变量后,定义点击事件

dart 复制代码
  void numberOperator(String value) {
    result.value += value;
  }

当点击数字时,会使Obx()包裹的界面重构

GetX

除了Obx,来自动刷新布局,还可以采用GetX,同样是自动刷新,但需要GetxController

先定义一个子类CalculatorController,并将之前逻辑的部分,都放进这个CalculatorController中

dart 复制代码
import 'package:get/get.dart';

class CalculatorController extends GetxController {
  final result = "".obs;
  int get resultLength => result.value.length <= 9 ? 9 : result.value.length;
  String get text => result.value.isEmpty ? "0" : result.value;

  void numberOperator(String value) {
    result.value += value;
  }
}
dart 复制代码
            GetX<CalculatorController>(
              init: controller,
              builder: (logic) {
                return buildCalculatorBlock(
                  text: logic.text,
                  bgColor: operatorColor,
                  width: 1.sw,
                  alignment: Alignment.centerRight,
                  fontSize: (60 * 9 / logic.resultLength).r,
                  padding: EdgeInsets.symmetric(horizontal: 25.r),
                );
              },
            ),

运行之后,点击数字,也能自动刷新

相比ObX,他有初始化状态,可以将逻辑部分归类到GetXController中

GetBuilder & update

除了Obx和GetX这两种自动刷新,还有手动刷新的方式,那就是采用GetBuilder

之前在GetX中创建好Controller,在这里仍然需要使用

鼠标点在需要包裹的Widget中,按下【option】和【回车】,就能看到

同样需要初始化controller,因为只需要刷新上面计算的结果,所以,只需要包裹需要刷新的部分 ,这很重要。

看我上面的截图,是可以输入数字的,但是,这里还需要一步,你们才能刷新。这就是为什么GetBuilder是手动刷新了。

如果不调用,需要刷新的界面不会刷新

同时可以再简化,因为obs响应式变量是配合自动刷新的GetX和ObX来使用的,在这里,既然都手动刷新了,那可以不采用响应式变量,当然你也可以留着。

改为普通的String,通过配合update,结果也能刷新。

只有你点击,即调用update,才会刷新对应的布局

GetBuilder 指定id 和 回调函数

GetBuilder不仅有statefulWidget那种生命周期回调函数,还可以可以指定id,指定特定的界面刷新数据

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get_state_manager/src/simple/get_state.dart';
import 'controller.dart';

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

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: Main());
  }
}

class Main extends StatelessWidget {
  final controller = Controller();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            GetBuilder<Controller>(
              init: controller,
              initState: (_) {
                controller.addLog("initState");
              },
              didChangeDependencies: (_) {
                controller.addLog("didChangeDependencies");
              },
              didUpdateWidget: (_, __) {
                controller.addLog("didUpdateWidget");
              },
              dispose: (_) {
                controller.addLog("dispose");
              },
              builder: (logic) {
                return Column(
                  mainAxisSize: MainAxisSize.min,
                  children: logic.log.map((e) => Text(e)).toList(),
                );
              },
            ),
            const Divider(),
            GetBuilder<Controller>(
              id: 'mine',
              init: controller,
              builder: (logic) {
                return Column(
                  mainAxisSize: MainAxisSize.min,
                  children: logic.log.map((e) => Text(e)).toList(),
                );
              },
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                  onPressed: () {
                    controller.updateLog();
                  },
                  child: const Text("刷新 update"),
                ),
                ElevatedButton(
                  onPressed: () {
                    controller.updateMineLog();
                  },
                  child: const Text("刷新mine update(['mine'])"),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
dart 复制代码
import 'package:get/get.dart';

class Controller extends GetxController {
  List<String> log = [];

  addLog(String logContent) {
    log.add(logContent);
  }

  updateLog() {
    log.add("update - ${DateTime.now().toIso8601String()}");
    update();
  }

  updateMineLog() {
    update(['mine']);
  }
}
GetxController的回调函数

可以看到initState要优先于onInit,接着当界面加载出来之后,再会执行到onReady,当Get.delete掉GetxController,最后会调用到onClose。当然之后有了依赖管理和绑定后,无需去删除GetxController

所以有了这三个(onInit / onReady / onClose),就能完成大部分功能了。GetBuilder里面的回调不是必须使用

  • onInit一般用于初始化数据,界面还没有出来时
  • 当界面首次出来后,会回调onReady
  • 当你使用了,例如流这种,需要销毁的控制器,页面关闭后,都要在onClose中销毁
dart 复制代码
import 'package:get/get.dart';

class Controller extends GetxController {
  List<String> log = [];

  addLog(String logContent) {
    log.add(logContent);
  }

  updateLog() {
    log.add("update - ${DateTime.now().toIso8601String()}");
    update();
  }

  @override
  void onInit() {
    log.add("onInit");
    super.onInit();
  }

  @override
  void onReady() {
    log.add("onReady");
    update();
    super.onReady();
  }

  @override
  void onClose() {
    print("controller - onClose");
    super.onClose();
  }
}
dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get_state_manager/src/simple/get_state.dart';
import 'package:get/instance_manager.dart';
import 'controller.dart';

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

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: Main());
  }
}

class Main extends StatelessWidget {
  final controller = Controller();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            GetBuilder<Controller>(
              init: controller,
              initState: (_) {
                controller.addLog("initState");
              },
              didChangeDependencies: (_) {
                controller.addLog("didChangeDependencies");
              },
              didUpdateWidget: (_, __) {
                controller.addLog("didUpdateWidget");
              },
              dispose: (_) {
                controller.addLog("dispose");
              },
              builder: (logic) {
                return Column(
                  mainAxisSize: MainAxisSize.min,
                  children: logic.log.map((e) => Text(e)).toList(),
                );
              },
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                  onPressed: () {
                    Get.delete<Controller>();
                    // controller.updateLog();
                  },
                  child: const Text("刷新 update"),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
GetxController的爹 - SuperController

能监听到更多的事件。例如我将应用切到后台,再切回来,可以监听到,我可以做刷新界面数据的操作

又比如:我切换到后台,就停止监听机器的数据,节约流量。停止视频服务等

总之就是功能多一些,这样

  • onDetached
  • onHidden
  • onInactive
  • onPaused
  • onResumed
dart 复制代码
import 'package:get/get.dart';

class Controller extends SuperController {
  List<String> log = [];

  addLog(String logContent) {
    log.add(logContent);
  }

  updateLog() {
    log.add("update - ${DateTime.now().toIso8601String()}");
    update();
  }

  @override
  void onInit() {
    log.add("onInit");
    super.onInit();
  }

  @override
  void onReady() {
    log.add("onReady");
    update();
    super.onReady();
  }

  @override
  void onClose() {
    print("controller - onClose");
    super.onClose();
  }

  @override
  void onDetached() {
    // TODO: implement onDetached
  }

  @override
  void onHidden() {
    // TODO: implement onHidden
  }

  @override
  void onInactive() {
    // TODO: implement onInactive
  }

  @override
  void onPaused() {
    // TODO: implement onPaused
  }

  @override
  void onResumed() {
    // TODO: implement onResumed
  }
}

依赖管理

Get.put

之前有用到GetsController,那么配合它,可以使用注册依赖,来达到多页面使用

回到之前的案例

我们构建了一个计算器。现在跳转到下一个界面,并获取输入的数据

实现的效果

改写用Get.put

页面跳转需要Get.to,而使用路由相关功能,需要使用GetMaterialApp

dart 复制代码
final controller = Get.put<CalculatorController>(CalculatorController());
dart 复制代码
floatingActionButton: FloatingActionButton(
          onPressed: () {
            Get.to(() => NextPage());
          },
          child: const Text("下一页"),
        ),

而在另一个界面中,需要通过Get.find来找到之前注册的Controller

最终,取到了Controller里面的数值

继续改造(Get.lazyput & Bindings)

现在写下来,代码很乱,所以整理一下

新建一个文件夹,里面放上四个空文件(之后会采用插件生成代码,现在先写一遍)

  • main-binding.dart 绑定层,用于注册Controller
  • main_logic.dart 逻辑层,就是之前的GetxController,只写逻辑
  • main_state.dart 状态层,是存放你的变量的地方,以及初始化你的变量
  • main_view.dart 界面层,只写界面,不写任何逻辑

    将main.dart中的所有代码都转移到main_view.dart

    接着移动controller到绑定层,并改写为Get.lazyput。
dart 复制代码
Get.lazyPut<CalculatorController>(() => CalculatorController());
Get.put / Get.lazyPut / Get.putAsync
  • Get.put 就是普通的注册依赖的方式
  • Get.lazyPut 是懒注册依赖,当有用到时,它就加载这个依赖
  • Get.putAsync 是异步注册


Get.find

继续改造,通过Get.find,并传入你定义的Controller泛型,就可以得到一个对象。这样就不会报错了

注意要定义在build内

GetMaterialApp -> initBinding

最后一步在GetMaterialApp中使用的bindings

main_logic

将之前定义好的controller放进逻辑层中,可以改个名字,代表main这一部分的逻辑层。直接删掉之前的文件

main_logic & main_state

将变量放进状态层,留下逻辑函数

next_page

运行项目

命名路由

使用别名路由,在GetMaterialApp中注册路由及绑定,并设置初始路径

路由以'/'开头

dart 复制代码
  static String routeName = '/main';
dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_gext_application/main/main_binding.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';

import 'main/main_view.dart';

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

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ScreenUtilInit(
      builder: (_, child) {
        return GetMaterialApp(
          debugShowCheckedModeBanner: false,
          initialRoute: Main.routeName,
          getPages: [
            GetPage(
              name: Main.routeName,
              page: () => Main(),
              binding: MainBinding(),
            ),
          ],
        );
      },
      child: Main(),
    );
  }
}

路由管理

注册路由

普通路由

home为最初的路由

Get.to()

跳转至一个新的页面 (Pushes a new page to the stack)

dart 复制代码
Get.to(() => const BPage());
Get.off()

关闭当前的页面,并进入一个新的页面(Pop the current page and pushes a new page to the stack)

dart 复制代码
Get.off(() => const BPage());
Get.offAll()

关闭所有的页面,并进入一个新的页面(Push a page and pop several pages in the stack until predicate returns true. predicate is optional)

dart 复制代码
Get.offAll(() => const BPage());
Get.back()

返回上一个页面

案例

A -> B -> C --(to / off / offAll)--> D

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return const GetMaterialApp(
      home: APage(),
    );
  }
}

class APage extends StatelessWidget {
  const APage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("A页面"),
            ElevatedButton(
              onPressed: () {
                Get.to(() => const BPage());
              },
              child: const Text("跳转至B页面"),
            ),
          ],
        ),
      ),
    );
  }
}

class BPage extends StatelessWidget {
  const BPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("B页面"),
            ElevatedButton(
              onPressed: () {
                Get.to(() => const CPage());
              },
              child: const Text("跳转至C页面"),
            ),
          ],
        ),
      ),
    );
  }
}

class CPage extends StatelessWidget {
  const CPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("C页面"),
            ElevatedButton(
              onPressed: () {
                Get.to(() => const DPage());
                // Get.off(() => const DPage());
                // Get.offAll(() => const DPage());
              },
              child: const Text("跳转至D页面"),
            ),
          ],
        ),
      ),
    );
  }
}

class DPage extends StatelessWidget {
  const DPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("D页面"),
            ElevatedButton(
              onPressed: () {
                Get.back();
              },
              child: const Text("返回上一个页面"),
            ),
          ],
        ),
      ),
    );
  }
}

别名路由

给每一个页面都定一个根路由和跳转路由

A -> B -> C -> D

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      initialRoute: APage.routeName,
      getPages: [
        GetPage(
          name: APage.rootName,
          page: () => APage(),
          children: [
            GetPage(
              name: BPage.rootName,
              page: () => BPage(),
              children: [
                GetPage(
                  name: CPage.rootName,
                  page: () => CPage(),
                  children: [
                    GetPage(
                      name: DPage.rootName,
                      page: () => DPage(),
                    ),
                  ],
                ),
              ],
            ),
          ],
        ),
      ],
    );
  }
}

class APage extends StatelessWidget {
  static String routeName = "/a";
  static String rootName = "/a";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("A页面"),
            ElevatedButton(
              onPressed: () {
                Get.toNamed(BPage.routeName);
              },
              child: const Text("跳转至B页面"),
            ),
          ],
        ),
      ),
    );
  }
}

class BPage extends StatelessWidget {
  static String routeName = "${APage.routeName}$rootName";
  static String rootName = "/b";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("B页面"),
            ElevatedButton(
              onPressed: () {
                Get.toNamed(CPage.routeName);
              },
              child: const Text("跳转至C页面"),
            ),
          ],
        ),
      ),
    );
  }
}

class CPage extends StatelessWidget {
  static String routeName = "${BPage.routeName}$rootName";
  static String rootName = "/c";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("C页面"),
            ElevatedButton(
              onPressed: () {
                Get.toNamed(DPage.routeName);
                // Get.off(() => const DPage());
                // Get.offAll(() => const DPage());
              },
              child: const Text("跳转至D页面"),
            ),
          ],
        ),
      ),
    );
  }
}

class DPage extends StatelessWidget {
  static String routeName = "${CPage.routeName}$rootName";
  static String rootName = "/d";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("D页面"),
            ElevatedButton(
              onPressed: () {
                Get.back();
              },
              child: const Text("返回上一个页面"),
            ),
          ],
        ),
      ),
    );
  }
}

以上的写法是,A的子页面有B,B的子页面是C,C的子页面是D

Get.toNamed()

跳转到新页面

dart 复制代码
Get.toNamed('/a');
Get.offNamed()

关闭当前页面,跳转到新页面

dart 复制代码
Get.offNamed('/a');
Get.offAllNamed()

关闭所有页面,跳转到新页面

dart 复制代码
Get.offAllNamed('/a');
dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      initialRoute: APage.routeName,
      getPages: [
        GetPage(
          name: APage.rootName,
          page: () => APage(),
          children: [
            GetPage(
              name: BPage.rootName,
              page: () => BPage(),
              children: [
                GetPage(
                  name: CPage.rootName,
                  page: () => CPage(),
                  children: [
                    GetPage(
                      name: DPage.rootName,
                      page: () => DPage(),
                    ),
                  ],
                ),
              ],
            ),
          ],
        ),
      ],
    );
  }
}

class APage extends StatelessWidget {
  static String routeName = "/a";
  static String rootName = "/a";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("A页面"),
            ElevatedButton(
              onPressed: () {
                Get.toNamed(BPage.routeName);
              },
              child: const Text("跳转至B页面"),
            ),
          ],
        ),
      ),
    );
  }
}

class BPage extends StatelessWidget {
  static String routeName = "${APage.routeName}$rootName";
  static String rootName = "/b";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("B页面"),
            ElevatedButton(
              onPressed: () {
                Get.toNamed(CPage.routeName);
              },
              child: const Text("跳转至C页面"),
            ),
          ],
        ),
      ),
    );
  }
}

class CPage extends StatelessWidget {
  static String routeName = "${BPage.routeName}$rootName";
  static String rootName = "/c";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("C页面"),
            ElevatedButton(
              onPressed: () {
                Get.toNamed(DPage.routeName);
                // Get.offNamed(DPage.routeName);
                // Get.offAllNamed(DPage.routeName);
              },
              child: const Text("跳转至D页面"),
            ),
          ],
        ),
      ),
    );
  }
}

class DPage extends StatelessWidget {
  static String routeName = "${CPage.routeName}$rootName";
  static String rootName = "/d";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("D页面"),
            ElevatedButton(
              onPressed: () {
                Get.back();
              },
              child: const Text("返回上一个页面"),
            ),
          ],
        ),
      ),
    );
  }
}

未知路由

当用户输入一个不存在的路径,返回一个指定页面

dart 复制代码
unknownRoute: GetPage(name: '/notfound', page: () => const UnknownPage())
dart 复制代码
class UnknownPage extends StatelessWidget {
  const UnknownPage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: Text(
          '404',
          style: TextStyle(
            fontSize: 40,
          ),
        ),
      ),
    );
  }
}

传参数

有三种方式

Get.to(arguments: {})

页面切到下一个页面,可以在argument中直接附带参数。

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      initialRoute: APage.routeName,
      getPages: [
        GetPage(
          name: APage.rootName,
          page: () => APage(),
          children: [
            GetPage(
              name: BPage.rootName,
              page: () => BPage(),
            ),
          ],
        ),
      ],
    );
  }
}

class APage extends StatelessWidget {
  static String routeName = "/a";
  static String rootName = "/a";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("A页面"),
            ElevatedButton(
              onPressed: () {
                Get.toNamed(
                  BPage.routeName,
                  arguments: {
                    "paramA": "hello",
                    "paramB": {
                      "name": "sheng_er_sheng",
                      "blog": 1,
                    }
                  },
                );
              },
              child: const Text("跳转至B页面"),
            ),
          ],
        ),
      ),
    );
  }
}

class BPage extends StatelessWidget {
  static String routeName = "${APage.routeName}$rootName";
  static String rootName = "/b";
  final paramA = Get.arguments["paramA"];
  final paramBName = Get.arguments["paramB"]["name"];
  final paramBBlog = Get.arguments["paramB"]["blog"];


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("B页面"),
            Text("$paramA, $paramBName, $paramBBlog"),
          ],
        ),
      ),
    );
  }
}
Get.toNamed() 路径带参数

页面切到下一个页面,可以在路由上附带参数。

这里要采用别名路由,很明显是网址携带参数的写法

?加个参数名,=后面接参数值,如果有第二个参数就用&链接一下,然后继续参数名,接一个=,参数值

这样即便没有前一个页面,也能通过这种传参数的方式进入到这个页面

比如我换一个网址,但是参数可以改动

这是代码中,指定的参数值

dart 复制代码
import 'dart:convert';

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

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      initialRoute: APage.routeName,
      getPages: [
        GetPage(
          name: APage.rootName,
          page: () => APage(),
          children: [
            GetPage(
              name: BPage.rootName,
              page: () => BPage(),
            ),
          ],
        ),
      ],
    );
  }
}

class APage extends StatelessWidget {
  static String routeName = "/a";
  static String rootName = "/a";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("A页面"),
            ElevatedButton(
              onPressed: () {
                const paramA = "hello";
                final paramB = {
                  "name": "双笙",
                  "blog": 1,
                };
                Get.toNamed("${BPage.routeName}?paramA=$paramA&paramB=${json.encode(paramB)}");
              },
              child: const Text("跳转至B页面"),
            ),
          ],
        ),
      ),
    );
  }
}

class BPage extends StatelessWidget {
  static String routeName = "${APage.routeName}$rootName";
  static String rootName = "/b";
  final paramA = Get.parameters["paramA"];
  final paramBName = json.decode(Get.parameters["paramB"]!)["name"];
  final paramBBlog = json.decode(Get.parameters["paramB"]!)["blog"];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("B页面"),
            Text("$paramA, $paramBName, $paramBBlog"),
          ],
        ),
      ),
    );
  }
}
在路由上规定参数(命名参数)

路由上定义参数名

这种写法需要注册路由

dart 复制代码
import 'dart:convert';

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

import 'aPage.dart';
import 'bPage.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      initialRoute: APage.routeName,
      getPages: [
        GetPage(
          name: APage.rootName,
          page: () => APage(),
          children: [
            GetPage(
              name: BPage.rootName,
              page: () => BPage(),
            ),
            GetPage(
              name: "${BPage.rootName}/:paramA",
              page: () => BPage(),
            ),
          ],
        ),
      ],
    );
  }
}
dart 复制代码
import 'dart:convert';

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

import 'bPage.dart';

class APage extends StatelessWidget {
  static String routeName = "/a";
  static String rootName = "/a";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("A页面"),
            ElevatedButton(
              onPressed: () {
                const paramA = "hello";
                const paramB = {
                  "name": "双笙",
                  "blog": 1,
                };
                Get.toNamed(
                  "${BPage.routeName}/$paramA?paramB=${json.encode(paramB)}",
                );
              },
              child: const Text("跳转至B页面"),
            ),
          ],
        ),
      ),
    );
  }
}
dart 复制代码
import 'dart:convert';

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

import 'aPage.dart';

class BPage extends StatelessWidget {
  static String routeName = "${APage.routeName}$rootName";
  static String rootName = "/b";
  final paramA = Get.parameters["paramA"];
  final paramB = json.decode(Get.parameters["paramB"]!);

  @override
  Widget build(BuildContext context) {
    final paramBName = paramB["name"];
    final paramBBlog = paramB["blog"];
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("B页面"),
            Text("$paramA, $paramBName, $paramBBlog"),
          ],
        ),
      ),
    );
  }
}

中间件

案例

描述:进入一个页面,但是这个页面需要登录。如果登录过就直接进入这个页面,否则跳转至登录页面

main --(没有登录)-> login --(返回首页)-> main --(已登录)-> vip

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

bool isLogin = false;

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

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      initialRoute: Main.routeName,
      getPages: [
        GetPage(
          name: Main.routeName,
          page: () => Main(),
        ),
        GetPage(
          name: Login.routeName,
          page: () => Login(),
        ),
        GetPage(
          name: Vip.routeName,
          page: () => Vip(),
          middlewares: [
            LoginMiddleware(),
          ],
        ),
      ],
    );
  }
}

class Main extends StatelessWidget {
  static String routeName = "/main";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("主页"),
            ElevatedButton(
              onPressed: () {
                Get.toNamed(Vip.routeName);
              },
              child: const Text("VIP页面"),
            ),
          ],
        ),
      ),
    );
  }
}

class Vip extends StatelessWidget {
  static String routeName = "/vip";

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: Text("欢迎VIP用户"),
      ),
    );
  }
}

class Login extends StatelessWidget {
  static String routeName = "/login";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("登录页面"),
            ElevatedButton(
              onPressed: () {
                Get.snackbar("提示", "登录成功");
                isLogin = true;
                Get.offAllNamed(Main.routeName);
              },
              child: const Text("我是马化腾"),
            )
          ],
        ),
      ),
    );
  }
}

class LoginMiddleware extends GetMiddleware {
  @override
  RouteSettings? redirect(String? route) {
    if (route == Vip.routeName) {
      Get.snackbar("提示", "请先登录");
      return isLogin ? null : RouteSettings(name: Login.routeName);
    } else {
      return null;
    }
  }
}
补充(routingCallback)

嵌套路由


跳转时,带上id,值为key值。点击会发现始终在/main路由下,但是页面分别进入了三个内部的路由


最后进入完成页面

dart 复制代码
extension TitleText on Text {
  Text get title => Text(
    data!,
    style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
  );
}
dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_gext_application/nestedRoute/tWidget.dart';
import 'package:get/get.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      initialRoute: Main.routeName,
      getPages: [
        GetPage(name: Main.routeName, page: () => Main()),
        GetPage(name: SexPage.routeName, page: () => SexPage()),
        GetPage(name: YearPage.routeName, page: () => YearPage()),
        GetPage(name: NamePage.routeName, page: () => NamePage()),
        GetPage(name: SuccessPage.routeName, page: () => SuccessPage()),
      ],
    );
  }
}

const int key = 1000;

class Main extends StatelessWidget {
  static String routeName = "/main";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("身份验证")),
      body: Navigator(
        key: Get.nestedKey(key),
        initialRoute: SexPage.routeName,
        onGenerateRoute: (settings) {
          if (settings.name == SexPage.routeName) {
            return GetPageRoute(
              settings: settings,
              page: () => SexPage(),
            );
          } else if (settings.name == YearPage.routeName) {
            return GetPageRoute(
              settings: settings,
              page: () => YearPage(),
            );
          } else if (settings.name == NamePage.routeName) {
            return GetPageRoute(
              settings: settings,
              page: () => NamePage(),
            );
          }
          return null;
        },
      ),
    );
  }
}

class SexPage extends StatelessWidget {
  static String routeName = "/sex";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const Text("性别:男").title,
              ElevatedButton(
                onPressed: () {
                  Get.toNamed(YearPage.routeName, id: key);
                },
                child: const Text("下一页面"),
              )
            ],
          ),
        ),
      ),
    );
  }
}

class YearPage extends StatelessWidget {
  static String routeName = "/year";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const Text("年龄:40").title,
              ElevatedButton(
                onPressed: () {
                  Get.toNamed(NamePage.routeName, id: key);
                },
                child: const Text("下一页面"),
              )
            ],
          ),
        ),
      ),
    );
  }
}

class NamePage extends StatelessWidget {
  static String routeName = "/name";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const Text("姓名:高启强").title,
              ElevatedButton(
                onPressed: () {
                  Get.toNamed(SuccessPage.routeName);
                },
                child: const Text("下一页面"),
              )
            ],
          ),
        ),
      ),
    );
  }
}

class SuccessPage extends StatelessWidget {
  static String routeName = "/success";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: const Text("你是一个人类,验证完毕").title,
      ),
    );
  }
}

相关组件

SnakBar

dart 复制代码
            Get.snackbar(
              "DOTA2",
              "恭喜中国队夺冠!",
              backgroundColor: Colors.white,
              boxShadows: [
                BoxShadow(
                  color: Colors.black.withAlpha(55),
                  offset: const Offset(1, 1),
                  blurRadius: 2,
                )
              ],
            );

Dialogs

dart 复制代码
Get.defaultDialog(
              title: '警告',
              onConfirm: () {
                Get.back();
              },
              middleText: "这是一个默认的弹窗,你知道了吗?",
            );

BottomSheets

dart 复制代码
Get.bottomSheet(
              Wrap(
                children: <Widget>[
                  ListTile(
                    leading: const Icon(Icons.music_note),
                    title: const Text('Music'),
                    onTap: () {},
                  ),
                  ListTile(
                    leading: const Icon(Icons.videocam),
                    title: const Text('Video'),
                    onTap: () {},
                  ),
                ],
              ),
              backgroundColor: Colors.white,
            );

ValueBuilder

StatefulWidget的简化版

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return const GetMaterialApp(
      home: Home(),
    );
  }
}

class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ValueBuilder<bool?>(
          initialValue: false,
          builder: (value, updateFn) {
            return Switch(
              value: value!,
              onChanged: (newValue) {
                updateFn(newValue);
              },
            );
          },
          onUpdate: (value) => print("Value updated: $value"),
          onDispose: () => print("dispose"),
        ),
      ),
    );
  }
}

ObxValue

ValueBuilder的简化版本,融入了响应式变量

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      home: Home(),
    );
  }
}

class Home extends StatelessWidget {
  final switchValue = false.obs;

  Home({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ObxValue(
          (data) => Switch(value: data.value, onChanged: data.call),
          switchValue,
        ),
      ),
    );
  }
}

Get Worker

  • ever 每次
  • once 头次
  • debounce 连续变化后的最后那一次
  • interval 间隔t秒后取一次

案例

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      home: Home(),
    );
  }
}

class Home extends StatelessWidget {
  final input = "".obs;

  final everValue = "".obs;
  final onceValue = "".obs;
  final debounceValue = "".obs;
  final intervalValue = "".obs;

  Home({super.key}) {
    ever(input, (value) {
      everValue.value = value;
    });
    once(input, (value) {
      onceValue.value = value;
    });
    debounce(input, (value) {
      debounceValue.value = value;
    });
    interval(
      input,
      (value) {
        intervalValue.value = value;
      },
      time: const Duration(seconds: 2),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Obx(() {
          return Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              ObxValue(
                (data) {
                  return Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Text(data.value),
                      TextField(onChanged: data.call),
                    ],
                  );
                },
                input,
              ),
              Text("ever(每次) - ${everValue.value}"),
              Text("once(首次) - ${onceValue.value}"),
              Text("debounce(防抖) - ${debounceValue.value}"),
              Text("internal(间隔) - ${intervalValue.value}"),
            ],
          );
        }),
      ),
    );
  }
}

GetView + StateMixin + GetConnect

  • StateMixin 比如有一个数据,它是需要通过网络请求来获取的。那么它有空、获取中、获取成功、获取失败几个状态,那么这个StateMixin可以管理这个数据。
  • GetView 个人比较推荐使用这个。它是通过传入泛型来注册Controller
  • GetConnect 提供数据

案例

获取某一坐标下的天气情况

GetConnect

网络请求获取数据

dart 复制代码
import 'package:get/get.dart';

class WeatherProvider extends GetConnect {
  final weatherApi = "c23bb570c66f248d4543c7480169072b";

  @override
  void onInit() {
    super.onInit();
    httpClient.baseUrl = 'https://api.openweathermap.org';
  }

  Future<Response> getWeather(double lat, double lon) {
    String url = '/data/2.5/weather?lat=$lat&lon=$lon&appid=$weatherApi';
    return get(url);
  }
}

GetxController with StateMixin

维护WeatherDataModel这个变量

通过GetConnect获取的数据

根据不同的状态返回, 分别返回不同的RxStatus

dart 复制代码
import 'package:get/get.dart';

import 'weather_data_model.dart';
import 'weather_provider.dart';

class WeatherController extends GetxController with StateMixin<WeatherDataModel> {
  final provider = Get.find<WeatherProvider>();

  void getWeather() async {
    final response = await provider.getWeather(43.5, 140.5);
    if (response.hasError) {
      change(null, status: RxStatus.error("error"));
    } else {
      final data = WeatherDataModel.fromJson(response.body);
      change(data, status: RxStatus.success());
    }
  }
}

GetView

通过传入泛型WeatherController,获取controller。最后通过controller.obx

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import 'binding.dart';
import 'controller.dart';

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

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      home: Main(),
      initialBinding: Binding(),
    );
  }
}

class Main extends GetView<WeatherController> {
  const Main({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            ElevatedButton(
              onPressed: () {
                controller.getWeather();
              },
              child: Text("获取数据"),
            ),
            controller.obx(
              (state) {
                return Text("weather is ${state!.weather!.first.main!}");
              },
              onLoading: const Text("加载中..."),
              onEmpty: const Text("数据空..."),
              onError: (error) {
                return const Text("出错...");
              },
            ),
          ],
        ),
      ),
    );
  }
}

注册依赖及绑定

dart 复制代码
import 'package:flutter_gext_application/GetView+GetConnect+StateMixin/weather_provider.dart';
import 'package:get/get.dart';

import 'controller.dart';

class Binding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => WeatherController());
    Get.lazyPut(() => WeatherProvider());
  }
}

GetWidget + Get.create()

先看案例

案例

  • Get.lazyput + GetView
    第一种我采用Get.put 或者Get.lazyput的方式去注册GetxController
    配合GetView

    无论怎么点,这个controller早已注册好了,返回的永远都是同一个实例
    - Get.create + GetWidget 第二种我采用Get.create的方式去注册GetxController 配合GetWidget
    Get.create ,注册的了新的实例

相关的官方文档

大致意思:每隔GetWidget都有自己的controller,在GetWidget中, 你可以放心使用Get.create

Get.create会实例一个新的controller。每次调用find,都会实例一个新的对象

GetService

可以理解为GetxController的简化版本,也有它的生命周期。但是有个特性,在项目中总是要通过Get.find来找到它。比如:后端提供的接口api,存储数据,缓存等

这样看来,也就知道它为什么名字里带service

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() async {
  await initServices();
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return const GetMaterialApp(home: Home());
  }
}

class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold();
  }
}

Future<bool> initServices() async {
  print('starting service ...');
  await Get.putAsync(() => DbService().init());
  await Get.putAsync(() => SettingService().init());
  print('All services started...');
  return true;
}

class DbService extends GetxService {
  Future<DbService> init() async {
    print('$runtimeType delays 2 sec');
    await 2.delay();
    print('$runtimeType ready!');
    return this;
  }
}

class SettingService extends GetxService {
  Future<SettingService> init() async {
    print('$runtimeType delays 1 sec');
    await 1.delay();
    print('$runtimeType ready!');
    return this;
  }
}

响应式布局

响应式这件事不止布局,还设计各个方面。这里Getx是提供了GetResponsiveView来处理布局上的响应式

GetResponsiveView

  • builder()
  • 根据默认返回的屏幕宽度返回,一行到底要摆几个方块
dart 复制代码
class ResponsiveView extends GetResponsiveView<Controller> {
  @override
  Widget builder() {
    return Scaffold(
      appBar: AppBar(title: ResponsiveViewSetting()),
      body: GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: screen.responsiveValue<int>(
                desktop: 6,
                tablet: 4,
                mobile: 2,
                watch: 1,
              ) ??
              4,
        ),
        itemBuilder: (BuildContext context, int index) {
          return Container(
            decoration: BoxDecoration(
              border: Border.all(color: Colors.black.withAlpha(55)),
              color: Colors.blue.withAlpha(55),
            ),
            alignment: Alignment.center,
            margin: const EdgeInsets.all(10),
            child: Text("$content${screen.width}"),
          );
        },
        itemCount: 100,
      ),
    );
  }

  String get content => screen.screenType == ScreenType.Desktop
      ? "桌面"
      : screen.screenType == ScreenType.Phone
          ? "手机"
          : screen.screenType == ScreenType.Tablet
              ? "平板"
              : screen.screenType == ScreenType.Watch
                  ? "手表"
                  : "无";
}
  • alwaysUseBuilder
    不使用build(),使用另外四个回调返回
  • ResponsiveScreenSettings
    自己定义手机该多宽,平板该多宽,桌面端该多宽
dart 复制代码
  ResponsiveView()
      : super(
            settings: const ResponsiveScreenSettings(
          desktopChangePoint: 900,
          tabletChangePoint: 800,
          watchChangePoint: 500,
        ));

运行效果

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_gext_application/GetResponsiveView/controller.dart';
import 'package:flutter_gext_application/nestedRoute/main.dart';
import 'package:get/get.dart';



class ResponsiveView extends GetResponsiveView<Controller> {
  // ResponsiveView()
  //     : super(
  //           settings: const ResponsiveScreenSettings(
  //         desktopChangePoint: 900,
  //         tabletChangePoint: 800,
  //         watchChangePoint: 500,
  //       ));
  @override
  Widget builder() {
    return Scaffold(
      appBar: AppBar(title: ResponsiveViewSetting()),
      body: GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: screen.responsiveValue<int>(
                desktop: 6,
                tablet: 4,
                mobile: 2,
                watch: 1,
              ) ??
              4,
        ),
        itemBuilder: (BuildContext context, int index) {
          return Container(
            decoration: BoxDecoration(
              border: Border.all(color: Colors.black.withAlpha(55)),
              color: Colors.blue.withAlpha(55),
            ),
            alignment: Alignment.center,
            margin: const EdgeInsets.all(10),
            child: Text("$content${screen.width}"),
          );
        },
        itemCount: 100,
      ),
    );
  }

  String get content => screen.screenType == ScreenType.Desktop
      ? "桌面"
      : screen.screenType == ScreenType.Phone
          ? "手机"
          : screen.screenType == ScreenType.Tablet
              ? "平板"
              : screen.screenType == ScreenType.Watch
                  ? "手表"
                  : "无";
}

class ResponsiveViewSetting extends GetResponsiveView<Controller> {
  ResponsiveViewSetting({super.key}) : super(alwaysUseBuilder: false);

  @override
  Widget tablet() {
    return const Text("TABLE").title;
  }

  @override
  Widget phone() {
    return const Text("PHONE").title;
  }

  @override
  Widget desktop() {
    return const Text("DESKTOP").title;
  }

  @override
  Widget watch() {
    return const Text("WATCH").title;
  }
}
dart 复制代码
void main() {
  runApp(App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: Home(),);
  }
}

class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    return ResponsiveView();
  }
}

自定义响应式函数

Getx定义的GetResponsiveView多少有限局限性,所以可以自己对上下文做扩展,比如根据宽度来决定返回传入的泛型

这样不需要引入getx也能实现响应式

dart 复制代码
import 'package:flutter/material.dart';

/// 扩展 上下文BuildContext
extension Responsive on BuildContext {
  T responsive<T>(
    T defaultVal, {
    T? sm,
    T? md,
    T? lg,
    T? xl,
  }) {
    final wd = MediaQuery.of(this).size.width;
    return wd >= 1280
        ? (xl ?? lg ?? md ?? sm ?? defaultVal)
        : wd >= 1024
            ? (lg ?? md ?? sm ?? defaultVal)
            : wd >= 768
                ? (md ?? sm ?? defaultVal)
                : wd >= 640
                    ? (sm ?? defaultVal)
                    : defaultVal;
  }
}
dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_gext_application/GetResponsiveView/context.dart';

class ResponsiveView extends StatelessWidget {
  const ResponsiveView({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount:
              context.responsive<int>(1, sm: 2, md: 3, lg: 4, xl: 5),
        ),
        itemBuilder: (BuildContext context, int index) {
          double width = MediaQuery.of(context).size.width;
          return Container(
            decoration: BoxDecoration(
              border: Border.all(color: Colors.black.withAlpha(55)),
              color: Colors.blue.withAlpha(55),
            ),
            alignment: Alignment.center,
            margin: const EdgeInsets.all(10),
            child: Text("$width"),
          );
        },
        itemCount: 100,
      ),
    );
  }
}

主题切换

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return const GetMaterialApp(home: Home());
  }
}

class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("主题"),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text("是否为黑色主题${Get.isDarkMode}"),
            ElevatedButton(
              onPressed: () {
                Get.changeTheme(
                    Get.isDarkMode ? ThemeData.light() : ThemeData.dark());
              },
              child: const Text("dark theme"),
            )
          ],
        ),
      ),
    );
  }
}

国际化语言翻译

首先需要一个Translation

dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import 'keys.dart';
import 'str_translations.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      locale: Locale(StrTranslation.zhCNKey),
      translations: StrTranslation(),
      home: const Home(),
    );
  }
}

class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(
              SR.testTranslation.tr,
              style: const TextStyle(fontSize: 20),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                  onPressed: () {
                    Get.updateLocale(Locale(StrTranslation.zhCNKey));
                  },
                  child: const Text('中文'),
                ),
                ElevatedButton(
                  onPressed: () {
                    Get.updateLocale(Locale(StrTranslation.enUSKey));
                  },
                  child: const Text('英文'),
                )
              ],
            ),
          ],
        ),
      ),
    );
  }
}
dart 复制代码
import 'package:flutter_gext_application/lan/en.dart';
import 'package:flutter_gext_application/lan/zh.dart';
import 'package:get/get.dart';

class StrTranslation extends Translations {
  static String zhCNKey = "zh_CN";
  static String enUSKey = "en_US";

  @override
  Map<String, Map<String, String>> get keys {
    return {
      zhCNKey: zh,
      enUSKey: en,
    };
  }
}
dart 复制代码
import 'keys.dart';

const zh = {
  SR.testTranslation: '好久不见,你还好吗',
};
dart 复制代码
import 'keys.dart';

const en = {
  SR.testTranslation: 'Long time no see, are you okay',
};
dart 复制代码
class SR {
  static const String testTranslation = "testTranslation";
}

运行

利用插件生成模板代码

挺好用的插件

更多API

建议直接看这个文档

他喵的,终于写完了

点个关注呗

相关推荐
瓜子三百克3 小时前
七、性能优化
flutter·性能优化
恋猫de小郭10 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
小蜜蜂嗡嗡17 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
瓜子三百克21 小时前
十、高级概念
flutter
帅次1 天前
Objective-C面向对象编程:类、对象、方法详解(保姆级教程)
flutter·macos·ios·objective-c·iphone·swift·safari
小蜜蜂嗡嗡1 天前
flutter flutter_vlc_player播放视频设置循环播放失效、初始化后获取不到视频宽高
flutter
孤鸿玉1 天前
[Flutter小技巧] Row中widget高度自适应的几种方法
flutter
bawomingtian1231 天前
FlutterView 源码解析
flutter
Zender Han2 天前
Flutter 进阶:实现带圆角的 CircularProgressIndicator
flutter