diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml index 304732f..af2b35b 100644 --- a/android/app/src/main/res/drawable/launch_background.xml +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -1,12 +1,12 @@ - - + + - - + - --> - + android:src="@drawable/ic_launcher_foreground" /> + + \ No newline at end of file diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..4046b37 --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #0E1211 + \ No newline at end of file diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index cb1ef88..fd3209e 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -1,18 +1,13 @@ - + - + - + \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 30b1cbe..be86c4c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,8 @@ import 'package:provider/provider.dart'; import 'package:media_kit/media_kit.dart'; import 'services/audio_service.dart'; import 'services/playback_service.dart'; +import 'services/app_lifecycle_service.dart'; +import 'services/webdav_service.dart'; import 'pages/home_page.dart'; import 'pages/player_page.dart'; import 'pages/playlist_page.dart'; @@ -13,34 +15,54 @@ final GlobalKey navigatorKey = GlobalKey(); void main() { WidgetsFlutterBinding.ensureInitialized(); - bool initSuccess = false; - String? initError; - - try { - MediaKit.ensureInitialized(); - PlaybackService().init(); - initSuccess = true; - } catch (e, stack) { - initError = '初始化失败: $e'; - debugPrint('❌ $initError'); - debugPrint(stack.toString()); - } - runApp( MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => AudioService()), + ChangeNotifierProvider(create: (_) => AppLifecycleService()), ], - child: QTPlayerApp( - initSuccess: initSuccess, - initError: initError, - ), + child: const QTPlayerApp(), ), ); + + // ⭐ 首帧渲染完成后才执行初始化 + WidgetsBinding.instance.addPostFrameCallback((_) { + _initializeServices(); + }); +} + +void _initializeServices() async { + final lifecycle = AppLifecycleService(); + + try { + // 1. MediaKit + lifecycle.updateStatus(AppStatus.mediaKitInitializing); + MediaKit.ensureInitialized(); + lifecycle.updateStatus(AppStatus.mediaKitReady); + + // 2. PlaybackService + lifecycle.updateStatus(AppStatus.playerInitializing); + PlaybackService().init(); + lifecycle.updateStatus(AppStatus.playerReady); + + // 3. WebDAV + lifecycle.updateStatus(AppStatus.webdavChecking); + final hasCred = await WebDAVService.instance.loadCredentials(); + if (hasCred) { + lifecycle.updateStatus(AppStatus.webdavReady); + } else { + lifecycle.updateStatus(AppStatus.webdavMissing); + } + + lifecycle.updateStatus(AppStatus.fullReady); + } catch (e) { + debugPrint('❌ 初始化错误: $e'); + lifecycle.updateStatus(AppStatus.error); + } } // ═══════════════════════════════════════════════════ -// RouteManager - 路由状态管理 +// RouteManager // ═══════════════════════════════════════════════════ class RouteManager extends ChangeNotifier { static final RouteManager _instance = RouteManager._internal(); @@ -64,7 +86,7 @@ class RouteManager extends ChangeNotifier { } // ═══════════════════════════════════════════════════ -// 导航观察者 - 监听路由变化 +// 导航观察者 // ═══════════════════════════════════════════════════ class MiniPlayerNavigatorObserver extends NavigatorObserver { void _updateRoute(Route? route) { @@ -79,7 +101,6 @@ class MiniPlayerNavigatorObserver extends NavigatorObserver { @override void didPop(Route route, Route? previousRoute) { - // ⭐ 关键:用 previousRoute 而不是 navigatorKey.currentContext _updateRoute(previousRoute); } @@ -90,47 +111,14 @@ class MiniPlayerNavigatorObserver extends NavigatorObserver { } // ═══════════════════════════════════════════════════ -// 主应用 +// QTPlayerApp - 无状态,只负责组装 // ═══════════════════════════════════════════════════ class QTPlayerApp extends StatelessWidget { - final bool initSuccess; - final String? initError; - - const QTPlayerApp({ - super.key, - required this.initSuccess, - required this.initError, - }); + const QTPlayerApp({super.key}); @override Widget build(BuildContext context) { - if (!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( - initError ?? '未知错误', - style: TextStyle(color: Colors.grey[400], fontSize: 14), - textAlign: TextAlign.center, - ), - ], - ), - ), - ), - ); - } - + debugPrint('🔥 QTPlayerApp.build() 执行,首帧即将渲染'); return MaterialApp( title: '清听', theme: ThemeData( @@ -151,15 +139,11 @@ class QTPlayerApp extends StatelessWidget { '/player': (context) => const PlayerPage(), '/playlist': (context) => const PlaylistPage(), }, - // ⭐ 核心改动:删除手写 Overlay,直接用 Stack builder: (context, child) { return Stack( fit: StackFit.expand, children: [ - // 1. Navigator(所有路由页面) if (child != null) child, - - // 2. MiniPlayer(由路由状态控制显隐) AnimatedBuilder( animation: RouteManager(), builder: (context, _) { diff --git a/lib/services/app_lifecycle_service.dart b/lib/services/app_lifecycle_service.dart new file mode 100644 index 0000000..4ecd333 --- /dev/null +++ b/lib/services/app_lifecycle_service.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +/// App 生命周期状态 +enum AppStatus { + starting, // 启动中 + mediaKitInitializing, // MediaKit 初始化中 + mediaKitReady, // MediaKit 就绪 + playerInitializing, // 播放器初始化中 + playerReady, // 播放器就绪 + webdavChecking, // WebDAV 检测中 + webdavReady, // WebDAV 就绪 + webdavMissing, // WebDAV 未配置 + fullReady, // 完全就绪 + error, // 错误 +} + +class AppLifecycleService extends ChangeNotifier { + AppStatus _status = AppStatus.starting; + + AppStatus get status => _status; + + bool get isPlayerReady => + _status == AppStatus.playerReady || _status == AppStatus.fullReady; + + bool get isWebDAVReady => + _status == AppStatus.webdavReady || _status == AppStatus.fullReady; + + bool get isFullReady => _status == AppStatus.fullReady; + + void updateStatus(AppStatus newStatus) { + if (_status != newStatus) { + _status = newStatus; + debugPrint('🔄 App 状态更新: $newStatus'); + notifyListeners(); + } + } + + // 重置(用于错误恢复) + void reset() { + _status = AppStatus.starting; + notifyListeners(); + } +}