merge: 共享清屏状态并左贴集数

This commit is contained in:
Zhou Weidong committed 2026-10-19 17:29:39 +08:00
commit 5ca5846a26
4 files changed
+78 -50

No files matched your search

+22 -21
View File
@@ -71,7 +71,7 @@ class PlayerPage extends StatefulWidget {
class _PlayerPageState extends State<PlayerPage> with WidgetsBindingObserver { class _PlayerPageState extends State<PlayerPage> with WidgetsBindingObserver {
late final PlayerLogic logic; late final PlayerLogic logic;
bool _feedChromeVisible = true; bool _chromeVisible = true;
@override @override
void initState() { void initState() {
@@ -190,7 +190,7 @@ class _PlayerPageState extends State<PlayerPage> with WidgetsBindingObserver {
children: [ children: [
const ColoredBox(color: AppColors.ink), const ColoredBox(color: AppColors.ink),
?video, ?video,
if (!logic.chromeHidden && (!widget.homeMode || _feedChromeVisible)) if (!logic.chromeHidden && (!widget.homeMode || _chromeVisible))
IgnorePointer( IgnorePointer(
key: widget.homeMode ? const Key('home-feed-scrim') : null, key: widget.homeMode ? const Key('home-feed-scrim') : null,
child: const DecoratedBox( child: const DecoratedBox(
@@ -209,7 +209,7 @@ class _PlayerPageState extends State<PlayerPage> with WidgetsBindingObserver {
), ),
), ),
), ),
if (widget.homeMode && _feedChromeVisible && !logic.chromeHidden) if (widget.homeMode && _chromeVisible && !logic.chromeHidden)
Positioned.fill( Positioned.fill(
child: GestureDetector( child: GestureDetector(
key: const Key('home-feed-video-toggle'), key: const Key('home-feed-video-toggle'),
@@ -317,7 +317,7 @@ class _PlayerPageState extends State<PlayerPage> with WidgetsBindingObserver {
if (logic.pip || logic.pipPreparing) if (logic.pip || logic.pipPreparing)
const SizedBox.expand() const SizedBox.expand()
else if (widget.homeMode) else if (widget.homeMode)
_feedChromeVisible _chromeVisible
? HomeFeedOverlay( ? HomeFeedOverlay(
l10n: l10n, l10n: l10n,
drama: drama, drama: drama,
@@ -359,7 +359,7 @@ class _PlayerPageState extends State<PlayerPage> with WidgetsBindingObserver {
onShare: () => logic.shareDrama(context, drama), onShare: () => logic.shareDrama(context, drama),
onEpisodes: () => logic.showEpisodes(context), onEpisodes: () => logic.showEpisodes(context),
onDownload: () => requestEpisodeDownload(context, episode.id), onDownload: () => requestEpisodeDownload(context, episode.id),
onClear: () => setState(() => _feedChromeVisible = false), onClear: () => setState(() => _chromeVisible = false),
onSearch: onSearch:
widget.onSearch ?? () => logic.showDiscover(context), widget.onSearch ?? () => logic.showDiscover(context),
onPip: logic.pictureInPicture.isPlatformSupported onPip: logic.pictureInPicture.isPlatformSupported
@@ -384,6 +384,9 @@ class _PlayerPageState extends State<PlayerPage> with WidgetsBindingObserver {
? () => unawaited(logic.playNextEpisode()) ? () => unawaited(logic.playNextEpisode())
: null, : null,
playing: logic.session.userWantsPlay, playing: logic.session.userWantsPlay,
controlsVisible: _chromeVisible,
onClear: () => setState(() => _chromeVisible = false),
onExitClear: () => setState(() => _chromeVisible = true),
onTogglePlay: () => onTogglePlay: () =>
logic.session.setUserWantsPlay(!logic.session.userWantsPlay), logic.session.setUserWantsPlay(!logic.session.userWantsPlay),
// 收藏/点赞要对到具体账号:未登录先引导登录(首页/详情仍可匿名浏览) // 收藏/点赞要对到具体账号:未登录先引导登录(首页/详情仍可匿名浏览)
@@ -427,7 +430,7 @@ class _PlayerPageState extends State<PlayerPage> with WidgetsBindingObserver {
? (position) => logic.seekTo(context, videoPort!, position) ? (position) => logic.seekTo(context, videoPort!, position)
: null, : null,
), ),
if (widget.homeMode && !_feedChromeVisible) if (widget.homeMode && !_chromeVisible)
Positioned.fill( Positioned.fill(
child: GestureDetector( child: GestureDetector(
key: const Key('home-feed-video-tap'), key: const Key('home-feed-video-tap'),
@@ -436,7 +439,7 @@ class _PlayerPageState extends State<PlayerPage> with WidgetsBindingObserver {
logic.session.setUserWantsPlay(!logic.session.userWantsPlay), logic.session.setUserWantsPlay(!logic.session.userWantsPlay),
), ),
), ),
if (widget.homeMode && !_feedChromeVisible) if (widget.homeMode && !_chromeVisible)
Positioned( Positioned(
top: 0, top: 0,
right: 0, right: 0,
@@ -447,7 +450,7 @@ class _PlayerPageState extends State<PlayerPage> with WidgetsBindingObserver {
child: ExitClearScreenButton( child: ExitClearScreenButton(
key: const Key('home-feed-exit-clear-screen'), key: const Key('home-feed-exit-clear-screen'),
tooltip: l10n.exitClearScreen, tooltip: l10n.exitClearScreen,
onPressed: () => setState(() => _feedChromeVisible = true), onPressed: () => setState(() => _chromeVisible = true),
), ),
), ),
), ),
@@ -599,7 +602,10 @@ class _PlaybackOverlay extends StatefulWidget {
required this.liked, required this.liked,
required this.saved, required this.saved,
required this.playing, required this.playing,
required this.controlsVisible,
required this.onTogglePlay, required this.onTogglePlay,
required this.onClear,
required this.onExitClear,
required this.onBack, required this.onBack,
required this.onEpisodes, required this.onEpisodes,
required this.onComments, required this.onComments,
@@ -620,7 +626,10 @@ class _PlaybackOverlay extends StatefulWidget {
final bool liked; final bool liked;
final bool saved; final bool saved;
final bool playing; final bool playing;
final bool controlsVisible;
final VoidCallback onTogglePlay; final VoidCallback onTogglePlay;
final VoidCallback onClear;
final VoidCallback onExitClear;
final VoidCallback? onBack; final VoidCallback? onBack;
final VoidCallback onEpisodes; final VoidCallback onEpisodes;
final VoidCallback onComments; final VoidCallback onComments;
@@ -638,14 +647,6 @@ class _PlaybackOverlay extends StatefulWidget {
} }
class _PlaybackOverlayState extends State<_PlaybackOverlay> { class _PlaybackOverlayState extends State<_PlaybackOverlay> {
bool _controlsVisible = true;
void _onVideoTap() {
widget.onTogglePlay();
}
void _clearScreen() => setState(() => _controlsVisible = false);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SafeArea( return SafeArea(
@@ -655,10 +656,10 @@ class _PlaybackOverlayState extends State<_PlaybackOverlay> {
child: GestureDetector( child: GestureDetector(
key: const Key('player-video-tap'), key: const Key('player-video-tap'),
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onTap: _onVideoTap, onTap: widget.onTogglePlay,
), ),
), ),
if (_controlsVisible) if (widget.controlsVisible)
Column( Column(
key: const Key('player-controls-visible'), key: const Key('player-controls-visible'),
children: [ children: [
@@ -766,14 +767,14 @@ class _PlaybackOverlayState extends State<_PlaybackOverlay> {
), ),
], ],
), ),
if (!_controlsVisible) if (!widget.controlsVisible)
Positioned( Positioned(
top: 8, top: 8,
right: 12, right: 12,
child: ExitClearScreenButton( child: ExitClearScreenButton(
key: const Key('player-exit-clear-screen'), key: const Key('player-exit-clear-screen'),
tooltip: widget.l10n.exitClearScreen, tooltip: widget.l10n.exitClearScreen,
onPressed: () => setState(() => _controlsVisible = true), onPressed: widget.onExitClear,
), ),
), ),
if (!widget.playing) if (!widget.playing)
@@ -828,7 +829,7 @@ class _PlaybackOverlayState extends State<_PlaybackOverlay> {
key: const Key('player-clear-screen'), key: const Key('player-clear-screen'),
tooltip: widget.l10n.clearScreen, tooltip: widget.l10n.clearScreen,
icon: Icons.fullscreen_rounded, icon: Icons.fullscreen_rounded,
onPressed: _clearScreen, onPressed: widget.onClear,
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
_TopAction( _TopAction(
+18 -29
View File
@@ -167,38 +167,27 @@ class HomeFeedOverlay extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Wrap(
spacing: 6,
runSpacing: 4,
crossAxisAlignment: WrapCrossAlignment.center,
children: [ children: [
Expanded( if (genre.isNotEmpty)
child: Wrap( _tag(
spacing: 6, genre,
runSpacing: 4, background: AppColors.blue,
children: [ foreground: AppColors.onPrimary,
if (genre.isNotEmpty)
_tag(
genre,
background: AppColors.blue,
foreground: AppColors.onPrimary,
),
if (drama.category.isNotEmpty && drama.category != genre)
_tag(
drama.category,
background: const Color(0xFF333333),
foreground: const Color(0xFFDDDDDD),
),
],
), ),
), if (drama.category.isNotEmpty && drama.category != genre)
const SizedBox(width: 8), _tag(
Flexible( drama.category,
child: Text( background: const Color(0xFF333333),
l10n.episodeOfTotal(episode.number, drama.episodeCount), foreground: const Color(0xFFDDDDDD),
key: const Key('home-feed-episode'),
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.right,
style: const TextStyle(color: Color(0xFFBBBBBB), fontSize: 9),
), ),
Text(
l10n.episodeOfTotal(episode.number, drama.episodeCount),
key: const Key('home-feed-episode'),
style: const TextStyle(color: Color(0xFFBBBBBB), fontSize: 9),
), ),
], ],
), ),
+7
View File
@@ -54,6 +54,13 @@ void main() {
expect(find.byKey(const Key('home-feed-title')), findsOneWidget); expect(find.byKey(const Key('home-feed-title')), findsOneWidget);
expect(find.byKey(const Key('home-feed-info-card')), findsOneWidget); expect(find.byKey(const Key('home-feed-info-card')), findsOneWidget);
expect(find.byKey(const Key('home-feed-episode')), findsOneWidget); expect(find.byKey(const Key('home-feed-episode')), findsOneWidget);
final cardRect = tester.getRect(
find.byKey(const Key('home-feed-info-card')),
);
final episodeRect = tester.getRect(
find.byKey(const Key('home-feed-episode')),
);
expect(episodeRect.left, lessThan(cardRect.center.dx));
expect(find.byKey(const Key('home-feed-follow-series')), findsOneWidget); expect(find.byKey(const Key('home-feed-follow-series')), findsOneWidget);
expect(find.byKey(const Key('home-feed-episodes')), findsOneWidget); expect(find.byKey(const Key('home-feed-episodes')), findsOneWidget);
for (final key in const [Key('home-feed-search'), Key('home-feed-more')]) { for (final key in const [Key('home-feed-search'), Key('home-feed-more')]) {
+31
View File
@@ -216,6 +216,37 @@ void main() {
expect(tester.takeException(), isNull); expect(tester.takeException(), isNull);
}); });
testWidgets('should_keep_series_clear_state_when_swiping_next_episode', (
tester,
) async {
await _pumpApp(tester);
await tester.binding.handlePopRoute();
await tester.pumpAndSettle();
await tester.binding.handlePopRoute();
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('drawer-open-series')));
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('detail-cta')));
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('player-clear-screen')));
await tester.pump();
expect(find.byKey(const Key('player-controls-top')), findsNothing);
await tester.drag(find.byType(PageView), const Offset(0, -550));
await tester.pumpAndSettle();
expect(find.byKey(const Key('player-controls-top')), findsNothing);
expect(find.byKey(const Key('player-controls-bottom')), findsNothing);
expect(find.byKey(const Key('player-exit-clear-screen')), findsOneWidget);
await tester.tap(find.byKey(const Key('player-exit-clear-screen')));
await tester.pump();
expect(find.byKey(const Key('player-controls-top')), findsOneWidget);
expect(find.byKey(const Key('player-controls-bottom')), findsOneWidget);
expect(find.byKey(const Key('player-exit-clear-screen')), findsNothing);
});
testWidgets('should_clear_and_restore_home_feed_chrome', (tester) async { testWidgets('should_clear_and_restore_home_feed_chrome', (tester) async {
await _pumpApp(tester); await _pumpApp(tester);
await tester.tap(find.byKey(const Key('tab-home'))); await tester.tap(find.byKey(const Key('tab-home')));