页面层级与返回逻辑实现完成,并且去掉web相关代码支持

This commit is contained in:
2026-08-19 23:27:47 +08:00
parent d116814bb6
commit 81990385f4
13 changed files with 242 additions and 526 deletions
+91 -162
View File
@@ -1,4 +1,3 @@
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:media_kit/media_kit.dart';
@@ -9,6 +8,8 @@ import 'pages/player_page.dart';
import 'pages/playlist_page.dart';
import 'widgets/global_mini_player.dart';
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
void main() {
WidgetsFlutterBinding.ensureInitialized();
@@ -21,13 +22,15 @@ void main() {
initSuccess = true;
} catch (e, stack) {
initError = '初始化失败: $e';
print('$initError');
print(stack);
debugPrint('$initError');
debugPrint(stack.toString());
}
runApp(
ChangeNotifierProvider(
create: (_) => AudioService(),
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AudioService()),
],
child: QTPlayerApp(
initSuccess: initSuccess,
initError: initError,
@@ -36,7 +39,60 @@ void main() {
);
}
class QTPlayerApp extends StatefulWidget {
// ═══════════════════════════════════════════════════
// RouteManager - 路由状态管理
// ═══════════════════════════════════════════════════
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) {
// ⭐ 关键:用 previousRoute 而不是 navigatorKey.currentContext
_updateRoute(previousRoute);
}
@override
void didReplace({Route? newRoute, Route? oldRoute}) {
_updateRoute(newRoute);
}
}
// ═══════════════════════════════════════════════════
// 主应用
// ═══════════════════════════════════════════════════
class QTPlayerApp extends StatelessWidget {
final bool initSuccess;
final String? initError;
@@ -46,99 +102,9 @@ class QTPlayerApp extends StatefulWidget {
required this.initError,
});
@override
State<QTPlayerApp> createState() => _QTPlayerAppState();
}
class _QTPlayerAppState extends State<QTPlayerApp>
with SingleTickerProviderStateMixin {
bool _showPlayerPage = false;
bool _showPlaylist = false;
late final AnimationController _animationController;
late final Animation<double> _slideAnimation;
@override
void initState() {
super.initState();
GlobalMiniPlayer.onOpenPlayerPage = _openPlayerPage;
GlobalMiniPlayer.onOpenPlaylistPage = _openPlaylist;
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
_slideAnimation = Tween<double>(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) {
if (!initSuccess) {
return MaterialApp(
home: Scaffold(
backgroundColor: const Color(0xFF0E1211),
@@ -154,7 +120,7 @@ class _QTPlayerAppState extends State<QTPlayerApp>
),
const SizedBox(height: 8),
Text(
widget.initError ?? '未知错误',
initError ?? '未知错误',
style: TextStyle(color: Colors.grey[400], fontSize: 14),
textAlign: TextAlign.center,
),
@@ -178,72 +144,35 @@ class _QTPlayerAppState extends State<QTPlayerApp>
),
useMaterial3: true,
),
home: const HomePage(),
navigatorKey: navigatorKey,
navigatorObservers: [MiniPlayerNavigatorObserver()],
routes: {
'/': (context) => const HomePage(),
'/player': (context) => const PlayerPage(),
'/playlist': (context) => const PlaylistPage(),
},
// ⭐ 核心改动:删除手写 Overlay,直接用 Stack
builder: (context, child) {
// ✅ 暴力测试:canPop 写死为 false
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) {
debugPrint('🔥🔥🔥 BACK FIRED: didPop=$didPop');
debugPrint('🔥 playlist=$_showPlaylist, player=$_showPlayerPage');
return Stack(
fit: StackFit.expand,
children: [
// 1. Navigator(所有路由页面)
if (child != null) child,
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,
),
),
],
),
),
],
),
// 2. MiniPlayer(由路由状态控制显隐)
AnimatedBuilder(
animation: RouteManager(),
builder: (context, _) {
if (!RouteManager().showMiniPlayer) {
return const SizedBox.shrink();
}
return const Align(
alignment: Alignment.bottomCenter,
child: GlobalMiniPlayer(),
);
},
),
],
);
},
);