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

Flutter Bloc中使用Timer定时30秒拉取数据的问题

解决Flutter Bloc中定时拉取数据的问题

当然可以把定时拉取数据的逻辑完全移入Bloc内部,你的代码之所以失效,是因为Timer的回调没有正确触发Bloc的事件处理流程。直接调用_fetchData()并不能让Bloc处理它返回的状态流,必须通过Bloc的add()方法触发事件,才能让mapEventToState正确处理并生成状态。

问题分析

  1. 你原来的_resetTimer回调里直接调用_fetchData(),但_fetchData返回的Stream<BlocState>并没有被Bloc的状态流订阅,所以状态不会更新。
  2. 尝试用yield* _resetTimer(...)的方式是错误的,因为_resetTimer不是返回Stream的异步生成器函数,强行yield*会导致Stream控制器的类型不匹配,触发你看到的错误。

正确实现方案

核心思路是:让Timer的回调通过add()方法触发Bloc事件,而非直接调用状态生成函数。这样Bloc就能通过标准的mapEventToState流程处理定时拉取需求。

以下是完整的修正代码:

1. 事件与状态定义(如果还没定义的话)

// 事件类
abstract class BlocEvent {}
class DataFetchRequested extends BlocEvent {}

// 状态类
abstract class BlocState {}
class StateInitial extends BlocState {}
class StateLoading extends BlocState {}
class StateSuccess extends BlocState {
  final dynamic data;
  StateSuccess(this.data);
}
class StateFailure extends BlocState {
  final String error;
  StateFailure(this.error);
}

2. 修正后的DataBloc

class DataBloc extends Bloc<BlocEvent, BlocState> { 
  final Api _api; 
  Timer _timer; 

  // 使用最新的Bloc构造器写法,替代废弃的initialState getter
  DataBloc(this._api) : super(StateInitial()); 

  @override 
  Stream<BlocState> mapEventToState(BlocEvent event) async* { 
    if (event is DataFetchRequested) { 
      // 启动/重置定时器,确保每次触发事件时都刷新定时周期
      _resetTimer();
      // 执行数据拉取并生成状态
      yield* _fetchData(); 
    } 
  } 

  Stream<BlocState> _fetchData() async* { 
    yield StateLoading(); 
    try { 
      final result = await _api.fetch(); 
      yield StateSuccess(result); 
    } catch (e) { 
      // 拉取失败时取消定时器,避免持续触发错误请求
      _timer?.cancel();
      _timer = null;
      yield StateFailure(e.toString()); 
    } 
  } 

  void _resetTimer() { 
    // 先取消之前的定时器,避免重复触发
    _timer?.cancel(); 
    // 每30秒触发一次DataFetchRequested事件
    _timer = Timer.periodic(const Duration(seconds: 30), (timer) {
      add(DataFetchRequested());
    }); 
  } 

  @override 
  Future<void> close() { 
    // Bloc关闭时务必取消定时器,防止内存泄漏
    _timer?.cancel(); 
    return super.close(); 
  } 
}

关键改动说明

  • Timer回调触发事件:在_resetTimer的回调中,我们调用add(DataFetchRequested()),让Bloc通过标准流程处理拉取请求,确保状态流能正确更新。
  • 错误时取消定时器:数据拉取失败后取消定时器,避免后续持续触发错误请求,提升用户体验。
  • 符合Bloc规范:使用最新的构造器写法替代废弃的initialState getter,避免版本兼容问题。

另一种方案:使用Stream.periodic

如果你更喜欢用Stream.periodic,也可以这样实现(注意要处理流的取消):

@override
Stream<BlocState> mapEventToState(BlocEvent event) async* {
  if (event is DataFetchRequested) {
    // 第一次立即拉取
    yield* _fetchData();
    // 每隔30秒拉取一次,直到Bloc关闭或出错
    await for (final _ in Stream.periodic(const Duration(seconds: 30))) {
      // 如果定时器已被取消(比如之前拉取失败),终止流
      if (_timer == null) break;
      yield* _fetchData();
    }
  }
}

// 记得在_fetchData的catch块中设置_timer = null

总结

把定时拉取逻辑放在Bloc内部是完全可行的,这样UI层只需要在初始化时触发一次DataFetchRequested事件,后续的定时逻辑都由Bloc自行处理,完美实现了关注点分离,让UI层更简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 18:07:40