// lib/main.dart import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:media_kit/media_kit.dart'; import 'services/audio_service.dart'; import 'services/playback_service.dart'; import 'pages/home_page.dart'; import 'pages/player_page.dart'; import 'pages/playlist_page.dart'; import 'widgets/global_mini_player.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); bool initSuccess = false; String? initError; try { MediaKit.ensureInitialized(); PlaybackService().init(); initSuccess = true; } catch (e, stack) { initError = '初始化失败: $e'; print('❌ $initError'); print(stack); } runApp( ChangeNotifierProvider( create: (_) => AudioService(), child: QTPlayerApp( initSuccess: initSuccess, initError: initError, ), ), ); } class QTPlayerApp extends StatefulWidget { final bool initSuccess; final String? initError; const QTPlayerApp({ super.key, required this.initSuccess, required this.initError, }); @override State createState() => _QTPlayerAppState(); } class _QTPlayerAppState extends State with SingleTickerProviderStateMixin { bool _showPlayerPage = false; bool _showPlaylist = false; late final AnimationController _animationController; late final Animation _slideAnimation; @override void initState() { super.initState(); GlobalMiniPlayer.onOpenPlayerPage = _openPlayerPage; GlobalMiniPlayer.onOpenPlaylistPage = _openPlaylist; _animationController = AnimationController( vsync: this, duration: const Duration(milliseconds: 300), ); _slideAnimation = Tween(begin: 1.0, end: 0.0).animate( CurvedAnimation( parent: _animationController, curve: Curves.easeOutCubic, ), ); } @override void dispose() { _animationController.dispose(); super.dispose(); } void _openPlayerPage() { setState(() { _showPlayerPage = true; _showPlaylist = false; }); _animationController.forward(from: 0.0); } void _closePlayerPage() { _animationController.reverse().then((_) { if (mounted) { setState(() { _showPlayerPage = false; }); } }); } void _openPlaylist() { setState(() { _showPlaylist = true; }); } void _closePlaylist() { setState(() { _showPlaylist = false; }); } // ============================================================ // ✅ PopScope 的 onPopInvokedWithResult // ============================================================ void _onPopInvokedWithResult(bool didPop, Object? result) { if (didPop) return; // 1️⃣ 优先关闭播放列表(最高层级) if (_showPlaylist) { _closePlaylist(); return; } // 2️⃣ 其次关闭播放页 if (_showPlayerPage) { _closePlayerPage(); return; } // 3️⃣ 没有覆盖层,允许退出(由 Navigator 处理) // 注意:这里不能直接退出,需要让 Navigator 处理 // 但 canPop 会根据状态返回 true/false } @override Widget build(BuildContext context) { if (!widget.initSuccess) { return MaterialApp( home: Scaffold( backgroundColor: const Color(0xFF0E1211), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error_outline, size: 64, color: Colors.red[300]), const SizedBox(height: 16), const Text( '应用初始化失败', style: TextStyle(color: Colors.white, fontSize: 20), ), const SizedBox(height: 8), Text( widget.initError ?? '未知错误', style: TextStyle(color: Colors.grey[400], fontSize: 14), textAlign: TextAlign.center, ), ], ), ), ), ); } return MaterialApp( title: '清听', theme: ThemeData( brightness: Brightness.dark, primaryColor: const Color(0xFF7C9A9E), colorScheme: const ColorScheme.dark( primary: Color(0xFF7C9A9E), secondary: Color(0xFFB8D4D0), surface: Color(0xFF1A1F1E), onSurface: Colors.white, ), useMaterial3: true, ), home: const HomePage(), builder: (context, child) { // ✅ 暴力测试:canPop 写死为 false return PopScope( canPop: false, onPopInvokedWithResult: (didPop, result) { debugPrint('🔥🔥🔥 BACK FIRED: didPop=$didPop'); debugPrint('🔥 playlist=$_showPlaylist, player=$_showPlayerPage'); if (_showPlaylist) { debugPrint('🔥 closing playlist'); _closePlaylist(); } else if (_showPlayerPage) { debugPrint('🔥 closing player'); _closePlayerPage(); } else { debugPrint('🔥 no overlay, allowing exit'); // 注意:这里 canPop=false,需要手动退出 // 用 SystemNavigator.pop() 或 Navigator.pop(context) } }, child: Overlay( initialEntries: [ OverlayEntry( builder: (context) => Stack( children: [ Positioned.fill(child: child!), const Positioned( left: 0, right: 0, bottom: 0, child: GlobalMiniPlayer(), ), if (_showPlayerPage) Positioned.fill( child: AnimatedBuilder( animation: _animationController, builder: (context, child) { final progress = _slideAnimation.value; final offsetY = MediaQuery.of(context).size.height * progress; return Transform.translate( offset: Offset(0, offsetY), child: Opacity( opacity: 1.0 - progress * 0.3, child: child, ), ); }, child: PlayerPage( onClose: _closePlayerPage, onOpenPlaylist: _openPlaylist, ), ), ), if (_showPlaylist) Positioned.fill( child: PlaylistPage( onClose: _closePlaylist, ), ), ], ), ), ], ), ); }, ); } }