Redux简介:
在Flutter中,使用Redux进行状态管理是一种流行的做法,特别是在大型或复杂的应用程序中。Redux是一个来自JavaScript社区的库,它提供了一种可预测的、可追踪的状态管理方式。在Flutter中使用Redux可以帮助你更好地组织和维护应用的状态,使得状态的管理更加集中和高效。(来自AI)
通常redux包含如下组成部分:
State(状态)
应用的全局状态,通常是一个不可变的对象。Action(动作)
Action 是一个普通对象,用于描述发生了什么。它是触发状态变更的唯一方式。Reducer(归并函数)
Reducer 是一个纯函数,接收当前状态和一个 action,返回新的状态。Reducer 不会修改原状态,而是返回一个新的状态对象。Store(状态容器)
Store 是 Redux 的核心,它保存了应用的状态树,并提供方法来分发 action 和监听状态变化。可以通过 StoreProvider 将 Store 注入到 Flutter 的 Widget 树中。
下面让我们使用redux实现数据显示以及UI刷新。
1.State添加
通常,项目中会有一个主State,来存储子State,子State是由具体业务进行划分的:
/// 项目唯一的主DemoState
class DemoState {
/// 子deviceState
final DemoDeviceState deviceState;
/// 子userState
final DemoUserState userState;
DemoState({
required this.deviceState,
required this.userState,
});
/// 通过子state数据来生成一个新的主state
DemoState copyWith({
DemoDeviceState? deviceState,
DemoUserState? userState,
}) {
return DemoState(
deviceState: deviceState ?? this.deviceState,
userState: userState ?? this.userState,
);
}
}
子UserState: 存储用户相关信息
/// 子 userState
class DemoUserState {
final UserModel userModel;
final List<String> deviceIdList;
DemoUserState(
{this.userModel = const UserModel('', ''),
this.deviceIdList = const <String>[]});
/// 生成一个新的 DemoUserState
DemoUserState copyWith({UserModel? userModel, List<String>? deviceIdList}) {
return DemoUserState(
userModel: userModel ?? this.userModel,
deviceIdList: deviceIdList ?? this.deviceIdList,
);
}
}
子DeviceState: 存储设备列表相关信息
/// 子 deviceState
class DemoDeviceState {
final Map<String, DeviceModel> deviceMap;
DemoDeviceState({this.deviceMap = const <String, DeviceModel>{}});
/// 生成一个新的 DemoDeviceState
DemoDeviceState copyWith({Map<String, DeviceModel>? deviceMap}) {
return DemoDeviceState(
deviceMap: deviceMap ?? this.deviceMap,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is DemoDeviceState &&
runtimeType == other.runtimeType &&
mapEquals(deviceMap, other.deviceMap);
@override
int get hashCode => mapHashCode(deviceMap);
}
2.Action添加
DeviceState相关数据更新的Action:
/// 更新 DeviceModel
class UpdateDeviceMapAction {
final DeviceModel deviceModel;
UpdateDeviceMapAction({required this.deviceModel});
}
UserState相关数据更新的Action:
/// 更新 UserModel
class UpdateUserModelAction {
final UserModel userModel;
UpdateUserModelAction({required this.userModel});
}
3.Reducer添加
DeviceState数据更新相关Action事件处理:
/// 事件绑定
final Reducer<DemoState> deviceCombineReducer =
combineReducers<DemoState>(<Reducer<DemoState>>[
TypedReducer<DemoState, UpdateDeviceMapAction>(_updateDeviceMap).call,
]);
/// 处理 UpdateDeviceMapAction 事件
DemoState _updateDeviceMap(DemoState state, UpdateDeviceMapAction action) {
Map<String, DeviceModel> map = {};
map.addAll(state.deviceState.deviceMap);
map[action.deviceModel.deviceId] = action.deviceModel;
state = state.copyWith(
deviceState: state.deviceState.copyWith(deviceMap: map),
);
return state;
}
UserState数据更新相关Action事件处理:
/// 事件绑定
final Reducer<DemoState> userCombineReducer =
combineReducers<DemoState>(<Reducer<DemoState>>[
TypedReducer<DemoState, UpdateUserModelAction>(_updateUserId).call,
]);
/// 处理 UpdateUserModelAction 事件
DemoState _updateUserId(DemoState state, UpdateUserModelAction action) {
state = state.copyWith(
userState: state.userState.copyWith(
userModel: action.userModel,
),
);
return state;
}
主State的reducer
绑定userCombineReducer
和deviceCombineReducer
:
/// 事件绑定
final Reducer<DemoState> demoReducer =
combineReducers<DemoState>(<Reducer<DemoState>>[
deviceCombineReducer, // DeviceState数据更新事件
userCombineReducer, // UserState数据更新事件
]);
4.Store添加
初始化全局唯一的Store:
final Store<DemoState> demoStore = Store<DemoState>(
demoReducer,
initialState:
DemoState(deviceState: DemoDeviceState(), userState: DemoUserState()),
);
至此,redux相关准备工作已完成,下面,我们将State内存储的数据挂载至页面显示。
我们通过StoreConnector
将state
内的数据更新到UI中进行显示,StoreConnector
包含两个主要的参数,一个是converter
,负责将UI展示需要的数据进行组装。一个是builder
负责将converter
返回的数据更新至UI
class DemoPage extends StatefulWidget {
const DemoPage({super.key});
@override
State<DemoPage> createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
int count = 0;
final DeviceListSelectors deviceListSelectors = DeviceListSelectors();
@override
Widget build(BuildContext context) {
// StoreProvider 为整个页面提供数据源
return StoreProvider<DemoState>(
store: demoStore,
child: Scaffold(
appBar: AppBar(
leading: GestureDetector(
onTap: () {
final String deviceId = '${count++}';
DeviceModel deviceModel = DeviceModel(
deviceName: '设备$deviceId',
deviceId: deviceId,
attributes: {});
demoStore
.dispatch(UpdateDeviceMapAction(deviceModel: deviceModel));
if (!count.isEven) {
// demoStore.dispatch(UpdateDeviceListAction(deviceId: deviceId));
}
},
child: const Text(
'+10台设备',
style: TextStyle(fontSize: 20, color: Colors.orange),
),
),
actions: [
GestureDetector(
onTap: () {
// demoStore.dispatch(UpdateNothingAction());
},
child: const Center(
child: Text(
'无数据更新',
style: TextStyle(fontSize: 20, color: Colors.orange),
),
)),
],
/// 通过StoreConnector将数据更新到AppBar中
title: StoreConnector<DemoState, AppBarModel>(
distinct: true,
converter: (store) => AppBarModel(
userName: store.state.userState.userModel.userName,
userId: store.state.userState.userModel.userId),
builder: (context, AppBarModel model) {
print('ss - appBar build');
return GestureDetector(
onTap: () {
demoStore.dispatch(UpdateUserModelAction(
userModel: const UserModel('sfj', '001')));
},
child: Text('用户:${model.userName}: ${model.userId}'),
);
},
),
),
body: StoreConnector<DemoState, List<String>>(
distinct: true,
converter: (Store<DemoState> store) {
print('ss - ListView converter');
return store.state.userState.deviceIdList;
},
builder: (BuildContext context, List<String> deviceIdList) {
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
DeviceModel deviceModel = model.deviceList[index];
return Text(
'设备: ${deviceIdList[index]}',
style: const TextStyle(fontSize: 20, color: Colors.orange),
);
},
itemCount: deviceList.length,
);
}),
),
);
}
}