AsyncRedux调试技能Skill asyncredux-debugging

这个技能用于帮助开发者调试AsyncRedux应用程序,涵盖状态检查、动作跟踪、日志记录和性能监控。关键词:AsyncRedux, 调试, Flutter, 状态管理, 开发工具, 移动应用开发, 异步处理, 性能优化。

移动开发 0 次安装 0 次浏览 更新于 3/19/2026

name: asyncredux-debugging description: 有效调试 AsyncRedux 应用程序。涵盖使用 store.state 打印状态、检查 actionsInProgress()、使用 ConsoleActionObserver、StateObserver 进行状态变更跟踪,以及跟踪 dispatchCount/reduceCount。

调试 AsyncRedux 应用程序

AsyncRedux 提供了多种工具,用于在开发过程中调试和监控应用程序的状态、动作和行为。

检查存储状态

直接从存储访问当前状态:

// 直接状态访问
print(store.state);

// 访问特定部分
print(store.state.user.name);
print(store.state.cart.items);

跟踪正在进行的动作

使用 actionsInProgress() 查看当前正在处理的动作:

// 返回一个不可修改的当前运行动作集合
Set<ReduxAction<AppState>> inProgress = store.actionsInProgress();

// 检查是否有任何动作正在运行
if (inProgress.isEmpty) {
  print('没有动作在进行中');
} else {
  for (var action in inProgress) {
    print('正在运行: ${action.runtimeType}');
  }
}

// 获取正在进行的动作副本
Set<ReduxAction<AppState>> copy = store.copyActionsInProgress();

// 检查特定动作是否匹配
bool matches = store.actionsInProgressEqualTo(expectedSet);

分发和减少计数

跟踪自存储创建以来已分发的动作数量和已执行的状态减少次数:

// 自存储创建以来分发的总动作数
print('分发计数: ${store.dispatchCount}');

// 执行的总状态减少次数
print('减少计数: ${store.reduceCount}');

这些计数器适用于:

  • 验证测试期间分发的动作
  • 检测意外分发
  • 性能监控

控制台动作观察器

内置的 ConsoleActionObserver 使用颜色格式化将分发的动作打印到控制台:

var store = Store<AppState>(
  initialState: AppState.initialState(),
  // 仅在调试模式下启用
  actionObservers: kReleaseMode ? null : [ConsoleActionObserver()],
);

控制台输出示例:

I/flutter (15304): | Action MyAction
I/flutter (15304): | Action LoadUserAction(user32)

动作以黄色(默认)或绿色(用于 WaitActionNavigateAction)显示。

自定义动作输出

在您的动作中覆盖 toString() 以显示额外信息:

class LoginAction extends AppAction {
  final String username;
  LoginAction(this.username);

  @override
  Future<AppState?> reduce() async {
    // ...
  }

  @override
  String toString() => 'LoginAction(username: $username)';
}

自定义颜色方案

通过修改静态 color 回调自定义颜色方案:

ConsoleActionObserver.color = (action) {
  if (action is ErrorAction) return ConsoleActionObserver.red;
  if (action is NetworkAction) return ConsoleActionObserver.blue;
  return ConsoleActionObserver.yellow;
};

可用颜色:whiteredblueyellowgreengreydark

用于状态变更日志的 StateObserver

创建 StateObserver 以记录状态变更:

class DebugStateObserver implements StateObserver<AppState> {
  @override
  void observe(
    ReduxAction<AppState> action,
    AppState prevState,
    AppState newState,
    Object? error,
    int dispatchCount,
  ) {
    final changed = !identical(prevState, newState);

    print('--- 动作 #$dispatchCount: ${action.runtimeType} ---');
    print('状态已变更: $changed');

    if (changed) {
      // 记录特定状态变更
      if (prevState.user != newState.user) {
        print('  用户已变更: ${prevState.user} -> ${newState.user}');
      }
      if (prevState.counter != newState.counter) {
        print('  计数器已变更: ${prevState.counter} -> ${newState.counter}');
      }
    }

    if (error != null) {
      print('  错误: $error');
    }
  }
}

// 配置存储
var store = Store<AppState>(
  initialState: AppState.initialState(),
  stateObservers: kDebugMode ? [DebugStateObserver()] : null,
);

检测状态变更

使用 identical() 检查状态是否实际变更:

bool stateChanged = !identical(prevState, newState);

这很高效,因为 AsyncRedux 使用不可变状态 - 如果引用相同,则未发生变更。

用于详细日志的自定义 ActionObserver

创建 ActionObserver 以进行详细的分发跟踪:

class DetailedActionObserver implements ActionObserver<AppState> {
  final Map<ReduxAction, DateTime> _startTimes = {};

  @override
  void observe(
    ReduxAction<AppState> action,
    int dispatchCount, {
    required bool ini,
  }) {
    if (ini) {
      // 动作开始
      _startTimes[action] = DateTime.now();
      print('[开始 #$dispatchCount] ${action.runtimeType}');
    } else {
      // 动作结束
      final startTime = _startTimes.remove(action);
      if (startTime != null) {
        final duration = DateTime.now().difference(startTime);
        print('[结束 #$dispatchCount] ${action.runtimeType} (${duration.inMilliseconds}ms)');
      } else {
        print('[结束 #$dispatchCount] ${action.runtimeType}');
      }
    }
  }
}

调试部件重建

使用 ModelObserverDefaultModelObserver 跟踪哪些部件重建:

var store = Store<AppState>(
  initialState: AppState.initialState(),
  modelObserver: DefaultModelObserver(),
);

输出格式:

Model D:1 R:1 = Rebuild:true, Connector:MyWidgetConnector, Model:MyViewModel{data}.
Model D:2 R:2 = Rebuild:false, Connector:MyWidgetConnector, Model:MyViewModel{data}.
  • D: 分发计数
  • R: 重建计数
  • Rebuild: 部件是否实际重建
  • Connector: StoreConnector 类型
  • Model: 带有状态摘要的 ViewModel

通过将 debug: this 传递给 StoreConnector 启用详细输出:

StoreConnector<AppState, MyViewModel>(
  debug: this, // 在输出中启用连接器名称
  converter: (store) => MyViewModel.fromStore(store),
  builder: (context, vm) => MyWidget(vm),
)

在部件中检查动作状态

使用上下文扩展检查动作状态:

Widget build(BuildContext context) {
  // 检查动作是否当前正在运行
  if (context.isWaiting(LoadDataAction)) {
    return CircularProgressIndicator();
  }

  // 检查动作是否失败
  if (context.isFailed(LoadDataAction)) {
    var exception = context.exceptionFor(LoadDataAction);
    return Text('错误: ${exception?.message}');
  }

  return Text('数据: ${context.state.data}');
}

在测试中等待条件

使用存储等待方法进行测试调试:

// 等待直到状态满足条件
await store.waitCondition((state) => state.isLoaded);

// 等待特定动作类型完成
await store.waitAllActionTypes([LoadUserAction, LoadSettingsAction]);

// 等待所有动作完成(空列表 = 等待所有)
await store.waitAllActions([]);

// 等待动作条件,访问正在进行的动作
await store.waitActionCondition((actionsInProgress, triggerAction) {
  return actionsInProgress.isEmpty;
});

完整调试设置示例

void main() {
  final store = Store<AppState>(
    initialState: AppState.initialState(),
    // 动作日志记录(仅调试)
    actionObservers: kDebugMode
        ? [ConsoleActionObserver(), DetailedActionObserver()]
        : null,
    // 状态变更日志记录(仅调试)
    stateObservers: kDebugMode
        ? [DebugStateObserver()]
        : null,
    // 部件重建跟踪(仅调试)
    modelObserver: kDebugMode ? DefaultModelObserver() : null,
    // 错误观察器(始终启用)
    errorObserver: MyErrorObserver(),
  );

  // 调试打印初始状态
  if (kDebugMode) {
    print('初始状态: ${store.state}');
    print('分发计数: ${store.dispatchCount}');
  }

  runApp(StoreProvider<AppState>(
    store: store,
    child: MyApp(),
  ));
}

调试技巧

  1. 在动作中打印状态:在您的减速器中使用 print(state) 查看该时刻的状态
  2. 检查初始状态:访问 action.initialState 查看动作分发时的状态(对比当前 state
  3. 使用动作状态:在分发后检查 action.status.isCompletedOkaction.status.originalError
  4. 条件日志记录:使用 package:flutter/foundation.dart 中的 kDebugMode 在生产中禁用
  5. 覆盖 toString:在动作和状态类上实现 toString() 以获得更好的调试输出

参考

文档中的 URL: