flutter getx路由管理、状态管理、路由守卫中间件、永久储存get_storage

news2025/12/14 21:37:49

一个简单的路由跳转、状态管理

目录

lib/
├── main.dart
├── routes/index.dart         // 路由表
├── middlewares/auth_middleware.dart  // 登录守卫
├── pages/
│   ├── home_page.dart
│   ├── login_page.dart
│   └── profile_page.dart
└── controllers/auth_controller.dart  // 登录状态管理

✅ 1. 登录状态控制器(AuthController)

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

class AuthController extends GetxController {
  var isLoggedIn = false.obs;

  void login() => isLoggedIn.value = true;
  void logout() => isLoggedIn.value = false;
}

✅ 2. 路由守卫中间件(AuthMiddleware)

// middlewares/auth_middleware.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/auth_controller.dart';

class AuthMiddleware extends GetMiddleware {
  @override
  RouteSettings? redirect(String? route) {
    final auth = Get.find<AuthController>();
    if (!auth.isLoggedIn.value) {
      return const RouteSettings(name: '/login');
    }
    return null; // 正常访问
  }

  @override
  int? priority = 0; // 优先级
}

✅ 3. 页面:Home、Login、Profile

// pages/home_page.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/auth_controller.dart';

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final auth = Get.find<AuthController>();
    return Scaffold(
      appBar: AppBar(title: const Text("首页")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text("欢迎来到首页"),
            ElevatedButton(
              onPressed: () => Get.toNamed("/profile"),
              child: const Text("进入个人中心"),
            ),
            ElevatedButton(
              onPressed: auth.logout,
              child: const Text("退出登录"),
            )
          ],
        ),
      ),
    );
  }
}

// pages/login_page.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/auth_controller.dart';

class LoginPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final auth = Get.find<AuthController>();
    return Scaffold(
      appBar: AppBar(title: const Text("登录页")),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            auth.login();
            Get.offAllNamed('/home');
          },
          child: const Text("点击登录"),
        ),
      ),
    );
  }
}

// pages/profile_page.dart
import 'package:flutter/material.dart';

class ProfilePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(child: Text("这是个人中心")),
    );
  }
}

✅ 4. 路由定义(routes.dart)

// routes.dart
import 'package:get/get.dart';
import 'middlewares/auth_middleware.dart';
import 'pages/home_page.dart';
import 'pages/login_page.dart';
import 'pages/profile_page.dart';

final List<GetPage> appRoutes = [
  GetPage(name: '/login', page: () => LoginPage()),

  // 添加守卫的页面
  GetPage(
    name: '/home',
    page: () => HomePage(),
    middlewares: [AuthMiddleware()],
  ),
  GetPage(
    name: '/profile',
    page: () => ProfilePage(),
    middlewares: [AuthMiddleware()],
  ),
];

✅ 5. 启动文件(main.dart)

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'controllers/auth_controller.dart';
import 'routes.dart';

void main() {
  Get.put(AuthController()); // 注册控制器
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      initialRoute: '/home',
      getPages: appRoutes,
    );
  }
}

重点

  • Get.find(); 在 main() 中全局注册
  • 路由配置中需要添加 中间件

Getx 路由套转配置方式

方法功能示例
Get.to()普通跳转Get.to(DetailPage())
Get.toNamed()跳转到命名路由Get.toNamed('/home')
Get.back()返回上一页Get.back()
Get.off()替换当前页面Get.off(DetailPage())
Get.offNamed()替换并跳转命名路由Get.offNamed('/home')
Get.offAll()清除所有页面,跳转Get.offAll(MainPage())
Get.offAllNamed()清除所有页面并跳转命名路由Get.offAllNamed('/login')
Get.toNamed('/page', arguments: data)传递数据接收:Get.arguments
Get.toNamed('/page/123')传递路径参数Get.parameters['id']

✅ 二、路由配置:GetMaterialApp + getPages

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'GetX Demo',
      initialRoute: '/',
      getPages: [
        GetPage(name: '/', page: () => HomePage()),
        GetPage(name: '/login', page: () => LoginPage()),
        GetPage(name: '/detail/:id', page: () => DetailPage()),
      ],
    );
  }
}

✅ 三、参数传递方式

传递 arguments(类似 Vue 的 query)
// 跳转时传
Get.toNamed('/detail', arguments: {'title': 'GetX 超棒!'});

// 接收
final args = Get.arguments;

2️⃣ 传递 path 参数(像 /user/123)
// 定义路由
GetPage(name: '/user/:id', page: () => UserPage());

// 跳转
Get.toNamed('/user/123');

// 接收参数
final id = Get.parameters['id']; // 获取的是 '123'

✅ 四、嵌套路由(子路由)

GetPage(
  name: '/parent',
  page: () => ParentPage(),
  children: [
    GetPage(name: '/child', page: () => ChildPage()),
  ],
);

跳转方式:

Get.toNamed('/parent/child');

✅ 五、使用中间件(路由守卫)

GetPage(
  name: '/home',
  page: () => HomePage(),
  middlewares: [AuthMiddleware()],
);

  • 中间件例子:
class AuthMiddleware extends GetMiddleware {
  @override
  RouteSettings? redirect(String? route) {
    final auth = Get.find<AuthController>();
    if (!auth.isLoggedIn.value) {
      return const RouteSettings(name: '/login');
    }
    return null;
  }
}

✅ 六、转场动画

GetPage(
  name: '/about',
  page: () => AboutPage(),
  transition: Transition.rightToLeft,
  transitionDuration: const Duration(milliseconds: 300),
);

七、导航栏方式(bottomNavigation + 路由)

  • 你也可以结合 GetX + IndexedStack 做底部导航 + 路由组合(需要时我可以提供完整例子)。

✅ 跳转方法速查表:

方法作用
Get.to(Widget)Push 一个页面
Get.off(Widget)替换当前页面
Get.offAll(Widget)清除所有页面
Get.toNamed('/path')跳转命名路由
Get.offNamed('/path')替换当前为命名路由
Get.offAllNamed('/path')全部清除并跳转命名
Get.back()返回上一级
Get.arguments获取传递的对象
Get.parameters获取 path 中的参数(如 /user/:id)

Getx 状态管理

  1. 响应式变量基础(Rx 类型)
  2. GetX 控制器结构与绑定
  3. UI 自动刷新更新机制
  4. GetStorage 实现永久存储
  5. 增删改查完整示例
  6. 完整状态管理架构建议

响应式变量基础(Rx 类型)

类型示例使用说明
RxIntRxInt count = 0.obs;obs 创建响应式变量
RxStringvar name = ''.obs;自动追踪变化
RxListRxList items = [].obs;响应式数组
RxMapRxMap map = {}.obs;响应式字典
Rx<T>Rx<User> user = User().obs;自定义类型
改变值方式:
count.value++;
name.value = '新名称';
items.add('新项');
map['key'] = 'value';

2️⃣ GetX 控制器结构与绑定

class CounterController extends GetxController {
  var count = 0.obs;

  void increment() {
    count.value++;
  }
}

  • 注册控制器:
// main.dart
Get.put(CounterController());

// 页面中局部绑定
final counter = Get.put(CounterController());

3️⃣ UI 自动刷新更新机制

  1. 方法 1:Obx(最推荐)
Obx(() => Text('计数:${counter.count}'));

  1. 方法 2:GetX Widget
GetX<CounterController>(
  builder: (controller) => Text('${controller.count}'),
);

  1. 方法 3:GetBuilder(非响应式,只手动更新)
GetBuilder<CounterController>(
  builder: (_) => Text('${_.count}'),
);

GetBuilder 适用于不频繁更新的组件,需要手动调用 update()。

4️⃣ GetStorage 实现永久存储(本地缓存)

步骤 1:引入依赖
dependencies:
  get_storage: ^2.1.1

步骤 2:初始化
void main() async {
  await GetStorage.init(); // 必须 await 初始化
  runApp(MyApp());
}

步骤 3:使用
final box = GetStorage();

// 存
box.write('isLoggedIn', true);

// 取
bool isLoggedIn = box.read('isLoggedIn') ?? false;

// 删
box.remove('isLoggedIn');

5️⃣ 增删改查完整示例(以 RxList 为例)

class TodoController extends GetxController {
  var todos = <String>[].obs;

  void add(String task) => todos.add(task);

  void updateAt(int index, String newValue) => todos[index] = newValue;

  void delete(int index) => todos.removeAt(index);

  void clear() => todos.clear();
}

  • 使用:
final todoController = Get.find<TodoController>();

Obx(() => ListView.builder(
  itemCount: todoController.todos.length,
  itemBuilder: (_, i) => ListTile(
    title: Text(todoController.todos[i]),
    trailing: IconButton(
      icon: Icon(Icons.delete),
      onPressed: () => todoController.delete(i),
    ),
  ),
));

6️⃣ 完整状态管理架构建议

功能推荐方式
页面数据共享使用 Get.put() 全局注册控制器
子页面控制器Get.lazyPut() 按需注册
多状态切换用多个 RxBool / RxEnum 控制
页面重构控制器 + Obx 封装成组件
状态持久化GetStorage 存取数据
中间件判断状态Get.find<Controller>().xxx.value

✅ 示例:登录状态持久化控制器(完整)

class AuthController extends GetxController {
  final storage = GetStorage();
  var isLoggedIn = false.obs;

  @override
  void onInit() {
    super.onInit();
    isLoggedIn.value = storage.read('isLoggedIn') ?? false;
  }

  void login() {
    isLoggedIn.value = true;
    storage.write('isLoggedIn', true);
  }

  void logout() {
    isLoggedIn.value = false;
    storage.remove('isLoggedIn');
  }
}

状态管理,永久存储 get_storage

  1. 在pubspec.yaml文件中添加库的依赖:
dependencies:
  get_storage: ^2.1.1
  1. 然后运行:
flutter pub get

✅ 2. 初始化(必须)

  • 在 main.dart 的入口函数中初始化:
import 'package:get_storage/get_storage.dart';

void main() async {
  await GetStorage.init(); // 初始化存储
  runApp(MyApp());
}

注意:await GetStorage.init() 是异步方法,必须在 runApp() 前执行。

✅ 3. 基本用法

final box = GetStorage(); // 实例化,默认使用 'GetStorage' 区域

// 写入数据
box.write('username', '张三');

// 读取数据
String name = box.read('username') ?? '游客';

// 删除某个值
box.remove('username');

// 清空所有数据
box.erase();

// 判断是否存在
bool exists = box.hasData('username');

✅ 4. 支持的类型

支持所有基本类型和 Map、List:

类型示例
String'张三'
int100
double3.14
booltrue
List<String>['a', 'b']
Map<String, dynamic>{'id': 1, 'name': 'Tom'}

✅ 5. 多区域存储(类似命名空间)

  • 你可以为不同模块使用不同的存储文件(如用户模块/缓存模块)
await GetStorage.init('user');
final userBox = GetStorage('user');

userBox.write('token', '123456');
print(userBox.read('token'));

✅ 6. 实时监听值变化(响应式)

box.listen(() {
  print('本地数据发生变化');
});

  • 你也可以监听某个 key:
box.listenKey('isDark', (value) {
  print('主题设置变为:$value');
});

✅ 7. 搭配 GetX Controller 使用(推荐)

  • 例如保存登录状态:
class AuthController extends GetxController {
  final storage = GetStorage();
  var isLoggedIn = false.obs;

  @override
  void onInit() {
    super.onInit();
    isLoggedIn.value = storage.read('isLoggedIn') ?? false;
  }

  void login() {
    isLoggedIn.value = true;
    storage.write('isLoggedIn', true);
  }

  void logout() {
    isLoggedIn.value = false;
    storage.remove('isLoggedIn');
  }
}

✅ 8. 存储对象(推荐使用 JSON)

final user = {
  'id': 1,
  'name': 'Alice',
  'roles': ['admin', 'editor'],
};

box.write('user', user);

// 读取
Map userData = box.read('user');

如需存储自定义对象,请使用 toJson / fromJson 显式转换。

✅ 9. 总结速查表

操作方法示例
初始化await GetStorage.init()在 main() 中
实例化GetStorage()可传命名空间
写入.write('key', value)
读取.read('key')
删除.remove('key')
清空.erase()
判断.hasData('key')
监听所有.listen((){})
监听某项.listenKey('key', callback)

使用场景推荐

场景建议用法
登录状态bool 持久化 + 控制器绑定
用户信息Map 存储 JSON
主题模式bool 监听并更新 UI
App 首次启动设置 isFirstRun 标志位
临时缓存数据write + remove 清理机制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2384143.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

贪心算法之跳跃游戏问题

问题背景 本文背景是leetcode的一道经典题目&#xff1a;跳跃游戏&#xff0c;描述如下&#xff1a; 给定一个非负整数数组 nums&#xff0c;初始位于数组的第一个位置&#xff08;下标0&#xff09;。数组中的每个元素表示在该位置可以跳跃的最大长度。判断是否能够到达最后…

Unity 如何使用Timeline预览、播放特效

在使用unity制作和拟合动画时&#xff0c;我们常用到Timeline&#xff0c;前后拖动滑轨&#xff0c;预览动画正放倒放非常方便。如果我们想对特效也进行这个操作&#xff0c;可以使用下文的步骤。 至此&#xff0c;恭喜你又解锁了一个新的技巧。如果我的分享对你有帮助&#xf…

MySQL篇-其他面试题

MySQL事务 问题&#xff1a;事务是什么&#xff1f;ACID问题 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 1、事务…

iOS 蓝牙开发中的 BT 与 BLE

在 iOS 开发者的语境里&#xff0c;大家把 BT 和 BLE 当成两种不同的蓝牙技术在谈——它们来自同一个 Bluetooth 规范&#xff0c;但面向的场景、协议栈乃至 Apple 提供的 API 都截然不同。 缩写全称 / 技术名称规范层叫法iOS 支持现状典型用途BTBluetooth Classic&#xff08…

鸿蒙Flutter实战:21-混合开发详解-1-概述

引言 在前面的系列文章中&#xff0c;我们从搭建开发环境开始&#xff0c;讲到如何使用、集成第三方插件&#xff0c;如何将现有项目进行鸿蒙化改造&#xff0c;以及上架审核等内容&#xff1b;还以高德地图的 HarmonyOS SDK 的使用为例&#xff0c; 讲解了如何将高德地图集成…

[架构之美]从PDMan一键生成数据库设计文档:Word导出全流程详解(二十)

[架构之美]从PDMan一键生成数据库设计文档&#xff1a;Word导出全流程详解&#xff08;二十&#xff09; 一、痛点 你是否经历过这些场景&#xff1f; 数据库字段频繁变更&#xff0c;维护文档耗时费力用Excel维护表结构&#xff0c;版本混乱难以追溯手动编写Word文档&#…

大量程粗糙度轮廓仪适用于哪些材质和表面?

大量程粗糙度轮廓仪是一种能够在广泛的测量范围内对工件表面进行粗糙度分析的精密仪器。它通常采用接触式或非接触式传感器&#xff0c;通过对工件表面的扫描&#xff0c;捕捉表面微观的起伏和波动&#xff0c;从而获取粗糙度数据。该仪器不仅能测量微小的表面细节&#xff0c;…

Unity-编辑器扩展

之前我们关于Unity的讨论都是针对于Unity底层的内容或者是代码层面的东西&#xff0c;这一次我们来专门研究Unity可视化的编辑器&#xff0c;在已有的基础上做一些扩展。 基本功能 首先我们来认识三个文件夹&#xff1a; Editor&#xff0c;Gizmos&#xff0c;Editor Defaul…

Lucide:一款精美的开源矢量图标库,前端图标新选择

名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。——苏轼《稼说送张琥》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、前言&#xff1a;为何选择 Lucide&#xff1f;二、Lucide 是什么&#xff1f;1.…

Mac如何允许安装任何来源软件?

打开系统偏好设置-安全性与隐私&#xff0c;点击右下角的解锁按钮&#xff0c;选择允许从任何来源。 如果没有这一选项&#xff0c;请到打开终端&#xff0c;输入命令行&#xff1a;sudo spctl --master-disable, 输入命令后回车&#xff0c;输入电脑的开机密码后回车。 返回“…

2025最新版Visual Studio Code for Mac安装使用指南

2025最新版Visual Studio Code for Mac安装使用指南 Installation and Application Guide to The Latest Version of Visual Studio Code in 2025 By JacksonML 1. 什么是Visual Studio Code&#xff1f; Visual Studio Code&#xff0c;通常被称为 VS Code&#xff0c;是由…

【嵙大o】C++作业合集

​ 参考&#xff1a; C swap&#xff08;交换&#xff09;函数 指针/引用/C自带-CSDN博客 Problem IDTitleCPP指针CPP引用1107 Problem A编写函数&#xff1a;Swap (I) (Append Code)1158 Problem B整型数据的输出格式1163 Problem C时间&#xff1a;24小时制转12小时制1205…

C++23 范围迭代器作为非范围算法的输入 (P2408R5)

文章目录 一、引言二、C23及范围迭代器的背景知识2.1 C23概述2.2 范围迭代器的概念 三、P2408R5提案的内容3.1 提案背景3.2 提案内容 四、范围迭代器作为非范围算法输入的优势4.1 代码简洁性4.2 提高开发效率4.3 更好的兼容性 五、具体的代码示例5.1 使用范围迭代器进行并行计算…

2025.05.20【Treemap】树图数据可视化技巧

Multi-level treemap How to build a treemap with group and subgroups. Customization Customize treemap labels, borders, color palette and more 文章目录 Multi-level treemapCustomization Treemap 数据可视化技巧什么是 TreemapTreemap 的应用场景如何在 R 中绘制 T…

深入了解Springboot框架的启动流程

目录 1、介绍 2、执行流程 1、运行run方法 2、初始化SpringApplication对象 1、确定容器类型 3、加载所有的初始化器 4、加载Spring上下文监听器 5、设置程序运行的主类 3、进入run方法 1、开启计时器 2、Headless模式配置 3、获取并启用监听器 4、准备环境 1、设…

LLaMA-Factory微调LLM-Research/Llama-3.2-3B-Instruct模型

1、GPU环境 nvidia-smi 2、pyhton环境安装 git clone https://github.com/hiyouga/LLaMA-Factory.git conda create -n llama_factory python3.10 conda activate llama_factory cd LLaMA-Factory pip install -e .[torch,metrics] 3、微调模型下载&#xff08;LLM-Research/…

3.8.1 利用RDD实现词频统计

在本次实战中&#xff0c;我们通过Spark的RDD实现了词频统计功能。首先&#xff0c;准备了包含单词的文件并上传至HDFS。接着&#xff0c;采用交互式方式逐步完成词频统计&#xff0c;包括创建RDD、单词拆分、映射为二元组、按键归约以及排序等操作。此外&#xff0c;还通过创建…

Spring Ioc和Aop,Aop的原理和实现案例,JoinPoint,@Aspect,@Before,@AfterReturning

DAY25.2 Java核心基础 Spring两大核心&#xff1a;Ioc和Aop IOC Ioc容器&#xff1a;装载bean的容器&#xff0c;自动创建bean 三种方式&#xff1a; 1、基于xml配置&#xff1a;通过在xml里面配置bean&#xff0c;然后通过反射机制创建bean&#xff0c;存入进Ioc容器中 …

[解决conda创建新的虚拟环境没用python的问题]

问题复现 使用conda create -n env的时候&#xff0c;在对应的虚拟环境的文件里面找不到对应的python文件 为什么 首先&#xff0c;我们来看一下创建环境时的触发链路&#xff1a; 这表明当前环境中找不到Python可执行文件。 解决方法 所以很明显&#xff0c;我们需要指定…

【C++】控制台小游戏

移动&#xff1a;W向上&#xff0c;S上下&#xff0c;A向左&#xff0c;D向右 程序代码&#xff1a; #include <iostream> #include <conio.h> #include <windows.h> using namespace std;bool gameOver; const int width 20; const int height 17; int …