import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:video_player/video_player.dart'; import '../../../../routes/route_paths.dart'; import '../../../../routes/app_router.dart'; import '../../../../bootstrap.dart'; import '../providers/auth_provider.dart'; /// 开屏页面 - 应用启动时显示的第一个页面 /// 播放开屏视频,视频结束后检查用户认证状态并跳转 class SplashPage extends ConsumerStatefulWidget { const SplashPage({super.key}); @override ConsumerState createState() => _SplashPageState(); } class _SplashPageState extends ConsumerState with SingleTickerProviderStateMixin { /// 视频播放控制器 VideoPlayerController? _videoController; /// 视频是否已初始化 bool _isVideoInitialized = false; /// 是否显示跳过按钮 bool _showSkipButton = false; /// 是否已经开始跳转(防止重复跳转) bool _isNavigating = false; /// 视频加载是否失败(使用 fallback 动画) bool _videoFailed = false; /// Fallback 动画控制器 AnimationController? _fallbackAnimationController; /// Logo 缩放动画 Animation? _logoScaleAnimation; /// Logo 透明度动画 Animation? _logoOpacityAnimation; /// 文字透明度动画 Animation? _textOpacityAnimation; @override void initState() { super.initState(); _initializeVideo(); } @override void dispose() { _videoController?.removeListener(_onVideoStateChanged); _videoController?.dispose(); _fallbackAnimationController?.dispose(); super.dispose(); } /// 初始化视频播放器 Future _initializeVideo() async { debugPrint('[SplashPage] ========== 开始初始化视频 =========='); debugPrint('[SplashPage] 视频路径: assets/videos/splash.mp4'); try { // 从 assets 加载视频 debugPrint('[SplashPage] 创建 VideoPlayerController...'); _videoController = VideoPlayerController.asset('assets/videos/splash.mp4'); debugPrint('[SplashPage] 开始 initialize()...'); final stopwatch = Stopwatch()..start(); await _videoController!.initialize(); stopwatch.stop(); debugPrint('[SplashPage] initialize() 完成,耗时: ${stopwatch.elapsedMilliseconds}ms'); // 打印视频信息 final value = _videoController!.value; debugPrint('[SplashPage] 视频信息:'); debugPrint('[SplashPage] - 尺寸: ${value.size.width} x ${value.size.height}'); debugPrint('[SplashPage] - 时长: ${value.duration.inMilliseconds}ms'); debugPrint('[SplashPage] - 是否已初始化: ${value.isInitialized}'); debugPrint('[SplashPage] - 是否有错误: ${value.hasError}'); if (value.hasError) { debugPrint('[SplashPage] - 错误信息: ${value.errorDescription}'); } if (mounted) { setState(() { _isVideoInitialized = true; }); // 设置视频播放完成监听 _videoController!.addListener(_onVideoStateChanged); // 开始播放视频 debugPrint('[SplashPage] 开始播放视频...'); await _videoController!.play(); debugPrint('[SplashPage] play() 调用完成,isPlaying: ${_videoController!.value.isPlaying}'); // 1秒后显示跳过按钮 Future.delayed(const Duration(seconds: 1), () { if (mounted) { setState(() { _showSkipButton = true; }); } }); } } catch (e, stackTrace) { debugPrint('[SplashPage] ========== 视频初始化失败 =========='); debugPrint('[SplashPage] 错误类型: ${e.runtimeType}'); debugPrint('[SplashPage] 错误信息: $e'); debugPrint('[SplashPage] 堆栈跟踪:\n$stackTrace'); // 检查 controller 状态 if (_videoController != null) { final value = _videoController!.value; debugPrint('[SplashPage] Controller 状态:'); debugPrint('[SplashPage] - 是否已初始化: ${value.isInitialized}'); debugPrint('[SplashPage] - 是否有错误: ${value.hasError}'); if (value.hasError) { debugPrint('[SplashPage] - 错误描述: ${value.errorDescription}'); } } // 如果视频加载失败,启动 fallback 动画 _startFallbackAnimation(); } } /// 启动 fallback 动画(视频加载失败时使用) void _startFallbackAnimation() { debugPrint('[SplashPage] 启动 fallback 动画'); setState(() { _videoFailed = true; }); // 创建动画控制器,总时长 3 秒 _fallbackAnimationController = AnimationController( vsync: this, duration: const Duration(milliseconds: 3000), ); // Logo 缩放动画:0.5 -> 1.0 -> 1.05 -> 1.0(弹性效果) _logoScaleAnimation = TweenSequence([ TweenSequenceItem( tween: Tween(begin: 0.5, end: 1.05) .chain(CurveTween(curve: Curves.easeOutBack)), weight: 40, ), TweenSequenceItem( tween: Tween(begin: 1.05, end: 1.0) .chain(CurveTween(curve: Curves.easeInOut)), weight: 10, ), TweenSequenceItem( tween: ConstantTween(1.0), weight: 50, ), ]).animate(_fallbackAnimationController!); // Logo 透明度动画:0 -> 1 _logoOpacityAnimation = TweenSequence([ TweenSequenceItem( tween: Tween(begin: 0.0, end: 1.0) .chain(CurveTween(curve: Curves.easeOut)), weight: 30, ), TweenSequenceItem( tween: ConstantTween(1.0), weight: 70, ), ]).animate(_fallbackAnimationController!); // 文字透明度动画:延迟出现 _textOpacityAnimation = TweenSequence([ TweenSequenceItem( tween: ConstantTween(0.0), weight: 30, ), TweenSequenceItem( tween: Tween(begin: 0.0, end: 1.0) .chain(CurveTween(curve: Curves.easeOut)), weight: 20, ), TweenSequenceItem( tween: ConstantTween(1.0), weight: 50, ), ]).animate(_fallbackAnimationController!); // 动画完成后跳转 _fallbackAnimationController!.addStatusListener((status) { if (status == AnimationStatus.completed) { _navigateToNextPage(); } }); // 开始动画 _fallbackAnimationController!.forward(); // 1秒后显示跳过按钮 Future.delayed(const Duration(seconds: 1), () { if (mounted) { setState(() { _showSkipButton = true; }); } }); } /// 跳过 fallback 动画 void _skipFallbackAnimation() { _fallbackAnimationController?.stop(); _navigateToNextPage(); } /// 视频状态变化监听 void _onVideoStateChanged() { if (_videoController == null) return; // 视频播放完成 if (_videoController!.value.position >= _videoController!.value.duration && _videoController!.value.duration > Duration.zero) { _navigateToNextPage(); } } /// 跳过视频 void _skipVideo() { _videoController?.pause(); _navigateToNextPage(); } /// 导航到下一个页面 Future _navigateToNextPage() async { // 防止重复跳转 if (_isNavigating) return; _isNavigating = true; // 初始化遥测服务(需要 BuildContext) await initializeTelemetry(context); // 检查认证状态 await ref.read(authProvider.notifier).checkAuthStatus(); if (!mounted) return; final authState = ref.read(authProvider); // 根据认证状态决定跳转目标 // 优先级: // 1. 钱包已创建且已备份 → 主页面 // 2. 账号已创建但钱包未完成 → 备份助记词页面 // 3. 首次打开或未看过向导 → 向导页 // 4. 其他情况 → 创建账户页面 if (authState.isWalletCreated) { // 已创建钱包且已备份,进入主页面(龙虎榜) debugPrint('[SplashPage] 钱包已创建且已备份 → 跳转到龙虎榜'); context.go(RoutePaths.ranking); } else if (authState.isAccountCreated && authState.userSerialNum != null) { // 账号已创建但钱包未完成(可能正在生成或未备份),直接进入备份助记词页面 debugPrint('[SplashPage] 账号已创建但钱包未完成 → 跳转到备份助记词页面'); debugPrint('[SplashPage] userSerialNum: ${authState.userSerialNum}, isWalletReady: ${authState.isWalletReady}'); context.go( RoutePaths.backupMnemonic, extra: BackupMnemonicParams( userSerialNum: authState.userSerialNum!, referralCode: authState.referralCode, ), ); } else if (authState.isFirstLaunch || !authState.hasSeenGuide) { // 首次打开或未看过向导,进入向导页 debugPrint('[SplashPage] 首次打开或未看过向导 → 跳转到向导页'); context.go(RoutePaths.guide); } else { // 已看过向导但未创建钱包,直接进入创建账户页面 debugPrint('[SplashPage] 已看过向导但未创建钱包 → 跳转到创建账户页面'); context.go(RoutePaths.onboarding); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.black, body: Stack( fit: StackFit.expand, children: [ // 视频播放器(全屏覆盖) if (_isVideoInitialized && _videoController != null) SizedBox.expand( child: FittedBox( fit: BoxFit.cover, child: SizedBox( width: _videoController!.value.size.width, height: _videoController!.value.size.height, child: VideoPlayer(_videoController!), ), ), ) else if (_videoFailed) // 视频加载失败,显示 fallback 动画 _buildFallbackAnimationView() else // 视频加载中显示 Logo _buildLoadingView(), // 跳过按钮 if (_showSkipButton) Positioned( top: MediaQuery.of(context).padding.top + 16, right: 16, child: _buildSkipButton(), ), ], ), ); } /// 构建加载中视图(视频未加载完成时显示) Widget _buildLoadingView() { return Container( width: double.infinity, height: double.infinity, decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Color(0xFFFFF5E6), Color(0xFFFFE4B5), ], ), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Logo Image.asset( 'assets/images/logo/app_icon.png', width: 128, height: 128, ), const SizedBox(height: 24), const Text( '榴莲皇后', style: TextStyle( fontSize: 32, fontFamily: 'Noto Sans SC', fontWeight: FontWeight.w700, height: 1.25, letterSpacing: 1.6, color: Color(0xFF4A3F35), ), ), const SizedBox(height: 24), // 加载指示器 const SizedBox( width: 24, height: 24, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Color(0xFFD4AF37)), ), ), ], ), ); } /// 构建 fallback 动画视图(视频加载失败时使用) Widget _buildFallbackAnimationView() { return AnimatedBuilder( animation: _fallbackAnimationController!, builder: (context, child) { return Container( width: double.infinity, height: double.infinity, decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Color(0xFFFFF5E6), Color(0xFFFFE4B5), ], ), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Logo 带缩放和透明度动画 Opacity( opacity: _logoOpacityAnimation?.value ?? 1.0, child: Transform.scale( scale: _logoScaleAnimation?.value ?? 1.0, child: Image.asset( 'assets/images/logo/app_icon.png', width: 128, height: 128, ), ), ), const SizedBox(height: 24), // 文字带透明度动画 Opacity( opacity: _textOpacityAnimation?.value ?? 1.0, child: const Text( '榴莲皇后', style: TextStyle( fontSize: 32, fontFamily: 'Noto Sans SC', fontWeight: FontWeight.w700, height: 1.25, letterSpacing: 1.6, color: Color(0xFF4A3F35), ), ), ), const SizedBox(height: 16), // 副标题(延迟显示) Opacity( opacity: _textOpacityAnimation?.value ?? 1.0, child: const Text( '认种榴莲 · 共享收益', style: TextStyle( fontSize: 16, fontFamily: 'Noto Sans SC', fontWeight: FontWeight.w400, color: Color(0xFF8B7355), ), ), ), ], ), ); }, ); } /// 构建跳过按钮 Widget _buildSkipButton() { return GestureDetector( onTap: _videoFailed ? _skipFallbackAnimation : _skipVideo, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.black.withValues(alpha: 0.5), borderRadius: BorderRadius.circular(20), border: Border.all( color: Colors.white.withValues(alpha: 0.3), width: 1, ), ), child: const Row( mainAxisSize: MainAxisSize.min, children: [ Text( '跳过', style: TextStyle( fontSize: 14, fontFamily: 'Inter', fontWeight: FontWeight.w500, color: Colors.white, ), ), SizedBox(width: 4), Icon( Icons.skip_next, size: 18, color: Colors.white, ), ], ), ), ); } }