You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Riverpod与Golden测试报错:组件树销毁后仍存在待处理Timer

解决Riverpod+Golden Toolkit循环测试的Timer pending问题

核心原因

循环调用带唯一key的ProviderScope时,部分Provider内部启动的Timer未在Widget树销毁时被正确取消,多次叠加后触发残留Timer报错。以下是保留ProviderScope key的前提下的解决方法:


1. 规范Provider内部Timer的生命周期管理

所有涉及Timer的Provider(如StateNotifier、ChangeNotifier)必须在dispose回调中主动取消Timer:

class CountNotifier extends StateNotifier<int> {
  Timer? _periodicTimer;

  CountNotifier() : super(0) {
    _periodicTimer = Timer.periodic(const Duration(seconds: 1), (_) {
      state++;
    });
  }

  @override
  void dispose() {
    _periodicTimer?.cancel(); // 销毁时强制取消Timer
    super.dispose();
  }
}

确保所有异步定时器都被绑定到Provider的生命周期,避免残留。

2. 手动控制ProviderContainer的销毁时机

在测试循环中,手动创建ProviderContainer并绑定到ProviderScope,测试完成后主动销毁容器,强制清理Provider资源:

for (final testCase in testCases) {
  // 手动创建容器
  final container = ProviderContainer(
    overrides: [/* 你的覆盖配置 */],
  );
  
  await generateScreenshot(
    tester,
    widget: ProviderScope(
      key: Key(testCase.key),
      container: container,
      child: YourAppWidget(),
    ),
    name: testCase.name,
  );
  
  // 测试后销毁容器,触发Provider dispose
  container.dispose();
  // 等待所有异步清理操作完成
  await tester.pumpAndSettle();
}

3. 用Golden Toolkit的scenarioWrapper封装清理逻辑

将ProviderScope的创建和销毁逻辑封装到场景包装器中,利用addTearDown确保每个测试场景结束后自动清理:

await multiScreenGolden(
  tester,
  'multi-scenario-golden',
  scenarios: testCases.map((case) => Scenario(
    widget: YourAppWidget(),
    name: case.name,
  )).toList(),
  scenarioWrapper: (child) {
    final container = ProviderContainer(overrides: [/* 覆盖配置 */]);
    // 每个场景用唯一key
    final scope = ProviderScope(
      key: Key(UniqueKey().toString()),
      container: container,
      child: child,
    );
    // 注册销毁回调
    addTearDown(() => container.dispose());
    return scope;
  },
);

4. 针对一次性Timer的临时处理

如果是短周期的一次性Timer,可在测试后等待Timer执行完成再销毁Widget树:

await generateScreenshot(...);
// 等待Timer执行完毕
await tester.pump(const Duration(milliseconds: 500));
await tester.pumpAndSettle();

注意:此方法仅适用于一次性Timer,周期性Timer仍需通过Provider dispose取消。


内容的提问来源于stack exchange,提问作者matteoh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 23:24:55