Flutter状态管理之Redux,demo示例

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

/// 事件绑定
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内存储的数据挂载至页面显示。

我们通过StoreConnectorstate内的数据更新到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,
              );
            }),
      ),
    );
  }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容