Redux簡(jiǎn)介:
在Flutter中,使用Redux進(jìn)行狀態(tài)管理是一種流行的做法,特別是在大型或復(fù)雜的應(yīng)用程序中。Redux是一個(gè)來自JavaScript社區(qū)的庫,它提供了一種可預(yù)測(cè)的、可追蹤的狀態(tài)管理方式。在Flutter中使用Redux可以幫助你更好地組織和維護(hù)應(yīng)用的狀態(tài),使得狀態(tài)的管理更加集中和高效。(來自AI)
通常redux包含如下組成部分:
State(狀態(tài))
應(yīng)用的全局狀態(tài),通常是一個(gè)不可變的對(duì)象。Action(動(dòng)作)
Action 是一個(gè)普通對(duì)象,用于描述發(fā)生了什么。它是觸發(fā)狀態(tài)變更的唯一方式。Reducer(歸并函數(shù))
Reducer 是一個(gè)純函數(shù),接收當(dāng)前狀態(tài)和一個(gè) action,返回新的狀態(tài)。Reducer 不會(huì)修改原狀態(tài),而是返回一個(gè)新的狀態(tài)對(duì)象。Store(狀態(tài)容器)
Store 是 Redux 的核心,它保存了應(yīng)用的狀態(tài)樹,并提供方法來分發(fā) action 和監(jiān)聽狀態(tài)變化??梢酝ㄟ^ StoreProvider 將 Store 注入到 Flutter 的 Widget 樹中。
下面讓我們使用redux實(shí)現(xiàn)數(shù)據(jù)顯示以及UI刷新。
1.State添加
通常,項(xiàng)目中會(huì)有一個(gè)主State,來存儲(chǔ)子State,子State是由具體業(yè)務(wù)進(jìn)行劃分的:
/// 項(xiàng)目唯一的主DemoState
class DemoState {
/// 子deviceState
final DemoDeviceState deviceState;
/// 子userState
final DemoUserState userState;
DemoState({
required this.deviceState,
required this.userState,
});
/// 通過子state數(shù)據(jù)來生成一個(gè)新的主state
DemoState copyWith({
DemoDeviceState? deviceState,
DemoUserState? userState,
}) {
return DemoState(
deviceState: deviceState ?? this.deviceState,
userState: userState ?? this.userState,
);
}
}
子UserState: 存儲(chǔ)用戶相關(guān)信息
/// 子 userState
class DemoUserState {
final UserModel userModel;
final List<String> deviceIdList;
DemoUserState(
{this.userModel = const UserModel('', ''),
this.deviceIdList = const <String>[]});
/// 生成一個(gè)新的 DemoUserState
DemoUserState copyWith({UserModel? userModel, List<String>? deviceIdList}) {
return DemoUserState(
userModel: userModel ?? this.userModel,
deviceIdList: deviceIdList ?? this.deviceIdList,
);
}
}
子DeviceState: 存儲(chǔ)設(shè)備列表相關(guān)信息
/// 子 deviceState
class DemoDeviceState {
final Map<String, DeviceModel> deviceMap;
DemoDeviceState({this.deviceMap = const <String, DeviceModel>{}});
/// 生成一個(gè)新的 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相關(guān)數(shù)據(jù)更新的Action:
/// 更新 DeviceModel
class UpdateDeviceMapAction {
final DeviceModel deviceModel;
UpdateDeviceMapAction({required this.deviceModel});
}
UserState相關(guān)數(shù)據(jù)更新的Action:
/// 更新 UserModel
class UpdateUserModelAction {
final UserModel userModel;
UpdateUserModelAction({required this.userModel});
}
3.Reducer添加
DeviceState數(shù)據(jù)更新相關(guān)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數(shù)據(jù)更新相關(guān)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數(shù)據(jù)更新事件
userCombineReducer, // UserState數(shù)據(jù)更新事件
]);
4.Store添加
初始化全局唯一的Store:
final Store<DemoState> demoStore = Store<DemoState>(
demoReducer,
initialState:
DemoState(deviceState: DemoDeviceState(), userState: DemoUserState()),
);
至此,redux相關(guān)準(zhǔn)備工作已完成,下面,我們將State內(nèi)存儲(chǔ)的數(shù)據(jù)掛載至頁面顯示。
我們通過StoreConnector將state內(nèi)的數(shù)據(jù)更新到UI中進(jìn)行顯示,StoreConnector包含兩個(gè)主要的參數(shù),一個(gè)是converter,負(fù)責(zé)將UI展示需要的數(shù)據(jù)進(jìn)行組裝。一個(gè)是builder負(fù)責(zé)將converter返回的數(shù)據(jù)更新至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 為整個(gè)頁面提供數(shù)據(jù)源
return StoreProvider<DemoState>(
store: demoStore,
child: Scaffold(
appBar: AppBar(
leading: GestureDetector(
onTap: () {
final String deviceId = '${count++}';
DeviceModel deviceModel = DeviceModel(
deviceName: '設(shè)備$deviceId',
deviceId: deviceId,
attributes: {});
demoStore
.dispatch(UpdateDeviceMapAction(deviceModel: deviceModel));
if (!count.isEven) {
// demoStore.dispatch(UpdateDeviceListAction(deviceId: deviceId));
}
},
child: const Text(
'+10臺(tái)設(shè)備',
style: TextStyle(fontSize: 20, color: Colors.orange),
),
),
actions: [
GestureDetector(
onTap: () {
// demoStore.dispatch(UpdateNothingAction());
},
child: const Center(
child: Text(
'無數(shù)據(jù)更新',
style: TextStyle(fontSize: 20, color: Colors.orange),
),
)),
],
/// 通過StoreConnector將數(shù)據(jù)更新到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(
'設(shè)備: ${deviceIdList[index]}',
style: const TextStyle(fontSize: 20, color: Colors.orange),
);
},
itemCount: deviceList.length,
);
}),
),
);
}
}