界面优先级存在部分问题,先锁定到这个版本,进行后续优化
This commit is contained in:
+90
-76
@@ -6,11 +6,9 @@ import 'services/audio_service.dart';
|
|||||||
import 'services/playback_service.dart';
|
import 'services/playback_service.dart';
|
||||||
import 'pages/home_page.dart';
|
import 'pages/home_page.dart';
|
||||||
import 'pages/player_page.dart';
|
import 'pages/player_page.dart';
|
||||||
import 'pages/playlist_page.dart'; // ✅ 导入 PlaylistPage
|
import 'pages/playlist_page.dart';
|
||||||
import 'widgets/global_mini_player.dart';
|
import 'widgets/global_mini_player.dart';
|
||||||
|
|
||||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
@@ -55,7 +53,7 @@ class QTPlayerApp extends StatefulWidget {
|
|||||||
class _QTPlayerAppState extends State<QTPlayerApp>
|
class _QTPlayerAppState extends State<QTPlayerApp>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
bool _showPlayerPage = false;
|
bool _showPlayerPage = false;
|
||||||
bool _showPlaylist = false; // ✅ 新增:播放列表显示状态
|
bool _showPlaylist = false;
|
||||||
|
|
||||||
late final AnimationController _animationController;
|
late final AnimationController _animationController;
|
||||||
late final Animation<double> _slideAnimation;
|
late final Animation<double> _slideAnimation;
|
||||||
@@ -88,33 +86,56 @@ class _QTPlayerAppState extends State<QTPlayerApp>
|
|||||||
void _openPlayerPage() {
|
void _openPlayerPage() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_showPlayerPage = true;
|
_showPlayerPage = true;
|
||||||
_showPlaylist = false; // 打开播放页时关闭播放列表
|
_showPlaylist = false;
|
||||||
});
|
});
|
||||||
_animationController.forward(from: 0.0);
|
_animationController.forward(from: 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _closePlayerPage() {
|
void _closePlayerPage() {
|
||||||
_animationController.reverse().then((_) {
|
_animationController.reverse().then((_) {
|
||||||
setState(() {
|
if (mounted) {
|
||||||
_showPlayerPage = false;
|
setState(() {
|
||||||
});
|
_showPlayerPage = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 打开播放列表
|
|
||||||
void _openPlaylist() {
|
void _openPlaylist() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_showPlaylist = true;
|
_showPlaylist = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 关闭播放列表
|
|
||||||
void _closePlaylist() {
|
void _closePlaylist() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_showPlaylist = false;
|
_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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (!widget.initSuccess) {
|
if (!widget.initSuccess) {
|
||||||
@@ -137,25 +158,6 @@ class _QTPlayerAppState extends State<QTPlayerApp>
|
|||||||
style: TextStyle(color: Colors.grey[400], fontSize: 14),
|
style: TextStyle(color: Colors.grey[400], fontSize: 14),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
runApp(
|
|
||||||
ChangeNotifierProvider(
|
|
||||||
create: (_) => AudioService(),
|
|
||||||
child: QTPlayerApp(
|
|
||||||
initSuccess: true,
|
|
||||||
initError: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: const Color(0xFFB8D4D0),
|
|
||||||
foregroundColor: Colors.black87,
|
|
||||||
),
|
|
||||||
child: const Text('重试'),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -165,7 +167,6 @@ class _QTPlayerAppState extends State<QTPlayerApp>
|
|||||||
|
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: '清听',
|
title: '清听',
|
||||||
navigatorKey: navigatorKey,
|
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
primaryColor: const Color(0xFF7C9A9E),
|
primaryColor: const Color(0xFF7C9A9E),
|
||||||
@@ -179,57 +180,70 @@ class _QTPlayerAppState extends State<QTPlayerApp>
|
|||||||
),
|
),
|
||||||
home: const HomePage(),
|
home: const HomePage(),
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
return Overlay(
|
// ✅ 暴力测试:canPop 写死为 false
|
||||||
initialEntries: [
|
return PopScope(
|
||||||
OverlayEntry(
|
canPop: false,
|
||||||
builder: (context) => Stack(
|
onPopInvokedWithResult: (didPop, result) {
|
||||||
children: [
|
debugPrint('🔥🔥🔥 BACK FIRED: didPop=$didPop');
|
||||||
// ① 主页面
|
debugPrint('🔥 playlist=$_showPlaylist, player=$_showPlayerPage');
|
||||||
Positioned.fill(child: child!),
|
|
||||||
|
|
||||||
// ② MiniPlayer
|
if (_showPlaylist) {
|
||||||
const Positioned(
|
debugPrint('🔥 closing playlist');
|
||||||
left: 0,
|
_closePlaylist();
|
||||||
right: 0,
|
} else if (_showPlayerPage) {
|
||||||
bottom: 0,
|
debugPrint('🔥 closing player');
|
||||||
child: GlobalMiniPlayer(),
|
_closePlayerPage();
|
||||||
),
|
} else {
|
||||||
|
debugPrint('🔥 no overlay, allowing exit');
|
||||||
// ③ 播放页(全屏覆盖)
|
// 注意:这里 canPop=false,需要手动退出
|
||||||
if (_showPlayerPage)
|
// 用 SystemNavigator.pop() 或 Navigator.pop(context)
|
||||||
Positioned.fill(
|
}
|
||||||
child: AnimatedBuilder(
|
},
|
||||||
animation: _animationController,
|
child: Overlay(
|
||||||
builder: (context, child) {
|
initialEntries: [
|
||||||
final progress = _slideAnimation.value;
|
OverlayEntry(
|
||||||
final offsetY =
|
builder: (context) => Stack(
|
||||||
MediaQuery.of(context).size.height * progress;
|
children: [
|
||||||
return Transform.translate(
|
Positioned.fill(child: child!),
|
||||||
offset: Offset(0, offsetY),
|
const Positioned(
|
||||||
child: Opacity(
|
left: 0,
|
||||||
opacity: 1.0 - progress * 0.3,
|
right: 0,
|
||||||
child: child,
|
bottom: 0,
|
||||||
),
|
child: GlobalMiniPlayer(),
|
||||||
);
|
),
|
||||||
},
|
if (_showPlayerPage)
|
||||||
child: PlayerPage(
|
Positioned.fill(
|
||||||
onClose: _closePlayerPage,
|
child: AnimatedBuilder(
|
||||||
onOpenPlaylist: _openPlaylist, // ✅ 传入回调
|
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(
|
||||||
if (_showPlaylist)
|
onClose: _closePlaylist,
|
||||||
Positioned.fill(
|
),
|
||||||
child: PlaylistPage(
|
|
||||||
onClose: _closePlaylist,
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// lib/pages/shell_page.dart
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import '../services/audio_service.dart';
|
||||||
|
import '../widgets/global_mini_player.dart';
|
||||||
|
import 'home_page.dart';
|
||||||
|
import 'player_page.dart';
|
||||||
|
import 'playlist_page.dart';
|
||||||
|
|
||||||
|
class ShellPage extends StatefulWidget {
|
||||||
|
const ShellPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ShellPage> createState() => _ShellPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ShellPageState extends State<ShellPage> {
|
||||||
|
// ✅ 从 PlayerPage 打开 PlaylistPage
|
||||||
|
void _openPlaylistFromPlayer() {
|
||||||
|
Navigator.pushNamed(context, '/playlist');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 关闭 PlayerPage
|
||||||
|
void _closePlayer() {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 关闭 PlaylistPage
|
||||||
|
void _closePlaylist() {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: const Color(0xFF0E1211),
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
// ---- 页面内容(Navigator) ----
|
||||||
|
Navigator(
|
||||||
|
initialRoute: '/',
|
||||||
|
onGenerateRoute: (settings) {
|
||||||
|
switch (settings.name) {
|
||||||
|
case '/':
|
||||||
|
return _buildPageRoute(
|
||||||
|
const HomePage(),
|
||||||
|
settings: settings,
|
||||||
|
);
|
||||||
|
case '/player':
|
||||||
|
return _buildPageRoute(
|
||||||
|
PlayerPage(
|
||||||
|
onClose: _closePlayer,
|
||||||
|
onOpenPlaylist: _openPlaylistFromPlayer,
|
||||||
|
),
|
||||||
|
settings: settings,
|
||||||
|
fromBottom: true,
|
||||||
|
);
|
||||||
|
case '/playlist':
|
||||||
|
return _buildPageRoute(
|
||||||
|
PlaylistPage(
|
||||||
|
onClose: _closePlaylist,
|
||||||
|
),
|
||||||
|
settings: settings,
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
// ---- MiniPlayer(覆盖在最上层) ----
|
||||||
|
const Positioned(
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
child: GlobalMiniPlayer(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PageRouteBuilder _buildPageRoute(
|
||||||
|
Widget page, {
|
||||||
|
required RouteSettings settings,
|
||||||
|
bool fromBottom = false,
|
||||||
|
}) {
|
||||||
|
return PageRouteBuilder(
|
||||||
|
settings: settings,
|
||||||
|
pageBuilder: (context, animation, secondaryAnimation) => page,
|
||||||
|
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
||||||
|
if (fromBottom) {
|
||||||
|
// 播放页:从底部滑入
|
||||||
|
final offset = Tween<Offset>(
|
||||||
|
begin: const Offset(0, 1),
|
||||||
|
end: Offset.zero,
|
||||||
|
).animate(CurvedAnimation(
|
||||||
|
parent: animation,
|
||||||
|
curve: Curves.easeOutCubic,
|
||||||
|
));
|
||||||
|
return SlideTransition(position: offset, child: child);
|
||||||
|
} else {
|
||||||
|
// 其他页面:淡入
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: animation,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transitionDuration: const Duration(milliseconds: 300),
|
||||||
|
reverseTransitionDuration: const Duration(milliseconds: 300),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,15 +2,12 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../services/audio_service.dart';
|
import '../services/audio_service.dart';
|
||||||
import '../main.dart';
|
|
||||||
|
|
||||||
class GlobalMiniPlayer extends StatelessWidget {
|
class GlobalMiniPlayer extends StatelessWidget {
|
||||||
const GlobalMiniPlayer({super.key});
|
const GlobalMiniPlayer({super.key});
|
||||||
|
|
||||||
// ✅ 全局回调:打开播放页
|
// ✅ 保留静态回调
|
||||||
static VoidCallback? onOpenPlayerPage;
|
static VoidCallback? onOpenPlayerPage;
|
||||||
|
|
||||||
// ✅ 新增:打开播放列表
|
|
||||||
static VoidCallback? onOpenPlaylistPage;
|
static VoidCallback? onOpenPlaylistPage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -114,7 +111,7 @@ class GlobalMiniPlayer extends StatelessWidget {
|
|||||||
size: 24,
|
size: 24,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// ✅ 使用回调打开播放列表(与播放页同一套层级)
|
// ✅ 使用静态回调,而不是 Navigator.pushNamed
|
||||||
if (onOpenPlaylistPage != null) {
|
if (onOpenPlaylistPage != null) {
|
||||||
onOpenPlaylistPage!();
|
onOpenPlaylistPage!();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user