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

如何结合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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 20:31:23