Flutter狀態(tài)管理之Redux,demo示例

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綁定userCombineReducerdeviceCombineReducer:

/// 事件綁定
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ù)掛載至頁面顯示。

我們通過StoreConnectorstate內(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,
              );
            }),
      ),
    );
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容