如何让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
相关产品推荐
相关产品推荐

