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

如何让Hydrated Bloc中state的expirationDate实时触发更新?

解决方案:在Hydrated Bloc中实现会话到期实时更新

要实现到期时间自动触发状态更新,核心是在SessionBloc内部通过定时器监听到期时间,自动触发状态变更事件,让UI实时响应。以下是具体实现步骤:

1. 完善SessionState

添加copyWith方法方便状态更新,同时补全Equatable的props重写:

@immutable
class SessionState extends Equatable {
  const SessionState({
    this.expirationDate,
    this.isExpired = true,
  });

  final DateTime? expirationDate;
  final bool isExpired;

  @override
  List<Object?> get props => [expirationDate, isExpired];

  SessionState copyWith({
    DateTime? expirationDate,
    bool? isExpired,
  }) {
    return SessionState(
      expirationDate: expirationDate ?? this.expirationDate,
      isExpired: isExpired ?? this.isExpired,
    );
  }
}

2. 实现SessionBloc的定时逻辑

在Bloc内部管理定时器,到期时自动触发状态更新,同时处理Hydrated的持久化:

import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:equatable/equatable.dart';

abstract class SessionEvent extends Equatable {
  const SessionEvent();

  @override
  List<Object> get props => [];
}

class SessionStarted extends SessionEvent {
  const SessionStarted(this.expirationDate);

  final DateTime expirationDate;

  @override
  List<Object> get props => [expirationDate];
}

class SessionExtended extends SessionEvent {
  const SessionExtended(this.newExpirationDate);

  final DateTime newExpirationDate;

  @override
  List<Object> get props => [newExpirationDate];
}

class SessionExpired extends SessionEvent {}

class SessionBloc extends HydratedBloc<SessionEvent, SessionState> {
  SessionBloc() : super(const SessionState()) {
    on<SessionStarted>(_onSessionStarted);
    on<SessionExtended>(_onSessionExtended);
    on<SessionExpired>(_onSessionExpired);

    // 初始化时检查并设置定时器
    _scheduleExpirationTimer();
  }

  Timer? _expirationTimer;

  void _onSessionStarted(SessionStarted event, Emitter<SessionState> emit) {
    final newState = state.copyWith(
      expirationDate: event.expirationDate,
      isExpired: false,
    );
    emit(newState);
    _scheduleExpirationTimer();
  }

  void _onSessionExtended(SessionExtended event, Emitter<SessionState> emit) {
    final newState = state.copyWith(
      expirationDate: event.newExpirationDate,
      isExpired: false,
    );
    emit(newState);
    _scheduleExpirationTimer();
  }

  void _onSessionExpired(SessionExpired event, Emitter<SessionState> emit) {
    emit(state.copyWith(isExpired: true));
    _expirationTimer?.cancel();
    _expirationTimer = null;
  }

  void _scheduleExpirationTimer() {
    // 先取消之前的定时器,避免重复触发
    _expirationTimer?.cancel();

    final expirationDate = state.expirationDate;
    if (expirationDate == null || state.isExpired) return;

    final now = DateTime.now();
    if (expirationDate.isBefore(now)) {
      // 已经过期,直接触发过期事件
      add(SessionExpired());
      return;
    }

    // 计算剩余时间,设置定时器
    final duration = expirationDate.difference(now);
    _expirationTimer = Timer(duration, () => add(SessionExpired()));
  }

  @override
  SessionState? fromJson(Map<String, dynamic> json) {
    final expirationDateStr = json['expirationDate'] as String?;
    return SessionState(
      expirationDate: expirationDateStr != null ? DateTime.parse(expirationDateStr) : null,
      isExpired: json['isExpired'] as bool? ?? true,
    );
  }

  @override
  Map<String, dynamic>? toJson(SessionState state) {
    return {
      'expirationDate': state.expirationDate?.toIso8601String(),
      'isExpired': state.isExpired,
    };
  }

  @override
  Future<void> close() {
    // 销毁时取消定时器,防止内存泄漏
    _expirationTimer?.cancel();
    return super.close();
  }
}

3. 简化UI层的状态判断

现在UI可以直接使用state.isExpired,无需每次计算时间,状态更新时会自动刷新:

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Material App',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: Colors.purple,
      ),
      home: BlocBuilder<SessionBloc, SessionState>(
        builder: (context, state) {
          return state.isExpired ? const LoginBuilder() : const RoleScreensBuilder();
        },
      ),
    );
  }
}

方案说明

  • 状态驱动UI:所有时间判断逻辑集中在Bloc内部,UI只需要响应状态变化,符合Bloc的架构设计。
  • 持久化兼容:通过HydratedBloc的fromJson/toJson,重启App后会自动恢复状态并重新设置定时器。
  • 内存安全:在Bloc销毁时取消定时器,避免内存泄漏。
  • 扩展性:支持会话续期(通过SessionExtended事件),只需更新过期时间即可重新设置定时器。

内容的提问来源于stack exchange,提问作者Samuel Alcántara

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 14:17:08