import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:media_kit/media_kit.dart'; //import 'package:audio_service/audio_service.dart' as audio_service; import 'package:permission_handler/permission_handler.dart'; import 'services/audio_service.dart'; import 'services/playback_service.dart'; import 'services/app_lifecycle_service.dart'; import 'services/webdav_service.dart'; import 'services/audio_player_handler.dart'; import 'pages/home_page.dart'; import 'pages/player_page.dart'; import 'pages/playlist_page.dart'; import 'widgets/global_mini_player.dart'; final GlobalKey navigatorKey = GlobalKey(); final stopwatch = Stopwatch(); // ⭐ 防止 Hot Restart / 热重载时重复初始化 bool _appInitialized = false; // ⭐ 全局缓存 Handler AudioPlayerHandler? _audioHandler; void main() async { // ⭐ P0:防止重复初始化 if (_appInitialized) { debugPrint('⏱️ main 已初始化,跳过重复执行'); return; } _appInitialized = true; stopwatch.start(); debugPrint('⏱️ T0 main: ${stopwatch.elapsedMilliseconds}ms'); WidgetsFlutterBinding.ensureInitialized(); debugPrint('⏱️ T0.5 ensureInitialized: ${stopwatch.elapsedMilliseconds}ms'); // ⭐ MediaKit 在 runApp 前初始化(官方要求) try { MediaKit.ensureInitialized(); debugPrint('⏱️ T0.6 MediaKit 初始化完成'); } catch (e) { debugPrint('❌ MediaKit 初始化失败: $e'); } // ⭐ 创建 Handler(AudioService 稍后初始化) _audioHandler = AudioPlayerHandler(); runApp( MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => AudioService()), ChangeNotifierProvider(create: (_) => AppLifecycleService()), Provider(create: (_) => _audioHandler!), ], child: const QTPlayerApp(), ), ); debugPrint('⏱️ T0.8 runApp 完成: ${stopwatch.elapsedMilliseconds}ms'); } // ════════════════════════════════════════════════════════════ // 路由管理器(控制 MiniPlayer 显示/隐藏) // ════════════════════════════════════════════════════════════ class RouteManager extends ChangeNotifier { static final RouteManager _instance = RouteManager._internal(); factory RouteManager() => _instance; RouteManager._internal(); String _currentRoute = '/'; String get currentRoute => _currentRoute; bool get showMiniPlayer => _currentRoute != '/player' && _currentRoute != '/playlist'; void updateRoute(String route) { if (_currentRoute != route) { _currentRoute = route; debugPrint('📌 路由更新: $route'); notifyListeners(); } } } // ════════════════════════════════════════════════════════════ // 导航观察者(监听路由变化) // ════════════════════════════════════════════════════════════ class MiniPlayerNavigatorObserver extends NavigatorObserver { void _updateRoute(Route? route) { final name = route?.settings.name ?? '/'; RouteManager().updateRoute(name); } @override void didPush(Route route, Route? previousRoute) { _updateRoute(route); } @override void didPop(Route route, Route? previousRoute) { _updateRoute(previousRoute); } @override void didReplace({Route? newRoute, Route? oldRoute}) { _updateRoute(newRoute); } } // ════════════════════════════════════════════════════════════ // 主应用 // ════════════════════════════════════════════════════════════ class QTPlayerApp extends StatefulWidget { const QTPlayerApp({super.key}); @override State createState() => _QTPlayerAppState(); } class _QTPlayerAppState extends State { bool _isInitializing = false; // ============================================================ // 生命周期 // ============================================================ @override void initState() { super.initState(); debugPrint('⏱️ T1.5 initState: ${stopwatch.elapsedMilliseconds}ms'); WidgetsBinding.instance.addPostFrameCallback((_) { debugPrint('⏱️ T2 首帧回调: ${stopwatch.elapsedMilliseconds}ms'); _startBackgroundInitialization(); }); } @override void dispose() { super.dispose(); } // ============================================================ // 后台初始化(不阻塞首帧) // ============================================================ Future _startBackgroundInitialization() async { if (_isInitializing) return; _isInitializing = true; debugPrint('⏱️ T3 后台初始化开始'); // 1. PlaybackService(轻量级) try { PlaybackService().init(); debugPrint('⏱️ T3.1 PlaybackService 初始化完成'); } catch (e) { debugPrint('❌ PlaybackService 初始化失败: $e'); } // 2. ⭐ AudioService 暂时禁用(等 MainActivity 改为 AudioServiceActivity 后启用) // await _initAudioService(); // 3. WebDAV await _initWebDAV(); // 4. 通知权限 await _requestPermissions(); debugPrint('⏱️ T4 全部初始化完成'); } // ════════════════════════════════════════════════════════════ // 子初始化方法 // ════════════════════════════════════════════════════════════ // ⭐ 暂时禁用,等 MainActivity 改为 AudioServiceActivity 后启用 // Future _initAudioService() async { // try { // await audio_service.AudioService.init( // builder: () => _audioHandler!, // config: const audio_service.AudioServiceConfig( // androidNotificationChannelId: 'com.example.qt_player.music', // androidNotificationChannelName: '清听音乐播放', // androidNotificationOngoing: true, // androidStopForegroundOnPause: true, // ), // ); // debugPrint('✅ AudioService 初始化完成'); // } catch (e) { // debugPrint('❌ AudioService 初始化失败: $e'); // } // } Future _initWebDAV() async { try { final hasCred = await WebDAVService.instance.loadCredentials(); debugPrint('✅ WebDAV 加载完成, 已连接: $hasCred'); } catch (e) { debugPrint('❌ WebDAV 加载失败: $e'); } } Future _requestPermissions() async { try { if (await Permission.notification.isDenied) { final status = await Permission.notification.request(); debugPrint('📢 通知权限状态: $status'); } } catch (e) { debugPrint('❌ 权限申请失败: $e'); } } // ============================================================ // Build // ============================================================ @override Widget build(BuildContext context) { debugPrint('⏱️ T1 build: ${stopwatch.elapsedMilliseconds}ms'); // 监听歌曲变化,更新通知栏 final audioService = context.watch(); final handler = context.read(); final song = audioService.currentSong; WidgetsBinding.instance.addPostFrameCallback((_) { if (song != null) { handler.updateNotification( id: song.id, title: song.title, artist: song.artist, ); } else { handler.mediaItem.add(null); } }); 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, ), navigatorKey: navigatorKey, navigatorObservers: [MiniPlayerNavigatorObserver()], routes: { '/': (context) => const HomePage(), '/player': (context) => const PlayerPage(), '/playlist': (context) => const PlaylistPage(), }, builder: (context, child) { return Stack( fit: StackFit.expand, children: [ if (child != null) child, AnimatedBuilder( animation: RouteManager(), builder: (context, _) { if (!RouteManager().showMiniPlayer) { return const SizedBox.shrink(); } return const Align( alignment: Alignment.bottomCenter, child: GlobalMiniPlayer(), ); }, ), ], ); }, ); } }