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

Flutter中BLoC结合Timer等需释放资源场景的状态处理疑问

解决方案:同时处理BLoC API状态与Timer资源释放

方案1:页面State中管理Timer,联动BLoC

直接在页面的StatefulWidget里维护Timer,是最轻量化的方式,无需额外创建BLoC。核心是在dispose中取消Timer,同时在Timer回调里先检查页面是否mounted,避免调用已销毁的context。

示例代码:

class YourPage extends StatefulWidget {
  const YourPage({super.key});

  @override
  State<YourPage> createState() => _YourPageState();
}

class _YourPageState extends State<YourPage> {
  Timer? _timer;
  final Stopwatch _stopwatch = Stopwatch();

  @override
  void initState() {
    super.initState();
    _startTimer();
    // 触发API请求
    context.read<ApiBloc>().add(FetchData());
  }

  void _startTimer() {
    _stopwatch.start();
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if (!mounted) {
        timer.cancel();
        return;
      }
      // 通知BLoC更新计时状态
      context.read<ApiBloc>().add(UpdateTimer(_stopwatch.elapsed));
    });
  }

  @override
  void dispose() {
    _timer?.cancel();
    _stopwatch.stop();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocBuilder<ApiBloc, ApiState>(
        builder: (context, state) {
          if (state is ApiLoading) {
            return const CircularProgressIndicator();
          } else if (state is ApiLoadedWithTimer) {
            return Text('计时: ${state.elapsed.inSeconds}s');
          }
          return const SizedBox();
        },
      ),
    );
  }
}

方案2:扩展现有API BLoC,内置Timer管理

若希望Timer逻辑与BLoC绑定,可给现有ApiBloc添加Timer相关的事件和状态,由BLoC自主管理Timer生命周期,页面仅需发送启动/停止事件。

示例代码(BLoC部分):

// 扩展事件
abstract class ApiEvent {}
class FetchData extends ApiEvent {}
class StartTimer extends ApiEvent {}
class StopTimer extends ApiEvent {}
class _UpdateTimer extends ApiEvent {} // 内部事件,由Timer触发

// 扩展状态
abstract class ApiState {}
class ApiLoading extends ApiState {}
class ApiLoaded extends ApiState {}
class ApiLoadedWithTimer extends ApiState {
  final Duration elapsed;
  ApiLoadedWithTimer(this.elapsed);
}

class ApiBloc extends Bloc<ApiEvent, ApiState> {
  Timer? _timer;
  final Stopwatch _stopwatch = Stopwatch();

  ApiBloc() : super(ApiLoading()) {
    on<FetchData>(_handleFetchData);
    on<StartTimer>(_handleStartTimer);
    on<StopTimer>(_handleStopTimer);
    on<_UpdateTimer>(_handleUpdateTimer);
  }

  Future<void> _handleFetchData(FetchData event, Emitter<ApiState> emit) async {
    // 模拟API请求
    await Future.delayed(const Duration(seconds: 2));
    emit(ApiLoaded());
  }

  void _handleStartTimer(StartTimer event, Emitter<ApiState> emit) {
    _stopwatch.start();
    _timer = Timer.periodic(const Duration(seconds: 1), (_) => add(_UpdateTimer()));
  }

  void _handleStopTimer(StopTimer event, Emitter<ApiState> emit) {
    _timer?.cancel();
    _stopwatch.stop();
  }

  void _handleUpdateTimer(_UpdateTimer event, Emitter<ApiState> emit) {
    if (state is ApiLoaded) {
      emit(ApiLoadedWithTimer(_stopwatch.elapsed));
    }
  }

  @override
  Future<void> close() {
    _timer?.cancel();
    _stopwatch.stop();
    return super.close();
  }
}

页面调用示例:

class YourPage extends StatefulWidget {
  const YourPage({super.key});

  @override
  State<YourPage> createState() => _YourPageState();
}

class _YourPageState extends State<YourPage> {
  @override
  void dispose() {
    // 页面销毁时通知BLoC停止Timer
    context.read<ApiBloc>().add(StopTimer());
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocListener<ApiBloc, ApiState>(
        listenWhen: (prev, curr) => prev is ApiLoading && curr is ApiLoaded,
        listener: (_, __) => context.read<ApiBloc>().add(StartTimer()),
        child: BlocBuilder<ApiBloc, ApiState>(
          builder: (_, state) {
            if (state is ApiLoadedWithTimer) {
              return Text('计时: ${state.elapsed.inSeconds}s');
            }
            return const CircularProgressIndicator();
          },
        ),
      ),
    );
  }
}

方案3:用Stream替代Timer,结合StreamSubscription

在BLoC中创建周期性Stream,用StreamSubscription管理,更符合响应式范式,也能安全控制生命周期。

示例代码(BLoC核心部分):

class ApiBloc extends Bloc<ApiEvent, ApiState> {
  StreamSubscription? _timerSubscription;
  final Stopwatch _stopwatch = Stopwatch();

  ApiBloc() : super(ApiLoading()) {
    on<StartTimer>(_handleStartTimer);
    on<StopTimer>(_handleStopTimer);
  }

  void _handleStartTimer(StartTimer event, Emitter<ApiState> emit) {
    _stopwatch.start();
    _timerSubscription = Stream.periodic(const Duration(seconds: 1))
        .listen((_) => add(_UpdateTimer()));
  }

  void _handleStopTimer(StopTimer event, Emitter<ApiState> emit) {
    _timerSubscription?.cancel();
    _stopwatch.stop();
  }

  @override
  Future<void> close() {
    _timerSubscription?.cancel();
    _stopwatch.stop();
    return super.close();
  }

  // 其他事件/状态处理逻辑...
}

关键注意事项

  • 核心原则:确保Timer/StreamSubscription在页面或BLoC销毁时被取消,避免资源泄漏和无效context调用。
  • 在StatefulWidget中使用context时,必须在回调里先检查mounted状态,防止页面销毁后调用context.read。
  • 无需为简单Timer单独创建BLoC,优先选择页面State管理或扩展现有BLoC,避免过度设计。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 19:40:43