fix(player): 复用播放会话进入全屏

This commit is contained in:
Zhou Weidong committed 2026-10-26 15:55:46 +08:00
1 parent ab4c246675
commit 23e80dd637
4 files changed
+303 -77

No files matched your search

+112 -6
View File
@@ -17,12 +17,14 @@ import 'package:fruitshorts/core/data/repositories/favorite_repository.dart';
import 'package:fruitshorts/core/data/repositories/playback_repository.dart';
import 'package:fruitshorts/core/data/repositories/watch_history_repository.dart';
import 'package:fruitshorts/core/data/services/playback_session.dart';
import 'package:fruitshorts/core/data/services/video_player_port.dart';
import 'package:fruitshorts/core/theme/app_colors.dart';
import 'package:fruitshorts/pages/auth/auth_view.dart';
import 'package:fruitshorts/pages/player/player_view.dart';
import 'package:fruitshorts/pages/player/widgets/player_center_toggle.dart';
import 'package:fruitshorts/pages/player/widgets/player_render_stack.dart';
import 'package:fruitshorts/pages/player/widgets/seek_progress_bar.dart';
import 'package:video_player/video_player.dart';
import '../support/fake_catalog.dart';
import '../support/fake_category.dart';
@@ -86,7 +88,9 @@ void main() {
testWidgets('should_preserve_recommend_feed_when_full_player_pops', (
tester,
) async {
await _pumpApp(tester);
final factory = _RouteVideoFactory();
final playback = FakePlaybackRepository();
await _pumpApp(tester, playerFactory: factory.create, playback: playback);
await tester.tap(find.byKey(const Key('tab-recommend')));
await tester.pumpAndSettle();
@@ -101,27 +105,80 @@ void main() {
expect(renderStack, findsOneWidget);
final playerState = tester.state(recommendPlayer);
final renderState = tester.state(renderStack);
final currentSurface = find.descendant(
of: recommendPlayer,
matching: find.byKey(
const ValueKey('player-render-slot-current'),
),
);
final controller = factory.created.first.controller;
final createdBefore = factory.created.length;
final requestsBefore = playback.requests.length;
expect(controller.value.position, const Duration(seconds: 5));
expect(
find.descendant(
of: currentSurface,
matching: find.byKey(const Key('player-single-placeholder')),
),
findsNothing,
);
await tester.tap(find.byKey(const Key('home-feed-full-series')));
await tester.pumpAndSettle();
expect(find.byType(PlayerPage), findsOneWidget);
expect(find.byKey(const Key('player-back')), findsOneWidget);
expect(factory.created.length, createdBefore);
expect(playback.requests.length, requestsBefore);
expect(controller.value.position, const Duration(seconds: 5));
expect(
tester.widget<PlayerPage>(find.byType(PlayerPage).last).homeMode,
isFalse,
find.descendant(
of: currentSurface,
matching: find.byKey(const Key('player-single-placeholder')),
),
findsNothing,
);
tester.state<NavigatorState>(find.byType(Navigator).first).pop();
await tester.pump();
await tester.pumpAndSettle();
expect(find.byKey(const Key('recommend-page')), findsOneWidget);
expect(recommendPlayer, findsOneWidget);
expect(renderStack, findsOneWidget);
expect(factory.created.length, createdBefore);
expect(playback.requests.length, requestsBefore);
expect(controller.value.position, const Duration(seconds: 5));
expect(tester.state(recommendPlayer), same(playerState));
expect(tester.state(renderStack), same(renderState));
expect(find.byKey(const Key('recommend-feed-skeleton')), findsNothing);
expect(
find.descendant(
of: currentSurface,
matching: find.byKey(const Key('player-single-placeholder')),
),
findsNothing,
);
});
testWidgets('should_swipe_full_player_without_creating_another_session', (
tester,
) async {
final factory = _RouteVideoFactory();
await _pumpApp(tester, playerFactory: factory.create);
await tester.tap(find.byKey(const Key('tab-recommend')));
await tester.pumpAndSettle();
expect(tester.state(recommendPlayer), same(playerState));
expect(tester.state(renderStack), same(renderState));
await tester.tap(find.byKey(const Key('home-feed-full-series')));
await tester.pumpAndSettle();
final createdBefore = factory.created.length;
await tester.drag(
find.byKey(const Key('player-video-tap')),
const Offset(0, -560),
);
await tester.pumpAndSettle();
expect(find.byType(PlayerPage), findsOneWidget);
expect(factory.created.length, lessThanOrEqualTo(createdBefore + 1));
expect(find.byKey(const Key('player-render-stack')), findsOneWidget);
});
testWidgets('should_render_player_loading_indicators_at_compact_size', (
@@ -1482,6 +1539,55 @@ Future<void> _pumpApp(
if (settle) await tester.pumpAndSettle();
}
class _RouteVideoFactory {
final List<_RouteVideoPlayer> created = [];
PlayerPort create(Episode episode) {
final player = _RouteVideoPlayer(episode);
created.add(player);
return player;
}
}
class _RouteVideoPlayer extends VideoPlayerPort {
_RouteVideoPlayer(super.episode);
@override
Future<void> initialize() async {
controller.value = const VideoPlayerValue(
duration: Duration(minutes: 1),
size: Size(1280, 720),
position: Duration(seconds: 5),
isInitialized: true,
isPlaying: true,
);
}
@override
Future<void> play() async {
final value = controller.value;
controller.value = VideoPlayerValue(
duration: value.duration,
size: value.size,
position: value.position,
isInitialized: value.isInitialized,
isPlaying: true,
);
}
@override
Future<void> pause() async {
final value = controller.value;
controller.value = VideoPlayerValue(
duration: value.duration,
size: value.size,
position: value.position,
isInitialized: value.isInitialized,
isPlaying: false,
);
}
}
class _RecordingPlayer implements PlayerPort {
bool playing = false;