当前位置: 首页 > news >正文

flutter redux状态管理

📚 Flutter 状态管理系列文章目录
  1. Flutter 状态管理(setState、InheritedWidget、 Provider 、Riverpod、 BLoC / Cubit、 GetX 、MobX 、Redux)

  2. setState() 使用详解:原理及注意事项

  3. InheritedWidget 组件使用及原理

  4. Flutter 中 Provider 的使用、注意事项与原理解析(含代码实战)

  5. GetX 用法详细解析以及注意事项

  6. Flutter BLoC 使用详细解析

  7. Flutter MobX 响应式原理与实战详解

  8. Flutter Riverpod 使用详细解析

  9. Riverpod原理解析(实现一个自己的Riverpod

  10. flutter redux状态管理

先看一个redux的计数器的代码例子


✅ 1. pubspec.yaml 添加依赖

dependencies:flutter:sdk: flutterflutter_redux: ^0.10.0redux: ^5.0.0

✅ 2. 完整 main.dart 文件

import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';void main() {// 初始化 Storefinal store = Store<AppState>(counterReducer,initialState: AppState(counter: 0),);runApp(MyApp(store: store));
}// 应用状态
class AppState {final int counter;AppState({required this.counter});
}// 动作
enum CounterAction { increment }// Reducer
AppState counterReducer(AppState state, dynamic action) {if (action == CounterAction.increment) {return AppState(counter: state.counter + 1);}return state;
}// 根组件
class MyApp extends StatelessWidget {final Store<AppState> store;const MyApp({super.key, required this.store});Widget build(BuildContext context) {return StoreProvider<AppState>(store: store,child: MaterialApp(title: 'Redux Counter',home: CounterPage(),),);}
}// 页面组件
class CounterPage extends StatelessWidget {Widget build(BuildContext context) {return StoreConnector<AppState, int>(converter: (store) => store.state.counter,builder: (context, counter) => Scaffold(appBar: AppBar(title: Text('Redux Counter')),body: Center(child: Text('Count: $counter',style: TextStyle(fontSize: 24),),),floatingActionButton: StoreConnector<AppState, VoidCallback>(converter: (store) {return () => store.dispatch(CounterAction.increment);},builder: (context, callback) => FloatingActionButton(onPressed: callback,child: Icon(Icons.add),),),),);}
}

🧠 架构解释

部分内容
AppState存储整个 App 的状态(这里只有一个 counter
enum CounterAction定义 Action,表示操作类型
counterReducerReducer 是一个纯函数,接收旧状态和 Action,返回新状态
StoreProvider全局注入 Store
StoreConnector获取状态或触发 dispatch

下面结合这个例子说明 当我点击加号按钮会发生什么,界面是怎么更新的

这里我们就结合 Redux 流程详细分析点击「加号按钮」时,界面是怎么更新的


🧩 点击按钮到底发生了什么?

🚀 先来看关键代码(FloatingActionButton):
floatingActionButton: StoreConnector<AppState, VoidCallback>(converter: (store) {return () => store.dispatch(CounterAction.increment);},builder: (context, callback) => FloatingActionButton(onPressed: callback,child: Icon(Icons.add),),
),

🧠 Redux 流程图:点击按钮 -> 状态更新 -> UI 刷新

 用户点击按钮(onPressed)│▼
callback 被触发:store.dispatch(CounterAction.increment)│▼
Reducer 被调用:counterReducer 接收 oldState 和 Action│▼
返回新状态 newState:AppState(counter: +1)│▼
Store 内部调用 notifyListeners() 通知所有订阅者│▼
StoreConnector 监听状态变化,触发 builder 重建│▼
UI 组件重新构建,显示最新 counter 数值

🔁 每个阶段详细拆解:

1️⃣ onPressed → dispatch

用户点击按钮时:

onPressed: callback

这个 callback 就是:

() => store.dispatch(CounterAction.increment)

它会向 Redux store 发出一个 Action。


2️⃣ Reducer 处理 Action,生成新状态
AppState counterReducer(AppState state, dynamic action) {if (action == CounterAction.increment) {return AppState(counter: state.counter + 1);}return state;
}
  • 如果 Action 是 CounterAction.increment
  • 就返回一个新的 AppState(counter + 1)

注意:状态是不可变的,所以必须返回一个新的 AppState 实例,而不是修改旧的。


3️⃣ Store 自动触发 UI 重建

store 的状态改变时:

final store = Store<AppState>(counterReducer,initialState: AppState(counter: 0),
);
  • Store 会通知所有使用 StoreConnector 的 widget
  • 它们会重新调用 builder 构建函数

4️⃣ StoreConnector 构建最新界面

这段代码中的 StoreConnector 会被重新构建:

StoreConnector<AppState, int>(converter: (store) => store.state.counter,builder: (context, counter) => Text('Count: $counter'),
)

新的 AppState 会被传入,counter 发生变化,于是界面上的文本 Count: x 就会更新。


✅ 总结一句话:

点击按钮 → 发出 Action → Reducer 生成新状态 → Store 更新 → UI 自动刷新(响应式)


进阶用法

Redux 在 Flutter 中除了基础用法(StateActionReducerStore)之外,还有许多 进阶用法,可以让项目结构更清晰、功能更强大。

下面我结合代码示例说明几种常见的 Redux 进阶用法:



✅1. Middleware 能做什么?

Redux 中的 Middleware(中间件) 是连接 dispatch()reducer 的中间层,允许你在 Action 发出与 State 更新之间 做一些额外处理。

用途举例
日志记录打印所有 Action 和 State
处理异步配合 redux_thunk 发起网络请求
权限校验登录验证、跳转拦截
状态持久化把 state 保存到本地(如 SharedPreferences
拦截非法操作比如某 Action 不满足条件就不继续传递

✅ Middleware 使用方法
1️⃣ 函数签名
typedef MiddlewareClass<State> = void Function(Store<State> store,dynamic action,NextDispatcher next,
);
  • store:当前 Redux 的状态仓库
  • action:被 dispatch 的 action
  • next(action):把 action 继续传递给下一个中间件或 reducer(必须调用!)

✅ 简单日志中间件示例
void loggingMiddleware<State>(Store<State> store,dynamic action,NextDispatcher next,
) {print('[🌀 Dispatching] $action');next(action); // 一定要调用,否则 reducer 永远收不到print('[✅ New State] ${store.state}');
}

注册方式:

final store = Store<AppState>(appReducer,initialState: AppState.initial(),middleware: [loggingMiddleware],
);

一个完整的Redux 的Middleware 是例子

✅ 项目概览:功能说明

我们构建一个登录后才能点击“点赞”的 App,包含以下功能:

功能点实现方式
✅ 通用日志中间件每个 action 自动打印日志(带模块 tag)
✅ Auth 中间件未登录时禁止 dispatch 点赞,并提示“请先登录”
✅ Loading 中间件点赞时展示 loading 状态,完成后消失
✅ 通知机制成功点赞后弹出 SnackBar 通知“点赞成功”

✅ 目录结构(精简示意)
lib/
├── main.dart                # 启动入口
├── redux/
│   ├── app_state.dart       # 全局状态 AppState
│   ├── reducers.dart        # combineReducers
│   ├── actions.dart         # 所有 action
│   ├── middleware.dart      # 所有中间件
│   └── models/              # 模块化状态模型(auth, like)
├── pages/
│   └── home_page.dart       # 主界面

✅ 1. app_state.dart
class AppState {final bool loggedIn;final bool isLiking;final int likeCount;AppState({required this.loggedIn,required this.isLiking,required this.likeCount,});AppState copyWith({bool? loggedIn, bool? isLiking, int? likeCount}) {return AppState(loggedIn: loggedIn ?? this.loggedIn,isLiking: isLiking ?? this.isLiking,likeCount: likeCount ?? this.likeCount,);}static AppState initial() => AppState(loggedIn: false,isLiking: false,likeCount: 0,);
}

✅ 2. actions.dart
// 通用 Action
class LoginAction {}
class LogoutAction {}// 点赞相关
class LikeRequestAction {}
class LikeSuccessAction {}
class LikeFailedAction {}// UI 通知
class ShowToastAction {final String message;ShowToastAction(this.message);
}

✅ 3. reducers.dart
import 'app_state.dart';
import 'actions.dart';AppState appReducer(AppState state, dynamic action) {if (action is LoginAction) {return state.copyWith(loggedIn: true);} else if (action is LogoutAction) {return state.copyWith(loggedIn: false);} else if (action is LikeRequestAction) {return state.copyWith(isLiking: true);} else if (action is LikeSuccessAction) {return state.copyWith(isLiking: false,likeCount: state.likeCount + 1,);} else if (action is LikeFailedAction) {return state.copyWith(isLiking: false);}return state;
}

✅ 4. middleware.dart(重点)
import 'package:flutter/material.dart';
import 'package:redux/redux.dart';
import 'app_state.dart';
import 'actions.dart';/// 通用日志中间件(带 tag)
Middleware<AppState> createLogger(String tag) {return (store, action, next) {print('[$tag] 👉 Dispatch: $action');next(action);print('[$tag] ✅ New State: ${store.state}');};
}/// Auth 中间件:未登录不能点赞
void authMiddleware(Store<AppState> store, dynamic action, NextDispatcher next) {if (action is LikeRequestAction && !store.state.loggedIn) {store.dispatch(ShowToastAction("请先登录"));return; // 拦截}next(action);
}/// Loading 中间件:异步处理点赞操作
void likeAsyncMiddleware(Store<AppState> store, dynamic action, NextDispatcher next) async {if (action is LikeRequestAction) {next(action); // 改为 loading 状态await Future.delayed(Duration(seconds: 2)); // 模拟请求store.dispatch(LikeSuccessAction());store.dispatch(ShowToastAction("点赞成功"));} else {next(action);}
}/// 通知中间件:展示 SnackBar
void toastMiddleware(Store<AppState> store, dynamic action, NextDispatcher next) {next(action);if (action is ShowToastAction) {final context = navigatorKey.currentContext;if (context != null) {ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(action.message)),);}}
}

✅ 5. main.dart
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
import 'redux/app_state.dart';
import 'redux/reducers.dart';
import 'redux/middleware.dart';
import 'pages/home_page.dart';final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();void main() {final store = Store<AppState>(appReducer,initialState: AppState.initial(),middleware: [createLogger("App"),authMiddleware,likeAsyncMiddleware,toastMiddleware,],);runApp(MyApp(store));
}class MyApp extends StatelessWidget {final Store<AppState> store;MyApp(this.store);Widget build(BuildContext context) {return StoreProvider<AppState>(store: store,child: MaterialApp(navigatorKey: navigatorKey,title: 'Redux Middleware Demo',home: HomePage(),),);}
}

✅ 6. home_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import '../redux/app_state.dart';
import '../redux/actions.dart';class HomePage extends StatelessWidget {Widget build(BuildContext context) {return StoreConnector<AppState, _ViewModel>(converter: (store) => _ViewModel(loggedIn: store.state.loggedIn,isLiking: store.state.isLiking,likeCount: store.state.likeCount,onLogin: () => store.dispatch(LoginAction()),onLike: () => store.dispatch(LikeRequestAction()),),builder: (context, vm) => Scaffold(appBar: AppBar(title: Text("中间件演示")),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Text(vm.loggedIn ? "已登录" : "未登录"),SizedBox(height: 10),Text("点赞数:${vm.likeCount}"),SizedBox(height: 20),vm.isLiking? CircularProgressIndicator(): ElevatedButton(onPressed: vm.onLike,child: Text("点赞"),),SizedBox(height: 20),if (!vm.loggedIn)ElevatedButton(onPressed: vm.onLogin,child: Text("登录"),),],),),),);}
}class _ViewModel {final bool loggedIn;final bool isLiking;final int likeCount;final VoidCallback onLogin;final VoidCallback onLike;_ViewModel({required this.loggedIn,required this.isLiking,required this.likeCount,required this.onLogin,required this.onLike,});
}

✅ 总结:包含的中间件功能
名称功能
Logger 中间件打印所有 Action 和新状态
Auth 中间件未登录拦截 Like 操作并提示
Loading 中间件2 秒后模拟点赞成功并更新点赞数
Toast 中间件所有 ShowToastAction 弹出 SnackBar

✅ 状态持久化中间件(模拟)
void persistMiddleware(Store<AppState> store, dynamic action, NextDispatcher next) {next(action);// 模拟保存状态(实际可写入 SharedPreferences)print('[💾 Save] count=${store.state.counter.count}');
}

⚠️ 使用中间件的注意事项
注意事项说明
✅ 必须调用 next(action)否则 reducer 不会收到这个 action,导致状态不更新
✅ 中间件顺序重要比如日志写在异步前后位置不同,输出不同
✅ 避免副作用污染 reducer所有副作用都应在 middleware 完成,reducer 必须保持纯函数
✅ 多中间件组合多个 middleware 会按数组顺序依次执行
✅ 检查 Action 类型通常你需要判断 if (action is XxxAction) 再处理

🧠 技巧与扩展实践
技巧说明
✅ 创建通用日志中间件可接收一个 tag,打印指定模块日志
✅ 编写 Auth 中间件拦截登录状态,重定向或提示
✅ 写 Loading 中间件自动在异步请求发出/结束时 dispatch ShowLoading/HideLoading
✅ 与通知机制结合拦截 action 自动发送 Toast、Dialog、导航等 UI 行为

✅ 2. redux_thunk 的作用

redux_thunk 是 Redux 的一个中间件,用于支持异步操作,比如网络请求、定时任务等。

在默认 Redux 中,只能 dispatch 一个普通对象

store.dispatch(MyAction()); // ✅ 正常
store.dispatch(() async {}); // ❌ 错误,不支持函数

👉 有了 redux_thunk 后,你就可以 dispatch 一个函数(Thunk),这个函数里可以执行异步任务,最后再手动 dispatch 正常的 Action。


✅ 安装依赖

pubspec.yaml 中添加:

dependencies:redux_thunk: ^0.4.0

✅ 使用步骤
第一步:注册中间件
import 'package:redux/redux.dart';
import 'package:redux_thunk/redux_thunk.dart';final store = Store<AppState>(appReducer,initialState: AppState.initial(),middleware: [thunkMiddleware],
);

第二步:定义异步 action(Thunk)
import 'package:redux_thunk/redux_thunk.dart';class UpdateCounterAction {final int newValue;UpdateCounterAction(this.newValue);
}ThunkAction<AppState> fetchCounterFromServer() {return (Store<AppState> store) async {// 模拟网络请求await Future.delayed(Duration(seconds: 2));int response = 42;// 请求完成后,dispatch 普通 Actionstore.dispatch(UpdateCounterAction(response));};
}

第三步:在界面中使用
ElevatedButton(onPressed: () {store.dispatch(fetchCounterFromServer());},child: Text("异步获取计数"),
);

第四步:在 reducer 中处理 UpdateCounterAction
AppState reducer(AppState state, dynamic action) {if (action is UpdateCounterAction) {return state.copyWith(counter: action.newValue);}return state;
}

✅ Thunk vs 普通 Action 对比表
项目普通 ActionThunk Action
类型普通对象函数 (Store) => void
是否异步✅ 可以处理异步逻辑
何时 dispatch直接 dispatch异步完成后手动 dispatch
使用场景UI 改变、同步状态更新网络请求、延迟任务、条件判断

✅ 使用技巧
技巧示例
✅ 配合 Loading Actiondispatch(ShowLoading),请求完成后 dispatch(HideLoading)
✅ 条件判断执行if (!state.loggedIn) return;
✅ 错误处理try/catch 包裹网络请求,dispatch 错误 Action
✅ 链式 dispatch在 thunk 里 dispatch 多个普通 action 来控制流程

✅ 3. 模块化 reducer(combineReducers)

模块化 reducer(combineReducers)是 Redux 在管理大型复杂状态树时非常重要的机制,尤其适用于 Flutter Redux 项目的模块化设计与状态解耦


✅ 一、什么是 combineReducers?

combineReducers 是 Redux 提供的一个方法,用于组合多个子 reducer,把每个 reducer 管理的状态片段组合成一个整体 AppState


✅ 二、基本用法
final Reducer<AppState> appReducer = combineReducers<AppState>([TypedReducer<AppState, dynamic>((state, action) => AppState(auth: authReducer(state.auth, action),counter: counterReducer(state.counter, action),cart: cartReducer(state.cart, action),)),
]);

推荐做法:手动组合多个 reducer,并封装到 AppState 的构造函数中。


✅ 三、典型项目模块结构
lib/
└── redux/├── app_state.dart          // 全局状态树├── reducers.dart           // 所有 reducer 组合入口├── auth/│   ├── auth_state.dart│   ├── auth_reducer.dart├── counter/│   ├── counter_state.dart│   ├── counter_reducer.dart└── cart/├── cart_state.dart├── cart_reducer.dart

✅ 四、完整例子:三模块组合
app_state.dart
import 'auth/auth_state.dart';
import 'counter/counter_state.dart';
import 'cart/cart_state.dart';class AppState {final AuthState auth;final CounterState counter;final CartState cart;AppState({required this.auth,required this.counter,required this.cart,});static AppState initial() => AppState(auth: AuthState.initial(),counter: CounterState.initial(),cart: CartState.initial(),);
}

reducers.dart
import 'auth/auth_reducer.dart';
import 'counter/counter_reducer.dart';
import 'cart/cart_reducer.dart';
import 'app_state.dart';AppState appReducer(AppState state, dynamic action) {return AppState(auth: authReducer(state.auth, action),counter: counterReducer(state.counter, action),cart: cartReducer(state.cart, action),);
}

✅ 每个子 reducer 只处理自己的 state,组合后构建成新的 AppState。


✅ 五、技巧 & 注意事项
✅ 技巧💡 描述
每个模块只处理自己子 reducer 只操作对应的子状态,如 authReducer 只管 auth
不要在 reducer 中跨模块访问 state比如 cartReducer 不应该操作 auth 的状态
状态必须有初始值每个模块的 State 应有 initial() 方法,避免 null 异常
结合 ViewModel 解耦 UI搭配 StoreConnector + ViewModel 映射使用
结构扁平化更清晰尽量避免深度嵌套,如 appState.user.profile.address.detail...
支持嵌套组合多层模块可以使用多级 combineReducers(示例见下)
合并时返回新实例reducer 不能修改旧状态,要返回 copyWith 后的新状态

✅ 六、嵌套 combineReducers(高级)

如果某模块本身有多个子模块,也可以进一步拆分 reducer:

class UserState {final ProfileState profile;final SettingsState settings;UserState({required this.profile, required this.settings});
}UserState userReducer(UserState state, dynamic action) {return UserState(profile: profileReducer(state.profile, action),settings: settingsReducer(state.settings, action),);
}AppState appReducer(AppState state, dynamic action) {return AppState(user: userReducer(state.user, action),cart: cartReducer(state.cart, action),);
}

✅ 七、是否使用 Redux 官方 combineReducers()

在 Flutter 中,我们不推荐使用 redux.dart 提供的 combineReducers<>() 函数,因为它基于 TypedReducer 是不可读的:

final appReducer = combineReducers<AppState>([TypedReducer<AppState, SomeAction>(someHandler),
]);

👉 推荐自己写 AppState 的构造函数手动组合 reducer,更清晰、更好调试、更好拓展。


✅ 八、总结
优点描述
🎯 清晰的模块边界每个 reducer 只处理自己的状态
🎯 易于扩展新模块只需新建 reducer 并在 appReducer 中组合
🎯 易于测试每个 reducer 都可以独立单元测试
🎯 易于维护状态树结构一目了然

✅ 4. ViewModel 映射(StoreConnector + ViewModel)

ViewModel 映射(通过 StoreConnector<AppState, ViewModel>)是 Flutter Redux 中的核心设计模式之一,它的主要目的是


✅ 一、使用目的
目的说明
解耦 UI 与 Store避免在 UI 中频繁直接访问 store.state.xxx,让 UI 更干净
优化性能ViewModel 实现 ==props 可以精细控制 build 触发时机
集中事件处理所有 UI 操作都抽象成 viewModel.onXxx(),便于测试和维护
提升可测试性ViewModel 可以被独立测试,UI 层只管展示

✅ 二、基本结构
StoreConnector<AppState, MyViewModel>(converter: (store) => MyViewModel.fromStore(store),builder: (context, vm) => UI(vm),
)

✅ 三、ViewModel 示例
class MyViewModel {final bool isLoggedIn;final int count;final List<String> cartItems;final VoidCallback onLogin;final VoidCallback onIncrement;final Function(String) onAddToCart;MyViewModel({required this.isLoggedIn,required this.count,required this.cartItems,required this.onLogin,required this.onIncrement,required this.onAddToCart,});factory MyViewModel.fromStore(Store<AppState> store) {return MyViewModel(isLoggedIn: store.state.auth.loggedIn,count: store.state.counter.count,cartItems: store.state.cart.items,onLogin: () => store.dispatch(LoginAction()),onIncrement: () => store.dispatch(IncrementAction()),onAddToCart: (item) => store.dispatch(AddItemAction(item)),);}
}

✅ 四、常见技巧与注意事项
✅ 1. 使用 == 优化性能

实现 ==hashCode 或用 Equatable,避免不必要重建 UI。

class MyViewModel extends Equatable {...List<Object?> get props => [isLoggedIn, count, cartItems];
}

✅ 2. 使用 distinct: true 避免重复 build
StoreConnector<AppState, MyViewModel>(distinct: true, // 只有当 ViewModel 改变时才 rebuildconverter: ...,builder: ...,
)

⚠️ 前提:你的 ViewModel 必须实现正确的 ==


✅ 3. 拆分局部 ViewModel

如果某个小部件只依赖 counter,可以定义小型 ViewModel,减少 rebuild 粒度:

StoreConnector<AppState, int>(converter: (store) => store.state.counter.count,builder: (context, count) => Text('$count'),
)

✅ 4. 将所有业务逻辑从 UI 层抽离到 ViewModel
ElevatedButton(onPressed: vm.onAddToCart, // 不写 store.dispatch(...)child: Text("加购物车"),
)

这让 UI 更像“纯 View”,不含状态操作。


✅ 5. 可以测试 ViewModel 的逻辑
final store = Store<AppState>(...);
final vm = MyViewModel.fromStore(store);test('登录后状态变更', () {vm.onLogin();expect(store.state.auth.loggedIn, true);
});

✅ 五、什么时候不用 ViewModel?
  • 很小的 App 或 UI 页面很简单时(状态少)
  • 无状态展示组件,如 LoadingSpinner

但中大型项目,强烈建议用 ViewModel + StoreConnector 模式。


✅ 六、结论:ViewModel 模式的核心价值
优点描述
✅ 让 UI 无状态化Widget 不感知任何 store,只接受 props
✅ 清晰职责划分状态转换逻辑集中在 ViewModel 中
✅ 更易测试UI 可以 mock ViewModel 测试渲染逻辑
✅ 性能更优可细粒度控制更新、避免冗余 build

✅ 5. 多个子模块 state 的拆分(State 树)

在 Redux 中进行多个子模块 State 的拆分,是构建大型 Flutter Redux 项目的基础。


✅ 一、什么是 State 拆分?

将全局状态 AppState 拆分成多个子模块状态(如 AuthStateCounterStateCartState),每个模块各自维护自己的状态、Action 和 Reducer,最终组合成一个全局状态树。


✅ 二、典型结构示意(项目拆分)
AppState {AuthState auth,CounterState counter,CartState cart,
}

✅ 三、完整示例:拆出 Auth + Counter + Cart 三个模块
1️⃣ AppState.dart
class AppState {final AuthState auth;final CounterState counter;final CartState cart;AppState({required this.auth,required this.counter,required this.cart,});static AppState initial() => AppState(auth: AuthState.initial(),counter: CounterState.initial(),cart: CartState.initial(),);
}

✅ 四、每个子模块包含什么?

每个模块应当包含以下三部分:

文件内容
xxx_state.dart状态数据结构
xxx_actions.dart所有该模块相关 Action 类型
xxx_reducer.dart专属 reducer,只处理自己的 state

✅ 五、示例:Cart 模块(购物车)
cart_state.dart
class CartState {final List<String> items;CartState({required this.items});CartState copyWith({List<String>? items}) {return CartState(items: items ?? this.items);}static CartState initial() => CartState(items: []);
}

cart_actions.dart
class AddItemAction {final String item;AddItemAction(this.item);
}class RemoveItemAction {final String item;RemoveItemAction(this.item);
}

cart_reducer.dart
import 'cart_state.dart';
import 'cart_actions.dart';CartState cartReducer(CartState state, dynamic action) {if (action is AddItemAction) {return state.copyWith(items: List.from(state.items)..add(action.item));} else if (action is RemoveItemAction) {return state.copyWith(items: List.from(state.items)..remove(action.item));}return state;
}

✅ 六、在总 Reducer 中组合
reducers.dart
import 'auth/auth_reducer.dart';
import 'counter/counter_reducer.dart';
import 'cart/cart_reducer.dart';
import 'app_state.dart';AppState appReducer(AppState state, dynamic action) {return AppState(auth: authReducer(state.auth, action),counter: counterReducer(state.counter, action),cart: cartReducer(state.cart, action),);
}

✅ 七、在 UI 层访问子状态
StoreConnector<AppState, _ViewModel>(converter: (store) => _ViewModel(isLoggedIn: store.state.auth.loggedIn,count: store.state.counter.count,cartItems: store.state.cart.items,),builder: (context, vm) => ...
)

🎯 八、State 拆分技巧与注意事项
技巧说明
✅ 保持每个模块状态独立每个 State 仅维护自己数据,互不干扰
✅ 所有状态都必须初始值每个模块必须有 initial() 方法
✅ 不要跨模块访问状态不能在 cartReducer 中访问 auth 的状态
✅ 状态命名清晰auth.loggedIncart.items,不要混乱结构
✅ 模块目录结构标准化每个模块文件夹三件套:state + actions + reducer
✅ 配合 ViewModel 封装访问逻辑UI 层不要直接操作 state.xxx.xxx,通过 VM 抽象简洁

✅ 九、可选:嵌套模块结构(高级)

你甚至可以这样嵌套:

AppState {user: {profile: {...},settings: {...}},cart: {...}
}

这种情况下,你需要在 userReducer 中进一步拆分 profileReducersettingsReducer,使用 combineReducers 组合子 reducer。


✅ 十、总结:State 拆分带来的优势
优势描述
🎯 更易维护每个模块只管理自己的状态和 reducer
🎯 更清晰状态结构层级明确,不混乱
🎯 更易测试每个模块可以单独单元测试
🎯 更易扩展新模块只需增加 reducer,不改原代码

✅ 6. 持久化(如本地缓存)

在 Flutter 中使用 Redux 时,**实现状态持久化(Redux Persistence)**可以让用户数据在应用关闭后仍能保留,比如登录状态、购物车、设置偏好等。


✅ 一、持久化 Redux 状态的常用方式
方式描述
shared_preferences + redux_persist✅ 最推荐,支持自动保存和恢复
自定义 middleware + 手动写入本地灵活但麻烦,适合特殊场景
使用 hydrated_redux类似 hydrated_bloc,不过支持不如 BLoC

✅ 二、推荐方案:使用 redux_persist + shared_preferences

插件地址:redux_persist

📦 添加依赖:
dependencies:redux: ^5.0.0flutter_redux: ^0.10.0redux_persist: ^0.8.3redux_persist_flutter: ^0.8.3shared_preferences: ^2.0.15

✅ 三、持久化流程图
+-----------------+
| App 启动        |
+--------+--------+|v
+--------v--------+      +-------------------------+
| 读取本地存储的  |<-----| shared_preferences      |
| JSON State      |      +-------------------------+
+--------+--------+|v
+--------v--------+
| 初始化 Redux    |
| Store,注入     |
| middleware +    |
| persistedState  |
+--------+--------+|v
+--------v--------+
| UI 使用状态     |
+-----------------+

✅ 四、完整示例代码(精简版)
1️⃣ app_state.dart
import 'dart:convert';class AppState {final bool loggedIn;AppState({required this.loggedIn});AppState copyWith({bool? loggedIn}) {return AppState(loggedIn: loggedIn ?? this.loggedIn);}static AppState initial() => AppState(loggedIn: false);Map<String, dynamic> toJson() => {'loggedIn': loggedIn};static AppState fromJson(dynamic json) =>AppState(loggedIn: json['loggedIn'] ?? false);
}

2️⃣ main.dart
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
import 'package:redux_persist/redux_persist.dart';
import 'package:redux_persist_flutter/redux_persist_flutter.dart';
import 'app_state.dart';class LoginAction {}AppState reducer(AppState state, dynamic action) {if (action is LoginAction) {return state.copyWith(loggedIn: true);}return state;
}void main() async {WidgetsFlutterBinding.ensureInitialized();final persistor = Persistor<AppState>(storage: FlutterStorage(),serializer: JsonSerializer<AppState>(AppState.fromJson),);final initialState = await persistor.load();final store = Store<AppState>(reducer,initialState: initialState ?? AppState.initial(),middleware: [persistor.createMiddleware()],);runApp(MyApp(store: store));
}class MyApp extends StatelessWidget {final Store<AppState> store;const MyApp({super.key, required this.store});Widget build(BuildContext context) {return StoreProvider(store: store,child: MaterialApp(title: 'Redux 持久化',home: StoreConnector<AppState, bool>(converter: (store) => store.state.loggedIn,builder: (context, loggedIn) => Scaffold(appBar: AppBar(title: Text('Redux 持久化')),body: Center(child: loggedIn? Text('✅ 已登录'): ElevatedButton(onPressed: () =>store.dispatch(LoginAction()),child: Text('登录')),),),),),);}
}

✅ 五、注意事项和技巧
⚠️ 项目建议
只保存需要的字段不建议保存整个 UI 状态,只保存核心业务数据
避免存储 Widget、Function、Controller 等类型JSON 序列化失败
大状态建议拆模块持久化userState.toJson()cartState.toJson() 分开管理
状态变化频繁时考虑节流(Throttle)否则频繁写入磁盘会影响性能
支持版本迁移处理新字段建议加默认值,否则老版本 json 会报错
使用 debugPrint() 打印序列化日志调试查看是否正确持久化与加载

✅ 六、持久化 + 中间件联动技巧

你可以在中间件中监听关键 Action,比如登录、登出、添加购物车等,手动触发保存操作:

Middleware<AppState> logMiddleware =(store, action, next) {next(action);if (action is LoginAction) {debugPrint("用户登录,建议持久化");}
};

✅ 七、结语

状态持久化是 Redux 应用走向真实业务场景的必经之路。

能力带来好处
✅ 状态恢复提升用户体验
✅ 离线缓存支持断网操作
✅ 重启不丢失数据支持会话继续
✅ 状态回放与测试可导出 JSON 做 diff、测试等

🚀 总结:进阶 Redux 用法清单

用法说明示例/关键方法
Middleware拦截 Action,做日志或异步处理loggingMiddlewareredux_thunk
Thunk Action异步调用网络接口ThunkAction<AppState>
ViewModel抽离 UI 与状态逻辑StoreConnector -> ViewModel
combineReducers拆分多个模块的 reducercombineReducers([...])
State 模块化拆分不同模块状态AppState(counterState, authState)
状态持久化(存储)保存状态到本地通过 Middleware 存入本地存储

📚 Flutter 状态管理系列文章目录
  1. Flutter 状态管理(setState、InheritedWidget、 Provider 、Riverpod、 BLoC / Cubit、 GetX 、MobX 、Redux)

  2. setState() 使用详解:原理及注意事项

  3. InheritedWidget 组件使用及原理

  4. Flutter 中 Provider 的使用、注意事项与原理解析(含代码实战)

  5. GetX 用法详细解析以及注意事项

  6. Flutter BLoC 使用详细解析

  7. Flutter MobX 响应式原理与实战详解

  8. Flutter Riverpod 使用详细解析

  9. Riverpod原理解析(实现一个自己的Riverpod

  10. flutter redux状态管理

http://www.lryc.cn/news/585101.html

相关文章:

  • 【unitrix】 4.21 类型级二进制数基本结构体(types.rs)
  • JavaScript加强篇——第五章 DOM节点(加强)与BOM
  • 【驱动】移植CH340驱动,设置 udev 规则,解决和 BRLTTY 的冲突
  • 容器管理: 单机用Docker Compose,多机用Kubernetes
  • 用 React Three Fiber 实现 3D 城市模型的扩散光圈特效
  • 保安员从业资格证历年考试真题
  • Debian:从GNOME切换到Xfce
  • 【音视频】HLS拉流抓包分析
  • 物联网与互联网融合生态
  • C#事件:从原理到实践的深度剖析
  • 小架构step系列11:单元测试引入
  • 基于规则匹配的文档标题召回
  • 【天坑记录】cursor jsx文件保存时错误格式化了
  • PHY模式,slave master怎么区分
  • [Dify] -基础入门4-快速创建你的第一个 Chat 应用
  • 三坐标微米级测量精度,高精度检测液压支架导向套的几何公差尺寸
  • 基于vscode的go环境安装简介
  • 冒泡、选择、插入排序:三大基础排序算法深度解析(C语言实现)
  • 排序算法(一):冒泡排序
  • 没有Mac如何完成iOS 上架:iOS App 上架App Store流程
  • python的社区残障人士服务系统
  • PC网站和uniapp安卓APP、H5接入支付宝支付
  • 通过命名空间引用了 Application 类,php不会自动包含路径文件吗?
  • Android原生TabLayout使用技巧
  • 没有管理员权限,在服务器安装使用 Jupyter + R 内核
  • springboot生成pdf方案之dot/html/图片转pdf三种方式
  • 深度学习入门教程(三)- 线性代数教程
  • SQL:数据库查询语言的核心技术
  • 语音对话秒译 + 视频悬浮字 + 相机即拍即译:ViiTor 如何破局跨语言场景?
  • FPGA实现SDI转LVDS视频发送,基于GTP+OSERDES2原语架构,提供工程源码和技术支持