页面层级与返回逻辑实现完成,并且去掉web相关代码支持
This commit is contained in:
+91
-162
@@ -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(),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user