Flutter 状态管理框架Get

状态管理框架 Get的使用

目录

[状态管理框架 Get的使用](#状态管理框架 Get的使用)

GetMaterialApp

路由的注册

路由的跳转

middlewares的使用

[组件使用 defaultDialog bottomSheet snackbar](#组件使用 defaultDialog bottomSheet snackbar)

状态刷新有很多种方式

ValueBuilder

[Obx 基础使用](#Obx 基础使用)

[是时候引入GetxController, 也是Get里面的常用的](#是时候引入GetxController, 也是Get里面的常用的)

GetX

[优化控制器的使用 put](#优化控制器的使用 put)

优化控制器的使用find

[优化控制器的使用 懒加载lazyput](#优化控制器的使用 懒加载lazyput)

GetView

接下来GetConnect就是网络请求的使用

GetConnect

GetConnectStatemixin

GetConnectDio

切换主题

多语言切换

常使用的Getx自带的API


不得不说,GetX使你的代码量减少了许多, 一个obs. 一个obx. 搞定你的数据监听刷新, 给你不一样的简洁。

GetMaterialApp

Dart 复制代码
 return GetMaterialApp(
      title: 'Flutter Demo',
      getPages: MyRoutes.routes, //注册路由
      // initialRoute: MyHomePage.route,  //该属性, 设置有有问题, 在自定义转场动画的时候, 跳转的时候右边会黑屏 
      unknownRoute: MyRoutes.notFoundPage, //默认404的路由地址
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );

// initialRoute: MyHomePage.route,//该属性,在自定义转场动画的时候, 跳转的时候右边会黑屏

路由的注册

Dart 复制代码
class MyRoutes {
  static List<GetPage> routes = [
     //正常注册
    GetPage(name: MySettingPage.route, page: () => const MySettingPage()),
    GetPage(name: MyHomePage.route, page: () => const MyHomePage(), 
    children: [
      //children 这里可以注册子页面, 一级, 二级, 三级, 页面都可以
      GetPage(name: MyProductPage.route, page: () => const MyProductPage(), 
          children: [
          GetPage(name: MyProductDetailPage.route, page: () => const MyProductDetailPage()),
          GetPage(name: MySettingPage.route, page: () => const MySettingPage()),
          GetPage(name: MySettingPage.routeID, page: () => const MySettingPage()),
      ]), 
    ]),
  ];
}

路由的跳转

Dart 复制代码
//使用路由String跳转, 这样写, 注册的时候 /MyProductPage 必须要在 MyHomePage 的children: [ 里面] 如上展示
Get.toNamed("/MyHomePage/MyProductPage");
Get.toNamed("/MyHomePage/MyProductPage/MySettingPage");
//直接跳转对应的页面
Get.to(const MyProductPage());
//使用路由跳转并传值
Get.toNamed("/MyHomePage/MyProductPage/MySettingPage?id==345");

//返回上一个页面, 并传值, 接收参数的 var result = await Get.toNamed("/MyHomePage/MyProductPage/MySettingPage?id==345");
Get.back(result: {"success": true}),

//特殊路由传值 需要定义路由: "/MySettingPage/:id";, 才能使用下方跳转传值
Get.toNamed("/MyHomePage/MyProductPage/MySettingPage/8910");

//跳转后, 消除上页面的堆栈
Get.off(const MyProductDetailPage());
Get.offNamed(MyProductDetailPage.route);

//跳转后, 消除所有的堆栈
Get.offAll(const MyProductDetailPage());
Get.offAllNamed(MyProductDetailPage.route);

middlewares的使用

一个类似拦截器的功能,可以传入多个,需要自定研究下优先级。

Dart 复制代码
 GetPage(name: MyMinePage.route, page: () => const MyMinePage(), middlewares: [        MyRouteAuthMiddleware()]),
class MyRouteAuthMiddleware extends GetMiddleware {
  @override
  RouteSettings? redirect(String? route) {
    // 加入 AuthService 这里可以判断下用户是否登录,  如果true 
    return super.的方法
    //否则去都登陆页面
    Future.delayed(const Duration(seconds: 1), () => Get.snackbar("提示", "请先登录APP"));
    return const RouteSettings(name: MyLoginPage.route);
  }
}

组件使用 defaultDialog bottomSheet snackbar

Dart 复制代码
Get.snackbar
Get.bottomSheet
Get.defaultDialog
自行调用, 没有难度

状态刷新有很多种方式

ValueBuilder

爱了, 爱了, 直接导入头文件, 只刷新该作用域的.

Dart 复制代码
ValueBuilder<List<String>?>(
    initialValue: const ["A", "B", "C"],
    builder: (value, updateFn) {
      return Column(children: [
        Text("List -> $value"),
        ElevatedButton(
          onPressed: () {
            List<String> newList = [...value!]; // 使用扩展运算符来创建列表的浅拷贝
            newList.add("${newList.length}");
            // newList.removeAt(1); // 移除索引为1的元素
            newList.shuffle();//元素随机
            updateFn(newList); // 使用新列表更新状态
          },
          child: const Text('ValueBuilder -> add'),
        )
      ]);
    }),   

Obx 基础使用

比如在Widget, 声明一个属性, 使用的时候使用Obx(()=>{}) 包裹一下,然后在其他地方点击,修改count值就OK了, 因为增加了obs, 就成了, count.value++ 来修改值

Dart 复制代码
//Int
RxInt count = 0.obs;
var count1 = 0.obs;
Obx(() => Column(
      children: [
         Text("int -> $count"),
         Text("int -> $count1"),
])

//刷新
updateCount() {
      count.value++;
      count1.value++;
 }
在监听list. map. 枚举 的时候要注意, 一定要从新赋值才会刷新

//刷新枚举
 void updateViewState(ViewState newValue) {  
    viewState.value = newValue; // 更新枚举值  
  }  
//刷新list
 updateList() {
      list[0] = "BMW";
      list1[0] = "BMW"; 
}
//刷新Map
updateMap() {
     map["Audi"] = "BMW";
     map1["Audi"] = "BMW";
}
//刷新List中的Model
updateModels() {
      models[0] = GetXModels(name: "BMW");
      models1[0] = GetXModels(name: "BMW");
    }
//刷新Model
updateModel() {
      model.value = GetXModels(name: "BMW");
      model1.value = GetXModels(name: "BMW");
}
使用的地方一地个要加上Obx()

是时候引入GetxController, 也是Get里面的常用的

GetX<MyGetxController>

可以包裹引用Controller值的地方, 进行局部刷新, 还有声明周期的回调 推荐 ☆☆☆

Dart 复制代码
//要创建控制器Controller
class MyPutController extends GetxController {}
//在widget的build方法中
final MyGetxController getxController = MyGetxController();
//使用例子
 ListTile(
  title: const Text("update list"),
  subtitle: GetX<MyGetxController>(
    init: getxController,
    initState: (_) {},
    builder: (_) {
      return Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('我要买${getxController.list.toString()}'),
          Text('我要买${getxController.list1.toString()}'),
        ],
      );
    },
  ),
  onTap: () {
    //list, 要修改list里面的元素, 才会去更新UI
    getxController.updateList();
  },
),

优化控制器的使用 put

//在Widget的build方法里面我们注册一个controller 推荐 ☆☆☆

Dart 复制代码
final MyPutController countController = Get.put<MyPutController>(MyPutController());
在使用过程中使用Obx(())包裹即可, 
ListTile(
  title: const Text("update Count"),
  subtitle: Obx(() => Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('count = ${countController.count.value}'),
          Text('count = ${countController.count1.value}'),
        ],
      )),
  onTap: () {
    countController.updateCount();
  },
),

优化控制器的使用find

//在put过MyProductController()的Widget的build方法里面即二级页面, 三级页面....子页面

Dart 复制代码
final MyProductController productController = Get.find<MyProductController>();
在使用过程中使用Obx(())包裹即可, 也可以使用GetX<MyProductController>build 也可以
ListTile(
     title: Text("传值 Get.arguments; = ${details.toString()} parameters == ${parameters.toString()}"),
     subtitle: Obx(() => Text(productController.myProductList.length.toString())),
     onTap: () {
        productController.addProduct(MyProduct(name: 'iPhone 16', description: 'APPle 设备', price: 8199));
        productController.addCount();
     }),

优化控制器的使用 懒加载lazyput

这里就要引出Binding的使用了

Dart 复制代码
class MyGetLazyPutBinding implements Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => MyGetLazyPutController());
  }
}
// MyGetLazyPutController 上面说的controller. 必须创建, 切继承GetxController
//注册路由的时候
GetPage(name: MyLazyPutPage.route, page: () => const MyLazyPutPage(),binding: MyGetLazyPutBinding()),
//在Widget页面的时候就,不用调用Getx.put的方法, 会自动Put
//直接去find找到该Controller
final MyGetLazyPutController getLazyPutController = Get.find<MyGetLazyPutController>();
//在使用过程中使用Obx(())包裹即可, 也可以使用GetX<MyProductController>build 也可以
//建议自己撸一遍代码, 会印象更加深刻

GetView

这个更加简单, 省略put, find 的步骤, 不过还是要一个Controller的

Dart 复制代码
//创建Widget的时候,需要增加一个泛型
class MyGetViewPage extends GetView<MyGetLazyPutController> 
//上面的路由注册, 还是要注册一个Controller
GetPage(name: MyLazyPutPage.route, page: () => const MyLazyPutPage(),binding: MyGetLazyPutBinding()),
//在widget中, 就自带了controller的变量 ,可以翻看源码中
abstract class GetView<T> extends StatelessWidget {
  const GetView({Key? key}) : super(key: key);
  final String? tag = null;
  T get controller => GetInstance().find<T>(tag: tag)!;
  @override
  Widget build(BuildContext context);
}
//就会知道, 内部帮忙find_Controller

接下来GetConnect就是网络请求的使用

GetConnect

这里跟官网的不太一样, 官网我感觉有点麻烦, 创建那么多的文件, 其实就是请求层, 数据层, 页面布局划分就好了

Dart 复制代码
//1.我们先继承GetConnect 设置下你的请求相关配置
//如: baseUrl headers 以及其他设置
class MyBaseHttp extends GetConnect {
  @override
  void onInit() {
    httpClient.baseUrl = "xxxxxx";
    // 请求拦截
    httpClient.addRequestModifier<void>((request) {
      Map<String, String> headerMap = {
        "os-type": GetPlatform.isIOS ? "ios" : "android",
        "timestamp": DateTime.now().microsecondsSinceEpoch.toString(),
      };
      request.headers.addAll(headerMap);
      return request;
    });
    // 响应拦截
    httpClient.addResponseModifier((request, response) {
      return response;
    });
  }
}
//2.然后继承 MyBaseHttp, 创建你的serviceController
class MyServiceController extends MyBaseHttp {
  //获取数据, 自定义数据
  Future<MyGetConnectModel> getContent() async {
    Response response = await get('/route/external_link.json'); //这里去请求的, 有post. put. 自己看下API
    if (response.statusCode == 200) {
      debugPrint(response.bodyString);
      return MyGetConnectModel(userId: 1, id: 2, title: 'title', body: 'body');
    } else {
      debugPrint(response.bodyString);
      return MyGetConnectModel(userId: 1, id: 2, title: 'title', body: 'body');
    }
  }
}
//3.创建页面的controller
class MyGetConnectController extends GetxController {
  // 获取实例
  final MyServiceController serviceController = Get.find<MyServiceController>();
  // model
  late MyGetConnectModel getConnectModel;
  //监听状态, 这里是一个枚举, 这里你也可以监听其他的,比如model的变化
  Rx<ViewState> viewState = ViewState.normal.obs;

  getData() async {
    updateViewState(ViewState.loading);
    getConnectModel = await serviceController.getContent();
    updateViewState(ViewState.normal);
  }

  void updateViewState(ViewState newValue) {  
    viewState.value = newValue; // 更新枚举值  
  }  
}
//4.注册
class MyGetContentBuinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => MyGetConnectService());
    Get.lazyPut(() => MyGetConnectController());
  }
}
//路由binding
GetPage(name: MyGetContentPage.route, page: () => const MyGetContentPage(),binding: MyGetContentBuinding()),
//5.创建Widget,布局
方式1: class MyGetContentPage extends GetView<MyGetConnectController> {}
方式2: class MyHomePage extends StatelessWidget{
final controller = Get.find<MyGetConnectController>();}
//数据刷新
GetX<MyGetConnectController>(initState: (state) {
  controller.getData();
}, init: controller,
builder: ((_) {
  debugPrint("controller.viewState.value: ${controller.viewState.value}")
  switch (controller.viewState.value) {
    case ViewState.loading:
      return const Center(
        child: CircularProgressIndicator(),
      );
    default:
     return _buildListView(controller.getConnectModel);
  }
})),
或者
Obx(()=>)
更新值的时候, 需要 controller哟

GetConnectStatemixin

Dart 复制代码
//跟上面基本一致, 这里说下不同点
//创建页面控制器的时候需要增加监听的类型数据
class MyGetConnectStateMixinController extends GetxController with StateMixin<List<MyGetConnectModel>> { }
//List<MyGetConnectModel>就是我要监听的数据类型
//创建页面方式
class MyGetConnectStateMixinPage extends GetView<MyGetConnectStateMixinController> {}
也可以使用find的方式找到该 MyGetConnectStateMixinController
然后通过controller.obx 进行监听处理, 有多种状态,
  final bool isLoading;
  final bool isError;
  final bool isSuccess;
  final bool isEmpty;
  final bool isLoadingMore;
  final String? errorMessage;
// 是在页面的MyGetConnectStateMixinController 控制器里面进行定义的.相关代码
 @override
  void onInit() {
    fetchList(); //获取数据
    super.onInit();
  }

  // 拉取数据列表
  Future<void> fetchList() async {
    // 获取数据, 也可以处理好处理传过来,
    final Response response = await connectStateMixinService.getContent(); //通过find找到该控制器 connectStateMixinService, 是继承上面的MyBaseHttp的请求类控制器
    // 判断请求是否有错误
    if (response.hasError) {
      // 改变数据,传入错误状态,在ui中会处理这些错误
     //返回状态的定义
      // change(null, status: RxStatus.error(response.statusText));
      // change(null, status: RxStatus.empty());
    } else {
      // 成功,自定义数据,改变状态为成功
      List<MyGetConnectModel> dataList = [];
      for (var i = 0; i < 10; i++) {
        MyGetConnectModel getConnectModel = MyGetConnectModel(userId: 12312, id: 231231, title: "title", body: "body");
        dataList.add(getConnectModel);
      }
      change(dataList, status: RxStatus.success());
    }

//页面布局Widget, 根据不同的状态, 可以写不同的结果
controller.obx(
  (state) {
    return ListView.separated(
      itemCount: state!.length,
      itemBuilder: (context, index) {
        final MyGetConnectModel getConnectModel = state[index];
        return ListTile(
          onTap: () {},
          title: Text(getConnectModel.title),
          trailing: Text("\$${getConnectModel.body}"),
        );
      },
      separatorBuilder: (BuildContext context, int index) {
        return const Divider();
      },
    );
  },
  onError: (error) {
    return Center(
      child: Text(error.toString()),
    );
  },
  onLoading: const SizedBox(
    width: double.infinity,
    height: double.infinity,
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        CircularProgressIndicator(),
        SizedBox(height: 10),
        Text(
          "疯狂加载中...",
          style: TextStyle(color: Colors.blue, fontSize: 16),
        ),
      ],
    ),
  ),
  onEmpty: const Center(
    child: Text("没有数据"),
  ),
));

GetConnectDio

需要自行导入Dio的框架

把上面的 GetConnect 部分中的请求部分, 更换为Dio请求, 然后使用Rx定义,监听的变量。

Dart 复制代码
//在Widget中, 通过Obx, 或者GetX<MyGetConnectController> 去刷新数据即可
class MyGetConnectDioPage extends GetView<MyGetConnectDioController> 

切换主题

Dart 复制代码
return Scaffold(
      appBar: AppBar(
        title: const Text("切换主题"),
      ),
      body: Center(
          child: ElevatedButton(
        onPressed: () {
          Get.changeTheme(Get.isDarkMode ? ThemeData.light() : ThemeData.dark());
        },
        child: Text("是否黑色主题 -> ${Get.isDarkMode}"),
      )),
    );

多语言切换

Dart 复制代码
class MyTranslationService extends Translations {
  static Locale? get locale => Get.deviceLocale;
  static const fallbackLocale = Locale('en', 'US');

  @override
  Map<String, Map<String, String>> get keys => {
        'en_US': enUS,  
        'zh_Hans': zhHans,
        'zh_HK': zhHK,
      };
}
//enUS  zhHans zhHK 要去创建文件 如: 
const Map<String, String> zhHans = {
  'title': '这是标题',
  'login': '登录用户 @name,邮箱账号 @email',
};

GetMaterialApp下:
  locale: MyTranslationService.locale,
  fallbackLocale: MyTranslationService.fallbackLocale,
  translations: MyTranslationService(),
// 使用
Text(
  "title -> ${'title'.tr}"),
Text(
  "login -> ${'login'.trParams({'name': 'xxx', 'email': 'xxx@gmail.com'})}"),

//使用tr. trParams 来处理多语言
//设置多语音
var locale = const Locale('zh', 'HK');
Get.updateLocale(locale);

常使用的Getx自带的API

Dart 复制代码
Get.arguments

//给出以前的路由名称
Get.previousRoute

// 给出要访问的原始路由,例如,rawRoute.isFirst()
Get.rawRoute

// 允许从GetObserver访问Rounting API。
Get.routing

// 检查 snackbar 是否打开
Get.isSnackbarOpen

// 检查 dialog 是否打开
Get.isDialogOpen

// 检查 bottomsheet 是否打开
Get.isBottomSheetOpen

// 删除一个路由。
Get.removeRoute()

//反复返回,直到表达式返回真。
Get.until()

// 转到下一条路由,并删除所有之前的路由,直到表达式返回true。
Get.offUntil()

// 转到下一个命名的路由,并删除所有之前的路由,直到表达式返回true。
Get.offNamedUntil()

//检查应用程序在哪个平台上运行。
GetPlatform.isAndroid
GetPlatform.isIOS
GetPlatform.isMacOS
GetPlatform.isWindows
GetPlatform.isLinux
GetPlatform.isFuchsia

//检查设备类型
GetPlatform.isMobile
GetPlatform.isDesktop
//所有平台都是独立支持web的!
//你可以知道你是否在浏览器内运行。
//在Windows、iOS、OSX、Android等系统上。
GetPlatform.isWeb


// 相当于.MediaQuery.of(context).size.height,
//但不可改变。
Get.height
Get.width

// 提供当前上下文。
Get.context

// 在你的代码中的任何地方,在前台提供 snackbar/dialog/bottomsheet 的上下文。
Get.contextOverlay

// 注意:以下方法是对上下文的扩展。
// 因为在你的UI的任何地方都可以访问上下文,你可以在UI代码的任何地方使用它。

// 如果你需要一个可改变的高度/宽度(如桌面或浏览器窗口可以缩放),你将需要使用上下文。
context.width
context.height

// 让您可以定义一半的页面、三分之一的页面等。
// 对响应式应用很有用。
// 参数: dividedBy (double) 可选 - 默认值:1
// 参数: reducedBy (double) 可选 - 默认值:0。
context.heightTransformer()
context.widthTransformer()

/// 类似于 MediaQuery.of(context).size。
context.mediaQuerySize()

/// 类似于 MediaQuery.of(context).padding。
context.mediaQueryPadding()

/// 类似于 MediaQuery.of(context).viewPadding。
context.mediaQueryViewPadding()

/// 类似于 MediaQuery.of(context).viewInsets。
context.mediaQueryViewInsets()

/// 类似于 MediaQuery.of(context).orientation;
context.orientation()

///检查设备是否处于横向模式
context.isLandscape()

///检查设备是否处于纵向模式。
context.isPortrait()

///类似于MediaQuery.of(context).devicePixelRatio。
context.devicePixelRatio()

///类似于MediaQuery.of(context).textScaleFactor。
context.textScaleFactor()

///查询设备最短边。
context.mediaQueryShortestSide()

///如果宽度大于800,则为真。
context.showNavbar()

///如果最短边小于600p,则为真。
context.isPhone()

///如果最短边大于600p,则为真。
context.isSmallTablet()

///如果最短边大于720p,则为真。
context.isLargeTablet()

///如果当前设备是平板电脑,则为真
context.isTablet()

///根据页面大小返回一个值<T>。
///可以给值为:
///watch:如果最短边小于300
///mobile:如果最短边小于600
///tablet:如果最短边(shortestSide)小于1200
///desktop:如果宽度大于1200
context.responsiveValue<T>()

状态管理框架 Provider 和 Get 的Likes, 各有千秋

相关推荐
旭日猎鹰5 小时前
Flutter踩坑记录(三)-- 更改入口执行文件
flutter
旭日猎鹰5 小时前
Flutter踩坑记录(一)debug运行生成的项目,不能手动点击运行
flutter
️ 邪神5 小时前
【Android、IOS、Flutter、鸿蒙、ReactNative 】自定义View
flutter·ios·鸿蒙·reactnative·anroid
比格丽巴格丽抱16 小时前
flutter项目苹果编译运行打包上线
flutter·ios
SoaringHeart16 小时前
Flutter进阶:基于 MLKit 的 OCR 文字识别
前端·flutter
AiFlutter21 小时前
Flutter通过 Coap发送组播
flutter
老码沉思录1 天前
React Native 全栈开发实战班 - 性能与调试之打包与发布
javascript·react native·react.js
老码沉思录2 天前
React Native 全栈开发实战班 - 性能与调试之内存管理
javascript·react native·react.js
前端郭德纲2 天前
ReactNative的环境搭建
javascript·react native·react.js
嘟嘟叽2 天前
初学 flutter 环境变量配置
flutter