如何结合AuthBloc实现动画启动页?当前Navigator跳转是否安全?
问题描述
我正在实现一个带动画的启动页,但不确定当前的实现方式是否安全。目前代码可以正常运行,但希望结合AuthBloc + State + Events的结构来实现该功能,同时认为当前使用Navigator/MaterialPageRoute的方式并非最优或安全方案。
现有代码
Main.dart文件
void main() { WidgetsFlutterBinding.ensureInitialized(); runApp( MaterialApp( debugShowCheckedModeBanner: false, title: 'Take Note', theme: ThemeData( primarySwatch: Colors.green, ), home: const SplashScreen(), routes: { // Routes for the app createOrUpdateNoteRoute: (context) => const CreateUpdateNoteView(), }, ), ); }
splash_screen.dart中的_playVideo方法
void _playVideo() async { _controller.play(); await Future.delayed(const Duration(seconds: 4)); if (!mounted) { return; } Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (context) => BlocProvider<AuthBloc>( create: (context) => AuthBloc(FirebaseAuthProvider()), child: const HomePage(), ), ), ); }
基于AuthBloc的优化实现方案
1. 重构启动页逻辑,引入Bloc管理状态
步骤1:定义启动页相关的Event和State
创建启动页专属Bloc,统一管理动画播放、状态流转逻辑:
// splash_bloc.dart part 'splash_event.dart'; part 'splash_state.dart'; class SplashBloc extends Bloc<SplashEvent, SplashState> { final AuthBloc authBloc; late final VideoPlayerController _controller; SplashBloc({required this.authBloc}) : super(SplashInitial()) { _initVideoController(); on<SplashStarted>(_onSplashStarted); on<SplashAnimationCompleted>(_onAnimationCompleted); } void _initVideoController() { // 替换为你的视频资源路径 _controller = VideoPlayerController.asset('assets/splash_anim.mp4') ..initialize().then((_) { add(SplashStarted()); }); } void _onSplashStarted(SplashStarted event, Emitter<SplashState> emit) { _controller.play(); // 两种触发动画完成的方式二选一: // 1. 固定延迟时间 Future.delayed(const Duration(seconds: 4), () => add(SplashAnimationCompleted())); // 2. 监听视频播放结束 // _controller.addListener(() { // if (_controller.value.position >= _controller.value.duration) { // add(SplashAnimationCompleted()); // } // }); } void _onAnimationCompleted(SplashAnimationCompleted event, Emitter<SplashState> emit) { // 动画完成后,触发AuthBloc的身份验证检查 authBloc.add(AuthCheckRequested()); emit(SplashAnimationFinished()); } @override Future<void> close() { _controller.dispose(); return super.close(); } } // splash_event.dart abstract class SplashEvent extends Equatable { const SplashEvent(); @override List<Object> get props => []; } class SplashStarted extends SplashEvent {} class SplashAnimationCompleted extends SplashEvent {} // splash_state.dart abstract class SplashState extends Equatable { const SplashState(); @override List<Object> get props => []; } class SplashInitial extends SplashState {} class SplashAnimationPlaying extends SplashState {} class SplashAnimationFinished extends SplashState {}
步骤2:重构SplashScreen,用Bloc监听状态
// splash_screen.dart class SplashScreen extends StatelessWidget { const SplashScreen({super.key}); @override Widget build(BuildContext context) { return BlocListener<SplashBloc, SplashState>( listener: (context, state) { // 动画完成后的逻辑交由AuthBloc处理,此处无需直接跳转 }, child: Scaffold( body: BlocBuilder<SplashBloc, SplashState>( builder: (context, state) { final controller = context.read<SplashBloc>()._controller; if (controller.value.isInitialized) { return VideoPlayer(controller); } return const Center(child: CircularProgressIndicator()); }, ), ), ); } }
2. 统一由AuthBloc管理身份验证与页面跳转
步骤1:完善AuthBloc的事件与状态
确保AuthBloc能处理身份验证检查,并发出对应状态:
// auth_bloc.dart part 'auth_event.dart'; part 'auth_state.dart'; class AuthBloc extends Bloc<AuthEvent, AuthState> { final FirebaseAuthProvider authProvider; AuthBloc({required this.authProvider}) : super(AuthInitial()) { on<AuthCheckRequested>(_onAuthCheckRequested); // 可添加其他Auth事件处理逻辑(如登录、登出) } void _onAuthCheckRequested(AuthCheckRequested event, Emitter<AuthState> emit) async { final user = await authProvider.getCurrentUser(); if (user != null) { emit(AuthAuthenticated(user: user)); } else { emit(AuthUnauthenticated()); } } } // auth_event.dart abstract class AuthEvent extends Equatable { const AuthEvent(); @override List<Object> get props => []; } class AuthCheckRequested extends AuthEvent {} // auth_state.dart abstract class AuthState extends Equatable { const AuthState(); @override List<Object> get props => []; } class AuthInitial extends AuthState {} class AuthAuthenticated extends AuthState { final User user; const AuthAuthenticated({required this.user}); @override List<Object> get props => [user]; } class AuthUnauthenticated extends AuthState {}
步骤2:在根Widget监听AuthBloc状态,统一处理跳转
修改Main.dart,将AuthBloc提升到根层级,通过BlocListener监听状态变化完成页面跳转:
// Main.dart void main() { WidgetsFlutterBinding.ensureInitialized(); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MultiBlocProvider( providers: [ BlocProvider<AuthBloc>( create: (context) => AuthBloc(FirebaseAuthProvider()), ), BlocProvider<SplashBloc>( create: (context) => SplashBloc(authBloc: context.read<AuthBloc>()), ), ], child: BlocListener<AuthBloc, AuthState>( listener: (context, state) { if (state is AuthAuthenticated) { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (context) => const HomePage()), ); } else if (state is AuthUnauthenticated) { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (context) => const LoginPage()), ); } }, child: MaterialApp( debugShowCheckedModeBanner: false, title: 'Take Note', theme: ThemeData( primarySwatch: Colors.green, ), home: const SplashScreen(), routes: { createOrUpdateNoteRoute: (context) => const CreateUpdateNoteView(), }, ), ), ); } }
3. 优化方案的优势
- 状态统一管理:跳转逻辑由Bloc状态驱动,避免分散的Navigator调用,维护性更强。
- 内存安全:视频控制器在Bloc的
close方法中释放,不会因页面提前销毁导致内存泄漏。 - 职责解耦:启动页仅负责动画播放,身份验证与跳转由AuthBloc统一处理,符合单一职责原则。
- 可调试性:通过Bloc状态变化可全程追踪流程,便于排查问题。
内容的提问来源于stack exchange,提问作者ArvinT
相关产品推荐
相关产品推荐

