# flutter_GetX **Repository Path**: LiuMing-Git/flutter_getx ## Basic Information - **Project Name**: flutter_GetX - **Description**: geix状态管理使用心得 - **Primary Language**: Dart - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2024-06-22 - **Last Updated**: 2024-06-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## Flutter GetX系列教程 #### 介绍、Snackbar、Dialog、BottomSheet、Navigation、Obx 前言 本文是基于官方最新稳定版本get: ^4.3.8来进行开发 学完本系列课程你将学会: - 1. - 1. 知道什么是GetX - 1. 会使用Snackbar - 1. 会使用Dialog - 1. 会使用BottomSheet - 1. 会用Navigation - 1. 会用Obx响应式状态管理 - 1. 会使用GetXController(三种使用方式GetBuilder、事件监听、生命周期、UniqueID) - 1. 语言国际化配置 - 1. 依赖注入 - 1. GetX Service - 1. GetX Binding(绑定) - 1. GetX获取API接口数据并显示 - 1. 获取存储和邮件验证 - 1. GetView和GetWidget - 1. Get Cli 脚手架使用以及常用命令 - 1. 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个属性, ``` 字段 属性 描述 title String 弹出的标题文字 message String 弹出的消息文字 colorText Color title和message的文字颜色 duration Duration Snackbar弹出的持续时间(默认3秒) instantInit bool 当false可以把snackbar 放在initState,默认true snackPosition SnackPosition 弹出时的位置,有两个选项【TOP,BOTTOM】默认TOP titleText Widget 弹出标题的组件,设置该属性会导致title属性失效 messageText Widget 弹出消息的组件,设置该属性会导致messageText属性失效 icon Widget 弹出时图标,显示在title和message的左侧 shouldIconPulse bool 弹出时图标是否闪烁,默认false maxWidth double Snackbar最大的宽度 margin EdgeInsets Snackbar外边距,默认zero padding EdgeInsets Snackbar内边距,默认EdgeInsets.all(16) borderRadius double 边框圆角大小,默认15 borderColor Color 边框的颜色,必须设置borderWidth,否则无效果 borderWidth double 边框的线条宽度 backgroundColor Color Snackbar背景颜色,默认Colors.grey.withOpacity(0.2) leftBarIndicatorColor Color 左侧指示器的颜色 boxShadows List Snackbar阴影颜色 backgroundGradient Gradient 背景的线性颜色 mainButton TextButton 主要按钮,一般显示发送、确认按钮 onTap OnTap 点击Snackbar事件回调 isDismissible bool 是否开启Snackbar手势关闭,可配合dismissDirection使用 showProgressIndicator bool 是否显示进度条指示器,默认false dismissDirection SnackDismissDirection Snackbar关闭的方向 progressIndicatorController AnimationController 进度条指示器的动画控制器 progressIndicatorBackgroundColor Color 进度条指示器的背景颜色 progressIndicatorValueColor Animation 进度条指示器的背景颜色,Animation snackStyle SnackStyle Snackbar是否会附加到屏幕边缘 forwardAnimationCurve Curve Snackbar弹出的动画,默认Curves.easeOutCirc reverseAnimationCurve Curve Snackbar消失的动画,默认Curves.easeOutCirc animationDuration Duration Snackbar弹出和小时的动画时长,默认1秒 barBlur double Snackbar背景的模糊度 overlayBlur double 弹出时的毛玻璃效果值,默认0 snackbarStatus SnackbarStatusCallback Snackbar弹出或消失时的事件回调(即将打开、已打开、即将关闭、已关闭) overlayColor Color 弹出时的毛玻璃的背景颜色 userInputForm Form 用户输入表单 ``` 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个属性 字段 属性 描述 title String 弹出的标题,默认(Alert) titlePadding EdgeInsetsGeometry 标题的内边距,默认(EdgeInsets.all(8)) titleStyle TextStyle 标题的样式 middleText String 中间内容区域显示的文字 middleTextStyle TextStyle 中间内容区域显示的文字样式 content Widget 弹出的内容,该值设置后middleText将无效 contentPadding EdgeInsetsGeometry 内容的内边距,默认(EdgeInsets.all(8)) onConfirm VoidCallback 确认按钮回调 onCancel VoidCallback 取消按钮回调 onCustom VoidCallback 自定义按钮回调 cancelTextColor Color 取消按钮文字的颜色 confirmTextColor Color 确认按钮文字的颜色 textConfirm String 确认按钮的文字 textCancel String 取消按钮的文字 textCustom String 自定义按钮的文字 confirm Widget 确认按钮的组件 cancel Widget 取消按钮的组件 custom Widget 自定义按钮的组件 backgroundColor Color 弹出框的背景颜色 barrierDismissible bool 是否可以通过点击背景关闭弹窗 buttonColor Color 按钮的文字颜色,根据按钮类型来设定不同的位置 radius double 弹出框的圆角大小,默认20 actions List 增加额外的子组件 onWillPop WillPopCallback 拦截关闭之前做一些操作 navigatorKey GlobalKey 用于打开对话框的key BottomSheet介绍 BottomSheet 是底部弹出的一个组件,常用于单选、验证码二次校验弹窗等,GetX的BottomSheet底部弹出是自定义通过路由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 _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属性和说明 字段 属性 描述 bottomsheet Widget 弹出的Widget组件 backgroundColor Color bottomsheet的背景颜色 elevation double bottomsheet的阴影 persistent bool 是否添加到路由中 shape ShapeBorder 边框形状,一般用于圆角效果 clipBehavior Clip 裁剪的方式 barrierColor Color 弹出层的背景颜色 ignoreSafeArea bool 是否忽略安全适配 isScrollControlled bool 是否支持全屏弹出,默认false useRootNavigator bool 是否使用根导航 isDismissible bool 点击背景是否可关闭,默认ture enableDrag bool 是否可以拖动关闭,默认true settings RouteSettings 路由设置 enterBottomSheetDuration Duration bottomsheet进入时的动画时间 exitBottomSheetDuration Duration bottomsheet退出时的动画时间 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 _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 _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([]); // final myMap = RxMap({}); // 第二种是使用 Rx,规定泛型 Rx。 // final name = Rx(''); // final isLogged = Rx(false); // final count = Rx(0); // final balance = Rx(0.0); // final number = Rx(0) // final items = Rx>([]); // final myMap = Rx>({}); // 自定义类 - 可以是任何类 // final user = Rx(); // 第三种更实用、更简单、更可取的方法,只需添加 .obs 作为value的属性。 // final name = ''.obs; // final isLogged = false.obs; // final count = 0.obs; // final balance = 0.0.obs; // final number = 0.obs; // final items = [].obs; // final myMap = {}.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(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(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以及如果使用,包括Snackbar、Dialog、BottomSheet、Navigation、Obx 等进行了一个简单的介绍,使用GetX后能极简的缩减我们的代码。 #### Flutter GetX系列教程---GetxController GetxController介绍 在实际的项目开发过程中,我们不可能把UI代码、业务逻辑都放在一起处理,这样对项目的架构、代码的可读性、后期的维护将会是致命的,好在GetX为我们提供了GetxController,GetxController主要的作用是用于UI代码与业务逻辑分离开来。 GetxController三种使用方式 这里主要讲解使用GetxController动态获取数据的三种方式以及更新数据的方式。 第一步:应用程序入口设置 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetXControllerExample/GetXControllerExample.dart'; import 'package:get/get.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return GetMaterialApp( title: "GetX", home: GetXControllerExample(), ); } } ``` 第二步:定义控制器继承自GetxController ``` import 'package:flutter_getx_example/ObxCustomClassExample/Teacher.dart'; import 'package:get/get.dart'; class MyController extends GetxController { // 第一种 // var teacher = Teacher(); // // void convertToUpperCase() { // teacher.name.value = teacher.name.value.toUpperCase(); // } // 第二种 // var teacher = Teacher(name: "Jimi", age: 18).obs; // void convertToUpperCase() { // teacher.update((val) { // teacher.value.name = teacher.value.name.toString().toUpperCase(); // }); // } // 第三种 var teacher = Teacher(); void convertToUpperCase() { teacher.name.value = teacher.name.value.toUpperCase(); update(); } } ``` 第三步:实例化控制器并使用 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetXControllerExample/MyController.dart'; import 'package:get/get.dart'; class GetXControllerExample extends StatelessWidget { // 第一种 MyController myController = Get.put(MyController()); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("GetX Obx---GetXController"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ // 第一种 // Obx(() => Text( // "我的名字是 ${myController.teacher.name}", // style: TextStyle(color: Colors.red, fontSize: 30), // )), // 第二种 // GetX( // init: MyController(), // builder: (controller) { // return Text( // "我的名字是 ${controller.teacher.name}", // style: TextStyle(color: Colors.green, fontSize: 30), // ); // }, // ), // 第三种 GetBuilder( init: myController, builder: (controller) { return Text( "我的名字是 ${controller.teacher.name}", style: TextStyle(color: Colors.green, fontSize: 30), ); }, ), SizedBox(height: 20,), ElevatedButton( onPressed: () { // 第一种 myController.convertToUpperCase(); // 第二种 // Get.find().convertToUpperCase(); }, child: Text("转换为大写")) ], ), ), ); } } ``` 效果展示 GetxController事件监听 这里主要讲解GetxController的事件监听,包括监听单个值、多个值等。 第一步:应用程序入口设置 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetXControllerWorkersExample/GetXControllerWorkersExample.dart'; import 'package:get/get.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return GetMaterialApp( title: "GetX", home: GetXControllerWorkersExample(), ); } } ``` 第二步:定义控制器继承自GetxController ``` import 'package:get/get.dart'; class WorkersController extends GetxController { var count = 0.obs; void increment() { count++; } } ``` 第三步:重写onInit并监听事件 ``` @override void onInit() { // TODO: implement onInit // 监听count的值,当它发生改变的时候调用 ever(count, (callback) => print("ever----$count")); // 监听多个值,当它们发生改变的时候调用 everAll([count], (callback) => print("everAll----$count")); // count值改变时调用,只执行一次 once(count, (callback) => print("once----$count")); // 用户停止打字时1秒后调用,主要是防DDos debounce(count, (callback) => print("debounce----$count")); // 忽略3秒内的所有变动 interval(count, (callback) => print("interval----$count")); super.onInit(); } ``` 第四步:实例化控制器并使用 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetXControllerWorkersExample/WorkersConroller.dart'; import 'package:get/get.dart'; class GetXControllerWorkersExample extends StatelessWidget { WorkersController workersController = Get.put(WorkersController()); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("GetXWorkersController"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ ElevatedButton( onPressed: () => workersController.increment(), child: Text("增加") ), Padding( padding: EdgeInsets.all(16), child: TextField( onChanged: (val) { workersController.increment(); }, ), ) ], ), ), ); } } ``` 效果展示 控制台输出结果 ``` flutter: ever----1 flutter: everAll----1 flutter: once----1 [GETX] Worker [once] called flutter: debounce----1 flutter: interval----1 flutter: ever----2 flutter: everAll----2 flutter: debounce----2 flutter: interval----2 flutter: ever----3 flutter: everAll----3 flutter: ever----4 flutter: everAll----4 flutter: ever----5 flutter: everAll----5 flutter: interval----5 flutter: debounce----5 GetxController生命周期 ``` 这里主要讲解GetxController的生命周期,包括初始化、加载完成、销毁等。 第一步:应用程序入口设置 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetXControllerLifecycleMethodsExample/GetXControllerLifecycleMethodExample.dart'; import 'package:get/get.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return GetMaterialApp( title: "GetX", home: GetXControllerLifecycleMethodExample(), ); } } ``` 第二步:定义控制器继承自GetxController ``` import 'package:get/get.dart'; class MyLifecycleController extends GetxController { var count = 0; void increment() async { await Future.delayed(Duration(milliseconds: 3000)); count++; update(); } void cleanTask() { print("清除了任务"); } } ``` 第三步:重写GetxController生命周期方法 ``` @override void onInit() { // TODO: implement onInit print("初始化"); super.onInit(); } @override void onReady() { // TODO: implement onReady print("加载完成"); super.onReady(); } @override void onClose() { // TODO: implement onClose print("控制器被释放"); super.onClose(); } ``` 第四步:实例化控制器并使用 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetXControllerLifecycleMethodsExample/MyLifecycleController.dart'; import 'package:get/get.dart'; class GetXControllerLifecycleMethodExample extends StatelessWidget { MyLifecycleController myLifecycleController = Get.put(MyLifecycleController()); @override Widget build(BuildContext context) { print("build"); return Scaffold( appBar: AppBar( title: Text("GetXControllerLifecycleMethod"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ GetBuilder( initState: (data) => myLifecycleController.increment(), dispose: (_) => myLifecycleController.cleanTask(), builder: (controller) { return Text( "计数器值为: ${myLifecycleController.count}", style: TextStyle(color: Colors.green, fontSize: 30), ); }, ), ], ), ), ); } } ``` 效果展示 控制台输出结果 ``` flutter: 初始化 [GETX] Instance "MyLifecycleController" has been created [GETX] Instance "MyLifecycleController" has been initialized [GETX] Instance "GetMaterialController" has been created [GETX] Instance "GetMaterialController" has been initialized flutter: build flutter: 加载完成 flutter: build flutter: build GetxController UniqueID ``` 我们在开发的过程中会碰到一种情况,就是多个地方引用了同一个属性,但我只想单独更新某一个地方,那么就可以用UniqueID来进行区分。 第一步:应用程序入口设置 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetXControllerUniqueIDExample/GetXControllerUniqueIDExample.dart'; import 'package:get/get.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return GetMaterialApp( title: "GetX", home: GetXControllerUniqueIDExample(), ); } } ``` 第二步:定义控制器继承自GetxController,并且定义uniqueID ``` import 'package:get/get.dart'; class CountController extends GetxController { var count = 0; void increment() { count++; update(['jimi_count']); } } ``` 第三步:实例化控制器并使用 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetXControllerUniqueIDExample/CountConroller.dart'; import 'package:get/get.dart'; class GetXControllerUniqueIDExample extends StatelessWidget { CountController countController = Get.put(CountController()); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("GetX Obx---GetXController"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ GetBuilder( builder: (controller) { return Text( "计数器值为: ${controller.count}", style: TextStyle(color: Colors.red, fontSize: 30), ); }, ), GetBuilder( id: 'jimi_count', builder: (controller) { return Text( "计数器值为: ${controller.count}", style: TextStyle(color: Colors.green, fontSize: 30), ); }, ), SizedBox(height: 20,), ElevatedButton( onPressed: () => countController.increment(), child: Text("增加")) ], ), ), ); } } ``` 效果展示 总结 本章节对GetxController的数据赋值、数据更新、事件监听、生命周期、UniqueID 进行了讲解,也明白了怎么用GetxController来对项目的UI代码与业务逻辑进行拆分。 #### Flutter GetX系列教程---国际化配置、依赖注入、Binding 国际化配置 在我们使用系统自带MaterialApp来实现国际化配置,需要进行很多配置,而且还需要手动去依赖第三方组件,而使用GetX来实现国际化配置,你只需要一行代码即可实现切换,接下来我们看一下具体实现。 第一步:应用程序入口配置 translations: 国际化配置文件 locale: 设置默认语言,不设置的话为系统当前语言 fallbackLocale: 配置错误的情况下,使用的语言 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/InternationalizationExample/InternationalizationExample.dart'; import 'package:get/get.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { /// 国际化配置 return GetMaterialApp( title: "GetX", translations: Messages(), locale: Locale('zh', 'CN'), //设置默认语言 fallbackLocale: Locale("zh", "CN"), // 在配置错误的情况下,使用的语言 home: InternationalizationExample(), ); } } ``` 第二步:创建国际化类 需要继承自Translations并重写keys方法。 ``` import 'package:get/get.dart'; class Messages extends Translations { @override // TODO: implement keys Map> get keys => { 'zh_CN': { 'hello': "你好, 世界" }, 'en_US': { 'hello': 'hello world' } }; } ``` 第三步:创建控制器类,用于切换语言 ``` import 'dart:ui'; import 'package:get/get.dart'; class MessagesController extends GetxController { void changeLanguage(String languageCode, String countryCode) { var locale = Locale(languageCode, countryCode); Get.updateLocale(locale); } } ``` 第四步:实例化控制器并使用 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetXControllerWorkersExample/WorkersConroller.dart'; import 'package:flutter_getx_example/InternationalizationExample/MessagesCnotroller.dart'; import 'package:get/get.dart'; class InternationalizationExample extends StatelessWidget { MessagesController messagesController = Get.put(MessagesController()); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Internationalization"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Text('hello'.tr, style: TextStyle(color: Colors.pink, fontSize: 30)), ElevatedButton( onPressed: () => messagesController.changeLanguage('zh', "CN"), child: Text("切换到中文") ), SizedBox(height: 20,), ElevatedButton( onPressed: () => messagesController.changeLanguage('en', "US"), child: Text("切换到英文") ), ], ), ), ); } } ``` 效果展示 依赖注入 在前面的文章中,我们经常使用Get.put(MyController())来进行控制器实例的创建,这样我们就算不使用控制器实例也会被创建,其实GetX还提供很多创建实例的方法,可根据不同的业务来进行创建,接下来我们简单介绍一下几个最常用的 Get.put(): 不使用控制器实例也会被创建 Get.lazyPut(): 懒加载方式创建实例,只有在使用时才创建 **Get.putAsync():** `Get.put()`的异步版版本 **Get.create():** 每次使用都会创建一个新的实例 我们来看一下代码演示 第一步:应用程序入口配置 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/DependecyInjectionExample/DependecyInjectionExample.dart'; import 'package:get/get.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return GetMaterialApp( title: "GetX", home: DependecyInjectionExample(), ); } } ``` 第二步:创建控制器 ``` import 'package:flutter_getx_example/ObxCustomClassExample/Teacher.dart'; import 'package:get/get.dart'; class MyController extends GetxController { var teacher = Teacher(); void convertToUpperCase() { teacher.name.value = teacher.name.value.toUpperCase(); } } ``` 第三步:实例化控制器并使用 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetXControllerExample/MyController.dart'; import 'package:get/get.dart'; class DependecyInjectionExample extends StatelessWidget { @override Widget build(BuildContext context) { // 即使不使用控制器实例也会被创建 // tag将用于查找具有标签名称的实例 // 控制器在不使用时被处理,但如果永久为真,则实例将在整个应用程序中保持活动状态 MyController myController = Get.put(MyController(), permanent: true); // MyController myController = Get.put(MyController(), tag: "instancel", permanent: true); // 实例将在使用时创建 // 它类似于'permanent',区别在于实例在不被使用时被丢弃 // 但是当它再次需要使用时,get 将重新创建实例 // Get.lazyPut(()=> MyController()); // Get.lazyPut(()=> MyController(), tag: "instancel"); // Get.put 异步版本 // Get.putAsync(() async => await MyController()); // 每次都将返回一个新的实例 // Get.create(() => MyController()); return Scaffold( appBar: AppBar( title: Text("GetXController"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ ElevatedButton( onPressed: () { // 实例使用的tag创建 // Get.find(tag: 'instancel'); Get.find(); }, child: Text("别点我")) ], ), ), ); } } ``` Get Service 这个类就像一个 GetxController,它共享相同的生命周期onInit()、onReady()、onClose()。 但里面没有 “逻辑”。它只是通知GetX的依赖注入系统,这个子类不能从内存中删除。所以如果你需要在你的应用程序的生命周期内对一个类实例进行绝对的持久化,那么就可以使用GetxService。 第一步:创建Service ``` import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; class Service extends GetxService { Future getCounter() async { SharedPreferences prefs = await SharedPreferences.getInstance(); int count = (prefs.getInt("counter") ?? 0) + 1; print("count 的值为: $count"); await prefs.setInt("counter", count); } } ``` 第二步:初始化Service ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetXServiceExample/GetXServiceExample.dart'; import 'package:flutter_getx_example/GetXServiceExample/Service.dart'; import 'package:get/get.dart'; /// 初始化服务 Future main() async { await initServices(); runApp(MyApp()); } Future initServices() async { print("初始化服务"); await Get.putAsync(() async => await Service()); print("所有服务启动"); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return GetMaterialApp( title: "GetX", home: GetXServiceExample(), ); } } ``` 第三步:调用Service ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetXServiceExample/Service.dart'; import 'package:get/get.dart'; class GetXServiceExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("GetX Service"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ ElevatedButton( onPressed: () { Get.find().getCounter(); }, child: Text("点我加1")) ], ), ), ); } } ``` GetX Binding 在我们使用GetX状态管理器的时候,往往每次都是用需要手动实例化一个控制器,这样的话基本页面都需要实例化一次,这样就太麻烦了,而Binding 能解决上述问题,可以在项目初始化时把所有需要进行状态管理的控制器进行统一初始化,接下来看代码演示: 第一步:声明需要进行的绑定控制器类 ``` import 'package:flutter_getx_example/GetXBindingExample/controller/BindingHomeController.dart'; import 'package:flutter_getx_example/GetXBindingExample/controller/BindingMyController.dart'; import 'package:get/get.dart'; class AllControllerBinding implements Bindings { @override void dependencies() { // TODO: implement dependencies Get.lazyPut(() => BindingMyController()); Get.lazyPut(() => BindingHomeController()); } } import 'package:get/get.dart'; class BindingHomeController extends GetxController { var count = 0.obs; void increment() { count++; } } import 'package:get/get.dart'; class BindingMyController extends GetxController { var count = 0.obs; void increment() { count++; } } ``` 第二步:在项目启动时进行初始化绑定 绑定的方式有多种,在不同的情况下有不同的使用方式,这里介绍一种,如果需要更加详细的介绍,观看视频将会是最佳的选择。 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetXBindingExample/binding/AllControllerBinding.dart'; import 'package:flutter_getx_example/GetXBindingExample/GetXBindingExample.dart'; import 'package:get/get.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { /// GetX Binding return GetMaterialApp( title: "GetX", initialBinding: AllControllerBinding(), home: GetXBindingExample(), ); } } ``` 第三步:在页面中使用状态管理器 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetXBindingExample/BHome.dart'; import 'package:flutter_getx_example/GetXBindingExample/binding/BHomeControllerBinding.dart'; import 'package:flutter_getx_example/GetXBindingExample/controller/BindingMyController.dart'; import 'package:get/get.dart'; class GetXBindingExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("GetXBinding"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Obx(() => Text( "计数器的值为 ${Get.find().count}", style: TextStyle(color: Colors.red, fontSize: 30), )), SizedBox(height: 20,), ElevatedButton( onPressed: () { Get.find().increment(); }, child: Text("点击加1") ), SizedBox(height: 20,), ElevatedButton( onPressed: () { Get.to(BHome()); // Get.toNamed("/bHome"); // Get.to(BHome(), binding: BHomeControllerBinding()); }, child: Text("跳转去首页") ), ], ), ), ); } } ``` #### Flutter GetX系列教程---新闻案例 前言 在前面的文章中我们对GetX的基础知识进行了讲解,包括状态管理、路由管理、依赖管理、国际化等。 今天我们来将一个小案例用于结合前面学习的知识做一个总结,这个案例主要是讲解网络数据请求、模型处理、GetXController,用MVC模型来实现。 视频教程地址 零基础视频教程地址 请求网络数据 我们新建一个ApiService.dart用于请求网络数据,该数据是一个新闻列表的数据。 ``` import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:flutter_getx_example/GetXApiDataExample/MovieModule/Models/MovieModel.dart'; class ApiService { static Future> fetchMovie() async { var response = await Dio().get("http://apis.juhe.cn/fapig/douyin/billboard?type=hot_video&size=50&key=9eb8ac7020d9bea6048db1f4c6b6d028"); if (response.statusCode == 200) { var jsonString = response.data['result']; return movieModelFromJson(json.encode(response.data["result"])); } } } ``` 定义模型类 我们新建一个MovieModel.dart用来对网络请求回来的数据进行模型转换。 ``` // To parse this JSON data, do // // final movieModel = movieModelFromJson(jsonString); import 'dart:convert'; List movieModelFromJson(String str) => List.from(json.decode(str).map((x) => MovieModel.fromJson(x))); String movieModelToJson(List data) => json.encode(List.from(data.map((x) => x.toJson()))); class MovieModel { MovieModel({ this.title, this.shareUrl, this.author, this.itemCover, this.hotValue, this.hotWords, this.playCount, this.diggCount, this.commentCount, }); String title; String shareUrl; String author; String itemCover; int hotValue; String hotWords; int playCount; int diggCount; int commentCount; factory MovieModel.fromJson(Map json) => MovieModel( title: json["title"], shareUrl: json["share_url"], author: json["author"], itemCover: json["item_cover"], hotValue: json["hot_value"], hotWords: json["hot_words"], playCount: json["play_count"], diggCount: json["digg_count"], commentCount: json["comment_count"], ); Map toJson() => { "title": title, "share_url": shareUrl, "author": author, "item_cover": itemCover, "hot_value": hotValue, "hot_words": hotWords, "play_count": playCount, "digg_count": diggCount, "comment_count": commentCount, }; } ``` 使用控制器对数据进行处理 我们对请求回来的网络数据转化为Model,并在请求前增加一个loading,当网络数据请求回来的时候关闭loading,我们来看一下代码 ``` import 'package:flutter_getx_example/GetXApiDataExample/ApiModule/ApiService.dart'; import 'package:flutter_getx_example/GetXApiDataExample/MovieModule/Models/MovieModel.dart'; import 'package:get/get.dart'; class MovieController extends GetxController { var isLoading = true.obs; // ignore: deprecated_member_use var movieList = List().obs; @override void onInit() { // TODO: implement onInit fetchMovie(); super.onInit(); } void fetchMovie() async { try { isLoading(true); var movie = await ApiService.fetchMovie(); if (movie != null) { movieList.assignAll(movie); } } finally { isLoading(false); } } } ``` 在视图层展示列表数据 前面我们对网络请求、模型、数据处理进行了处理,我们最终的目的是需要把数据展示到页面上,那我们接着来看一下视图的代码 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetXApiDataExample/MovieModule/Controller/MovieController.dart'; import 'package:get/get.dart'; class MovieListView extends StatelessWidget { final MovieController movieController = Get.put(MovieController()); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Movie"), ), body: Obx(() { if (movieController.isLoading.value) { return Center( child: CircularProgressIndicator(), ); } else { return ListView.builder( itemCount: movieController.movieList.length, itemBuilder: (context, index) { return Column( children: [ Row( children: [ Container( width: 100, height: 120, margin: EdgeInsets.all(10), child: ClipRRect( borderRadius: BorderRadius.circular(6), child: Image.network( movieController.movieList[index].itemCover, width: 150, height: 100, fit: BoxFit.fill, // color: Colors.orange, // colorBlendMode: BlendMode.color, ), ), ), Flexible( child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( movieController.movieList[index].author, style: TextStyle( color: Colors.black45, fontSize: 16 ), ) ], ), ), ], ), Container( color: Colors.grey.shade300, height: 2, ) ], ); }, ); } }), ); } ``` 效果展示 总结 该案例中最重要的通过继承自GetxController分离了UI和业务逻辑,并通过Get.put对控制器进行了实例化,然后使用Obx对数据进行了一个状态刷新。 #### Flutter GetX系列教程---GetUtils GetUtils介绍 GetUtils是getx为我们提供一些常用的工具类库,包括值是否为空、是否是数字、是否是视频、图片、音频、PPT、Word、APK、邮箱、手机号码、日期、MD5、SHA1等等。 我们这里举几个简单的例子: 以判断是否是邮箱、手机号、IPV4地址为例 ``` import 'package:flutter/material.dart'; import 'package:get/get.dart'; class GetXUtilsExample extends StatelessWidget { var textFieldController = TextEditingController(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("GetX Utils"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Padding( padding: EdgeInsets.all(20), child: TextField( controller: textFieldController, ), ), SizedBox(height: 10,), Padding( padding: EdgeInsets.all(10), child: ElevatedButton( child: Text("判断是否是邮箱"), onPressed: () async { if (GetUtils.isEmail(textFieldController.text)) { Get.snackbar("正确", "恭喜你, 完全正确", backgroundColor: Colors.greenAccent); } else { Get.snackbar( "邮箱错误", "请输入正确的邮箱", backgroundColor: Colors.pink ); } }, ), ), Padding( padding: EdgeInsets.all(10), child: ElevatedButton( child: Text("判断是否是手机号"), onPressed: () async { if (GetUtils.isPhoneNumber(textFieldController.text)) { Get.snackbar("正确", "恭喜你, 完全正确", backgroundColor: Colors.greenAccent); } else { Get.snackbar( "手机号错误", "请输入正确的手机号", backgroundColor: Colors.pink ); } }, ), ), Padding( padding: EdgeInsets.all(10), child: ElevatedButton( child: Text("判断是否是IPv4"), onPressed: () async { if (GetUtils.isIPv4(textFieldController.text)) { Get.snackbar("正确", "恭喜你, 完全正确", backgroundColor: Colors.greenAccent); } else { Get.snackbar( "地址错误", "请输入正确的IPv4地址", backgroundColor: Colors.pink ); } }, ), ), ], ), ), ); } } ``` #### Flutter GetX系列教程---GetView、GetWidget GetView介绍 GetView只是对已注册的Controller有一个名为controller的getter的const Stateless的Widget,如果我们只有单个控制器作为依赖项,那我们就可以使用GetView,而不是使用StatelessWidget,并且避免了写Get.Find()。 视频教程地址 零基础视频教程地址 GetView如何使用 GetView的使用方法非常简单,只是要将你的视图层继承自GetView并传入需要注册的控制器并Get.put()即可,我们来看下代码演示: 第一步:应用程序入口配置 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetViewAndGetWidgetExample/GetViewAndGetWidgetExample.dart'; import 'package:get/get.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return GetMaterialApp( title: "GetX", home: GetViewAndGetWidgetExample(), ); } } ``` 第二步:继承自GetView并使用状态管理 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetViewAndGetWidgetExample/GetViewCountController.dart'; import 'package:get/get.dart'; class GetViewAndGetWidgetExample extends GetView { @override Widget build(BuildContext context) { Get.put(GetViewCountController()); // Get.create(() => GetViewCountController()); return Scaffold( appBar: AppBar( title: Text("GetX GetView"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Obx(() => Text( "count的值为: ${controller?.count}", style: TextStyle( color: Colors.red, fontSize: 30 ), )), SizedBox(height: 20,), ElevatedButton( onPressed: () { controller.increment(); }, child: Text("点我加1")) ], ), ), ); } } ``` 效果展示 GetWidget介绍 它 “缓存 “了一个Controller,由于_cache_,不能成为一个 “const Stateless”(因为_cache_,所以不能成为一个const Stateless)。当我们使用Get.create(()=>Controller()) 会在每次调用时生成一个新的Controller Get.find()` GetWidget如何使用 第一步:应用程序入口设置 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetViewAndGetWidgetExample/GetViewAndGetWidgetExample.dart'; import 'package:get/get.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return GetMaterialApp( title: "GetX", home: GetViewAndGetWidgetExample(), ); } } ``` 第二步:继承自GetWidget并使用 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_example/GetViewAndGetWidgetExample/GetViewCountController.dart'; import 'package:get/get.dart'; class GetViewAndGetWidgetExample extends GetWidget { @override Widget build(BuildContext context) { // Get.put(GetViewCountController()); Get.create(() => GetViewCountController()); return Scaffold( appBar: AppBar( title: Text("GetX GetView"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Obx(() => Text( "count的值为: ${controller?.count}", style: TextStyle( color: Colors.red, fontSize: 30 ), )), SizedBox(height: 20,), ElevatedButton( onPressed: () { controller.increment(); }, child: Text("点我加1")) ], ), ), ); } } ``` 效果展示 总结 在我们平时的开发过程中基本上很少会用到GetWidget,因为我们在大部分情况下都不需要缓存Controller。 当我们的页面中只依赖了一个控制器的情况话,那么使用GetView将是很好的选择,因为他大大简化了我们的代码。 #### Flutter GetX系列教程---Cli使用以及常用命令 GetX Cli安装 第一步:安装Cli脚手架 我们通过命令flutter pub global activate get_cli 进行脚手架的全局安装,本文以Mac OS为例。 ``` JMdeMacBook-Pro:~ jm$ flutter pub global activate get_cli Resolving dependencies... + _fe_analyzer_shared 25.0.0 + analyzer 2.2.0 + ansicolor 2.0.1 + archive 3.1.2 + args 2.2.0 + async 2.8.2 + charcode 1.3.1 + cli_dialog 0.5.0 + cli_menu 0.3.0-nullsafety.0 + cli_util 0.3.3 + clock 1.1.0 + collection 1.15.0 + convert 3.0.1 + crypto 3.0.1 + dart_console 1.0.0 + dart_style 2.0.3 + ffi 1.1.2 + file 6.1.2 + get_cli 1.6.0 + glob 2.0.1 + http 0.13.3 + http_parser 4.0.0 + intl 0.17.0 + matcher 0.12.11 + meta 1.7.0 + package_config 2.0.0 + path 1.8.0 + pedantic 1.11.1 + process_run 0.12.1+1 + pub_semver 2.0.0 + pubspec 2.0.1 + quiver 3.0.1 + recase 4.0.0 + source_span 1.8.1 + stack_trace 1.10.0 + string_scanner 1.1.0 + synchronized 3.0.0 + term_glyph 1.2.0 + typed_data 1.3.0 + uri 1.0.0 + version 2.0.0 + watcher 1.0.0 + win32 2.2.9 + yaml 3.1.0 Downloading get_cli 1.6.0... Downloading version 2.0.0... Downloading recase 4.0.0... Downloading pubspec 2.0.1... Downloading cli_menu 0.3.0-nullsafety.0... Downloading cli_dialog 0.5.0... Downloading ansicolor 2.0.1... Downloading uri 1.0.0... Downloading process_run 0.12.1+1... Downloading synchronized 3.0.0... Downloading dart_console 1.0.0... Downloading quiver 3.0.1... Downloading dart_style 2.0.3... Downloading analyzer 2.2.0... Downloading _fe_analyzer_shared 25.0.0... Downloading win32 2.2.9... Building package executables... Built get_cli:get. Installed executables get and getx. Warning: Pub installs executables into $HOME/.pub-cache/bin, which is not on your path. You can fix that by adding this to your shell's config file (.bashrc, .bash_profile, etc.): export PATH="$PATH":"$HOME/.pub-cache/bin" Activated get_cli 1.6.0. ``` 第二步:设置环境变量 一般Mac的环境变量都是通过根目录的.bash_profile进行环境变量设置。 ``` #getX export PATH="$PATH":"$HOME/.pub-cache/bin" ``` 第三步:使设置的环境变量生效 我们可以通过source 加上路径,对环境变量生效。 1 JMdeMacBook-Pro:~ jm$ source "/Users/jm/.bash_profile" 第四步:校验是否成功安装 我们通过get命令,看能不能打印如下,如果有,那么恭喜你安装成功了。 ``` JMdeMacBook-Pro:~ jm$ get List available commands: create: controller: Generate controller page: Use to generate pages project: Use to generate new project provider: Create a new Provider screen: Generate new screen view: Generate view generate: locales: Generate translation file from json files model: generate Class model from json help: Show this help init: generate the chosen structure on an existing project: install: Use to install a package in your project (dependencies): remove: Use to remove a package in your project (dependencies): sort: Sort imports and format dart files update: To update GET_CLI --version: Shows the current CLI version' Time: 1814 Milliseconds ``` Cli创建工程 我们可以通过get create project来进行创建工程, ``` JMdeMacBook-Pro:getx jm$ get create project ``` 然后出现如下提示,我们选择创建Flutter Project ``` --> 1) Flutter Project 2) Get Server ``` 选择之后需要输入工程名称、公司域名、选择iOS语言、选择Android语言、是否空安全、是否校验,选完知道就会为我们开始创建工程。 ``` JMdeMacBook-Pro:getx jm$ get create project --> 1) Flutter Project 2) Get Server ? what is the name of the project? getx_example ? What is your company's domain? Example: com.yourcompany com.jimi what language do you want to use on ios? 1) Swift --> 2) Objective-C what language do you want to use on android? 1) Kotlin --> 2) Java Do you want to use null safe? --> 1) Yes! 2) No do you want to use some linter? 1) no 2) Pedantic [Deprecated] 3) Effective Dart [Deprecated] --> 4) Dart Recommended Running `flutter create /Users/jm/Desktop/Project/getx/getx_example` … $ flutter create --no-pub -i objc -a java --org com.jimi /Users/jm/Desktop/Project/getx/getx_example Creating project .... test/widget_test.dart (created) pubspec.yaml (created) README.md (created) lib/main.dart (created) windows/runner/flutter_window.cpp (created) windows/runner/utils.h (created) windows/runner/utils.cpp (created) windows/runner/runner.exe.manifest (created) windows/runner/CMakeLists.txt (created) windows/runner/win32_window.h (created) windows/runner/Runner.rc (created) windows/runner/win32_window.cpp (created) windows/runner/resources/app_icon.ico (created) windows/runner/main.cpp (created) windows/runner/resource.h (created) windows/runner/flutter_window.h (created) windows/flutter/CMakeLists.txt (created) windows/.gitignore (created) windows/CMakeLists.txt (created) ios/Runner.xcworkspace/contents.xcworkspacedata (created) ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (created) ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (created) ios/Runner/Info.plist (created) ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png (created) ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png (created) ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md (created) ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json (created) ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png (created) ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png (created) ios/Runner/Base.lproj/LaunchScreen.storyboard (created) ios/Runner/Base.lproj/Main.storyboard (created) ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata (created) ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (created) ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (created) ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (created) ios/Flutter/Debug.xcconfig (created) ios/Flutter/Release.xcconfig (created) ios/Flutter/AppFrameworkInfo.plist (created) ios/.gitignore (created) getx_example.iml (created) .gitignore (created) web/favicon.png (created) web/index.html (created) web/manifest.json (created) web/icons/Icon-maskable-512.png (created) web/icons/Icon-192.png (created) web/icons/Icon-maskable-192.png (created) web/icons/Icon-512.png (created) .metadata (created) macos/Runner.xcworkspace/contents.xcworkspacedata (created) macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (created) macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png (created) macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png (created) macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png (created) macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png (created) macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png (created) macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png (created) macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (created) macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png (created) macos/Runner/DebugProfile.entitlements (created) macos/Runner/Base.lproj/MainMenu.xib (created) macos/Runner/MainFlutterWindow.swift (created) macos/Runner/Configs/Debug.xcconfig (created) macos/Runner/Configs/Release.xcconfig (created) macos/Runner/Configs/Warnings.xcconfig (created) macos/Runner/Configs/AppInfo.xcconfig (created) macos/Runner/AppDelegate.swift (created) macos/Runner/Info.plist (created) macos/Runner/Release.entitlements (created) macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (created) macos/Runner.xcodeproj/project.pbxproj (created) macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (created) macos/Flutter/Flutter-Debug.xcconfig (created) macos/Flutter/Flutter-Release.xcconfig (created) macos/.gitignore (created) ios/Runner/AppDelegate.h (created) ios/Runner/main.m (created) ios/Runner/AppDelegate.m (created) ios/Runner.xcodeproj/project.pbxproj (created) android/app/src/profile/AndroidManifest.xml (created) android/app/src/main/res/mipmap-mdpi/ic_launcher.png (created) android/app/src/main/res/mipmap-hdpi/ic_launcher.png (created) android/app/src/main/res/drawable/launch_background.xml (created) android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (created) android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (created) android/app/src/main/res/values-night/styles.xml (created) android/app/src/main/res/values/styles.xml (created) android/app/src/main/res/drawable-v21/launch_background.xml (created) android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (created) android/app/src/main/AndroidManifest.xml (created) android/app/src/debug/AndroidManifest.xml (created) android/gradle/wrapper/gradle-wrapper.properties (created) android/gradle.properties (created) android/.gitignore (created) android/settings.gradle (created) android/app/build.gradle (created) android/app/src/main/java/com/jimi/getx_example/MainActivity.java (created) android/build.gradle (created) android/getx_example_android.iml (created) analysis_options.yaml (created) .idea/runConfigurations/main_dart.xml (created) .idea/libraries/Dart_SDK.xml (created) .idea/libraries/KotlinJavaRuntime.xml (created) .idea/modules.xml (created) .idea/workspace.xml (created) linux/main.cc (created) linux/my_application.h (created) linux/my_application.cc (created) linux/flutter/CMakeLists.txt (created) linux/.gitignore (created) linux/CMakeLists.txt (created) Wrote 129 files. All done! In order to run your application, type: $ cd . $ flutter run Your application code is in ./lib/main.dart. Running `flutter pub get` … $ flutter pub get Running "flutter pub get" in getx_example... 898ms $ dart migrate --apply-changes --skip-import-check Migrating /Users/jm/Desktop/Project/getx/getx_example See https://dart.dev/go/null-safety-migration for a migration guide. Analyzing project... All sources appear to be already migrated. Nothing to do. ✖ + HandshakeException: Connection terminated during handshake ✓ File: analysis_options.yaml created successfully at path: analysis_options.yaml --> 1) GetX Pattern (by Kauê) 2) CLEAN (by Arktekko) Your lib folder is not empty. Are you sure you want to overwrite your application? WARNING: This action is irreversible --> 1) Yes! 2) No ✓ 'Package: get installed! ✓ File: main.dart created successfully at path: lib/main.dart ✓ File: home_controller.dart created successfully at path: ./lib/app/modules/home/controllers/home_controller.dart ✓ File: home_view.dart created successfully at path: ./lib/app/modules/home/views/home_view.dart ✓ File: home_binding.dart created successfully at path: ./lib/app/modules/home/bindings/home_binding.dart ✓ File: app_routes.dart created successfully at path: lib/app/routes/app_routes.dart ✓ File: app_pages.dart created successfully at path: lib/app/routes/app_pages.dart ✓ home route created successfully. ✓ Home page created successfully. ✓ GetX Pattern structure successfully generated. Running `flutter pub get` … $ flutter pub get Running "flutter pub get" in getx_example... 968ms Time: 53925 Milliseconds ``` 如果能看到如上就证明通过Cli创建工程成功了。 Cli创建页面 第一种:Getx_pattern 我们可以通过get create page:login来快速创建一个页面,这个页面有controller、view、binding、routes等配置,结构是Getx_pattern ``` JMdeMacBook-Pro:getx_example jm$ get create page:login ✓ File: login_controller.dart created successfully at path: ./lib/app/modules/login/controllers/login_controller.dart ✓ File: login_view.dart created successfully at path: ./lib/app/modules/login/views/login_view.dart ✓ File: login_binding.dart created successfully at path: ./lib/app/modules/login/bindings/login_binding.dart ✓ login route created successfully. ✓ Login page created successfully. Time: 453 Milliseconds JMdeMacBook-Pro:getx_example jm$ ``` 第二种:CLEAN 我们可以通过get create page:login来快速创建一个页面,这个页面有controller、view、binding、routes等配置,结构是CLEAN。 ``` JMdeMacBook-Pro:getx_example jm$ get create screen:name ✓ File: name.controller.dart created successfully at path: ./lib/presentation/name/controllers/name.controller.dart ✓ File: name.screen.dart created successfully at path: ./lib/presentation/name/name.screen.dart ✓ File: name.controller.binding.dart created successfully at path: lib/infrastructure/navigation/bindings/controllers/name.controller.binding.dart ✓ File: routes.dart created successfully at path: lib/infrastructure/navigation/routes.dart ✓ File: routes.dart created successfully at path: lib/infrastructure/navigation/routes.dart ✓ name route created successfully. ✓ File: navigation.dart created successfully at path: lib/infrastructure/navigation/navigation.dart ✓ Name navigation added successfully. Time: 482 Milliseconds ``` Cli创建控制器 我们可以通过get create controller:login来快速为已存在的page进行创建controller,并且他还重新了controller的生命周期以及初始化方法。 ``` get create controller:another on home ✓ File: another_controller.dart created successfully at path: ./lib/app/modules/home/controllers/another_controller.dart ✓ The Another has been added to binding at path: lib/app/modules/home/bindings/home_binding.dart' Time: 387 Milliseconds JMdeMacBook-Pro:getx_example jm$ ``` Cli创建View 如果我们只是想单独创建一个View,那我们可以通过get create view:alogin on login来进行创建。 ``` get create view:alogin on login ✓ File: alogin_view.dart created successfully at path: ./lib/app/modules/login/views/alogin_view.dart Time: 378 Milliseconds JMdeMacBook-Pro:getx_example jm$ ``` Cli创建Provider 如果我们需要创建GetConnect,那我们可以通过get create provider:blogin on login来进行创建。 ``` JMdeMacBook-Pro:getx_example jm$ get create provider:blogin on login ✓ File: blogin_provider.dart created successfully at path: ./lib/app/modules/login/providers/blogin_provider.dart Time: 357 Milliseconds JMdeMacBook-Pro:getx_example jm$ ``` Cli创建国际化 如果我们需要对应用进行国际化配置,那我们可以通过get generate locales assets/locales来进行创建。 ``` JMdeMacBook-Pro:getx_example jm$ get generate locales assets/locales ✓ File: locales.g.dart created successfully at path: lib/generated/locales.g.dart ✓ locale files generated successfully. Time: 416 Milliseconds JMdeMacBook-Pro:getx_example jm$ ``` Cli通过json生成模型 第一种:不仅创建模型类,还会提供Provider 如果我们需要对某个json文件生成模型,那我们可以通过get generate model on home with assets/models/user.json来进行生成 ``` JMdeMacBook-Pro:getx_example jm$ get generate model on home with assets/models/user.json ✓ File: user_model.dart created successfully at path: ./lib/app/modules/home/user_model.dart ✓ File: user_provider.dart created successfully at path: ./lib/app/modules/home/providers/user_provider.dart Time: 499 Milliseconds JMdeMacBook-Pro:getx_example jm$ ``` 第二种:只生成模型类 ``` JMdeMacBook-Pro:getx_example jm$ get generate model on login with assets/models/user.json --skipProvider ✓ File: user_model.dart created successfully at path: ./lib/app/modules/login/user_model.dart Time: 408 Milliseconds ``` 第三种:通过连接生成模型类 ``` JMdeMacBook-Pro:getx_example jm$ get generate model on home from "https://api.github.com/users/CpdnCristiano" ? Could not set the model name automatically, which name do you want to use? githubHome ✓ File: github_home_model.dart created successfully at path: ./lib/app/modules/home/github_home_model.dart ✓ File: github_home_provider.dart created successfully at path: ./lib/app/modules/home/providers/github_home_provider.dart Time: 14033 Milliseconds ``` Cli安装包(dependencies) 第一种:直接安装最新版本 ``` JMdeMacBook-Pro:getx_example jm$ get install dio Installing package "dio" … ✓ 'Package: dio installed! Running `flutter pub get` … $ flutter pub get Running "flutter pub get" in getx_example... 2,656ms Time: 5815 Milliseconds ``` 第二种:同时安装多个包 ``` JMdeMacBook-Pro:getx_example jm$ get install path dio Installing package "path" … ✓ 'Package: path installed! Installing package "dio" … ✓ 'Package: dio installed! Running `flutter pub get` … $ flutter pub get Running "flutter pub get" in getx_example... 732ms Time: 7146 Milliseconds ``` 第三种:安装自定版本的包 ``` JMdeMacBook-Pro:getx_example jm$ get install prodiver:5.0.0 Installing package "prodiver" … ✓ 'Package: prodiver installed! ``` Cli安装包(dev_dependencies) 我们可以通过get install flutter_launcher_icons --dev安装开发时所依赖的包 ``` JMdeMacBook-Pro:getx_example jm$ get install flutter_launcher_icons --dev The [--dev] is not necessary Installing package "flutter_launcher_icons" … ✓ 'Package: flutter_launcher_icons installed! ``` Cli卸载包 第一种:卸载某个安装包 ``` JMdeMacBook-Pro:getx_example jm$ get remove http Removing package: "http" Package: http is not installed in this application Running `flutter pub get` … $ flutter pub get Running "flutter pub get" in getx_example... 772ms Time: 2641 Milliseconds ``` 第二种:同时卸载多个包 ``` JMdeMacBook-Pro:getx_example jm$ get remove dio path Removing package: "dio" ✓ Package: dio removed! Removing package: "path" ✓ Package: path removed! Running `flutter pub get` … $ flutter pub get Running "flutter pub get" in getx_example... 1,085ms Time: 3142 Milliseconds ``` Cli更新脚手架 我们可以通过get update对脚手架进行更新 ``` JMdeMacBook-Pro:getx_example jm$ get update Latest version of get_cli already installed Time: 3315 Milliseconds ``` Cli查看版本号 我们可以通过get -v 查看当前脚手架的版本号 ``` JMdeMacBook-Pro:getx_example jm$ get -v ░██████╗░███████╗████████╗   ░█████╗░██╗░░░░░░██╗ ██╔════╝░██╔════╝╚══██╔══╝   ██╔══██╗██║░░░░░░██║ ██║░░██╗░█████╗░░░░░██║░░░   ██║░░╚═╝██║░░░░░░██║ ██║░░╚██╗██╔══╝░░░░░██║░░░   ██║░░██╗██║░░░░░░██║ ╚██████╔╝███████╗░░░██║░░░   ╚█████╔╝███████╗ ██║ ░╚═════╝░╚══════╝░░░╚═╝░░░   ░╚════╝░╚══════╝ ╚═╝ Version: 1.6.0 Time: 148 Milliseconds ``` Cli帮助 当我们忘记了命令的使用方式,我们可以通过get help进行查看帮助。 ``` JMdeMacBook-Pro:getx_example jm$ get help List available commands: create: controller: Generate controller page: Use to generate pages project: Use to generate new project provider: Create a new Provider screen: Generate new screen view: Generate view generate: locales: Generate translation file from json files model: generate Class model from json help: Show this help init: generate the chosen structure on an existing project: install: Use to install a package in your project (dependencies): remove: Use to remove a package in your project (dependencies): sort: Sort imports and format dart files update: To update GET_CLI --version: Shows the current CLI version' Time: 94 Milliseconds ``` 总结 以上就是对Cli的脚手架命令的一个实践和总结,相对于自己手动创建结构,用Cli就可以很轻松的实现。 #### Flutter GetX---RxList、Rx([])、.obs对比分析 前言 首先我们知道GetX组件里面obs状态管理有三种创建属性的方式,我们这里以List为例 Rx([]) RxList .obs 三种方式对比分析 我们声明了一个类ListController继承自GetxController,用于属性创建以及状态通知的方法,首先我们用三种方式来创建属性并且通过convertToUpperCase方法进行对值的改变,然后我们通过调用update()`方法来进行数据更新,最后我们使用该属性状态的值,接下来我们看一下三种使用方式的对比。 第一种Rx([]) 第二种RxList 第三种 .obs ``` import 'dart:convert'; import 'package:get/get.dart'; class ListController extends GetxController { // 第一种 final listOne = Rx>([ { "name": "Jimi", "age": 18 } ]); // 第二种 final listTwo = RxList([ { "name": "Jimi", "age": 18 } ]); // 第三种 final listThree = [{ "name": "Jimi", "age": 18 }].obs; void convertToUpperCase() { listOne.value[0]["name"] = listOne.value.first["name"].toUpperCase(); listTwo.toList().first["name"] = listTwo.toList().first["name"].toString().toUpperCase(); listThree.toList().first["name"] = listTwo.toList().first["name"].toString().toUpperCase(); update(); } } ``` 我们在页面中获取状态更新的值 ``` import 'package:flutter/material.dart'; import 'package:flutter_getx_dvanced_example/ListController.dart'; import 'package:get/get.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { ListController listController = Get.put(ListController()); @override Widget build(BuildContext context) { return GetMaterialApp( title: "GetX", home: Scaffold( appBar: AppBar( title: Text("GetX"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ GetBuilder( init: listController, builder: (controller) { return Text( "我的名字是 ${controller.listOne.value.first['name']}", style: TextStyle(color: Colors.orange, fontSize: 30), ); }, ), SizedBox(height: 20,), GetBuilder( init: listController, builder: (controller) { return Text( "我的名字是 ${controller.listTwo.first['name']}", style: TextStyle(color: Colors.green, fontSize: 30), ); }, ), SizedBox(height: 20,), GetBuilder( init: listController, builder: (controller) { return Text( "我的名字是 ${controller.listThree.first['name']}", style: TextStyle(color: Colors.green, fontSize: 30), ); }, ), SizedBox(height: 20,), ElevatedButton( onPressed: () { listController.convertToUpperCase(); }, child: Text("转换为大写")) ], ), ), ), ); } } ``` 效果展示 Rx([])源码分析 Rx 继承自_RxImpl,_RxImpl又继承RxNotifier并混合 RxObjectMixin 类 RxImpl它主要的作用是管理泛型的所有逻辑的。 RxObjectMixin 它主要的作用是管理注册到GetX和Obx的全局对象,比如Widget的Rx值 Rx它主要的作用是将自定义模型类用Rx`来进行包装, ``` class Rx extends _RxImpl { Rx(T initial) : super(initial); @override dynamic toJson() { try { return (value as dynamic)?.toJson(); } on Exception catch (_) { throw '$T has not method [toJson]'; } } } abstract class _RxImpl extends RxNotifier with RxObjectMixin { _RxImpl(T initial) { _value = initial; } void addError(Object error, [StackTrace? stackTrace]) { subject.addError(error, stackTrace); } Stream map(R mapper(T? data)) => stream.map(mapper); void update(void fn(T? val)) { fn(_value); subject.add(_value); } void trigger(T v) { var firstRebuild = this.firstRebuild; value = v; if (!firstRebuild) { subject.add(v); } } } ``` RxList源码分析 RxList继承自ListMixin实现了RxInterface>并混合了NotifyManager>, RxObjectMixin> RxList它的主要作用是创建一个类似于List 的一个列表 ``` class RxList extends ListMixin with NotifyManager>, RxObjectMixin> implements RxInterface> { RxList([List initial = const []]) { _value = List.from(initial); } factory RxList.filled(int length, E fill, {bool growable = false}) { return RxList(List.filled(length, fill, growable: growable)); } factory RxList.empty({bool growable = false}) { return RxList(List.empty(growable: growable)); } /// Creates a list containing all [elements]. factory RxList.from(Iterable elements, {bool growable = true}) { return RxList(List.from(elements, growable: growable)); } /// Creates a list from [elements]. factory RxList.of(Iterable elements, {bool growable = true}) { return RxList(List.of(elements, growable: growable)); } /// Generates a list of values. factory RxList.generate(int length, E generator(int index), {bool growable = true}) { return RxList(List.generate(length, generator, growable: growable)); } /// Creates an unmodifiable list containing all [elements]. factory RxList.unmodifiable(Iterable elements) { return RxList(List.unmodifiable(elements)); } @override Iterator get iterator => value.iterator; @override void operator []=(int index, E val) { _value[index] = val; refresh(); } /// Special override to push() element(s) in a reactive way /// inside the List, @override RxList operator +(Iterable val) { addAll(val); refresh(); return this; } @override E operator [](int index) { return value[index]; } @override void add(E item) { _value.add(item); refresh(); } @override void addAll(Iterable item) { _value.addAll(item); refresh(); } @override int get length => value.length; @override @protected List get value { RxInterface.proxy?.addListener(subject); return _value; } @override set length(int newLength) { _value.length = newLength; refresh(); } @override void insertAll(int index, Iterable iterable) { _value.insertAll(index, iterable); refresh(); } @override Iterable get reversed => value.reversed; @override Iterable where(bool Function(E) test) { return value.where(test); } @override Iterable whereType() { return value.whereType(); } @override void sort([int compare(E a, E b)?]) { _value.sort(compare); refresh(); } } ``` .obs源码分析 当我们在调用.obs的时候其实内部的实现源码还是通过RxList(this)进行了一层包装,设计这个主要的目的就是为了方便开发者进行使用 ``` extension ListExtension on List { RxList get obs => RxList(this); /// Add [item] to [List] only if [item] is not null. void addNonNull(E item) { if (item != null) add(item); } // /// Add [Iterable] to [List] only if [Iterable] is not null. // void addAllNonNull(Iterable item) { // if (item != null) addAll(item); // } /// Add [item] to List only if [condition] is true. void addIf(dynamic condition, E item) { if (condition is Condition) condition = condition(); if (condition is bool && condition) add(item); } /// Adds [Iterable] to [List] only if [condition] is true. void addAllIf(dynamic condition, Iterable items) { if (condition is Condition) condition = condition(); if (condition is bool && condition) addAll(items); } /// Replaces all existing items of this list with [item] void assign(E item) { // if (this is RxList) { // (this as RxList)._value; // } clear(); add(item); } /// Replaces all existing items of this list with [items] void assignAll(Iterable items) { // if (this is RxList) { // (this as RxList)._value; // } clear(); addAll(items); } } ``` 总结 我们对Rx([])、RxList、.obs进行了一个总结,在我们平时的开发过程中建议大家使用.obs即可,因为这是最简单的方式。