Files
QingTing-Player/lib/main.dart
T

284 lines
9.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
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');
}
// ⭐ 创建 HandlerAudioService 稍后初始化)
_audioHandler = AudioPlayerHandler();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AudioService()),
ChangeNotifierProvider(create: (_) => AppLifecycleService()),
Provider<AudioPlayerHandler>(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<QTPlayerApp> createState() => _QTPlayerAppState();
}
class _QTPlayerAppState extends State<QTPlayerApp> {
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<void> _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<void> _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<void> _initWebDAV() async {
try {
final hasCred = await WebDAVService.instance.loadCredentials();
debugPrint('✅ WebDAV 加载完成, 已连接: $hasCred');
} catch (e) {
debugPrint('❌ WebDAV 加载失败: $e');
}
}
Future<void> _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<AudioService>();
final handler = context.read<AudioPlayerHandler>();
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(),
);
},
),
],
);
},
);
}
}