Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/video_player/video_player/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 2.14.1

* Fixes late position updates after a controller is disposed.
* Updates the example app to Robolectric 4.17.
* Updates minimum supported SDK version to Flutter 3.41/Dart 3.11.

Expand Down
4 changes: 4 additions & 0 deletions packages/video_player/video_player/lib/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,10 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
}

void _updatePosition(Duration position) {
// A pending platform seek or position request can complete after disposal.
if (_isDisposed) {
return;
}
// The underlying native implementation on some platforms sometimes reports
// a position slightly past the reported max duration. Clamp to the duration
// to insulate clients from this behavior.
Expand Down
2 changes: 1 addition & 1 deletion packages/video_player/video_player/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter
widgets on Android, iOS, macOS and web.
repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
version: 2.14.0
version: 2.14.1

environment:
sdk: ^3.12.0
Expand Down
42 changes: 42 additions & 0 deletions packages/video_player/video_player/test/video_player_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,24 @@ void main() {
});

group('seekTo', () {
test('ignores a pending seek result after disposal', () async {
final controller = VideoPlayerController.networkUrl(_localhostUri);
addTearDown(controller.dispose);
Comment thread
Neelansh-ns marked this conversation as resolved.
await controller.initialize();

final seekCompleter = Completer<void>();
fakeVideoPlayerPlatform.seekCompleter = seekCompleter;
Comment thread
Neelansh-ns marked this conversation as resolved.
final VideoPlayerValue valueBeforeSeek = controller.value;
final Future<void> pendingSeek = controller.seekTo(const Duration(milliseconds: 500));
expect(fakeVideoPlayerPlatform.calls.last, 'seekTo');

await controller.dispose();
seekCompleter.complete();
await pendingSeek;

expect(controller.value, valueBeforeSeek);
});

test('works', () async {
final controller = VideoPlayerController.networkUrl(_localhostUri);
addTearDown(controller.dispose);
Expand Down Expand Up @@ -1456,6 +1474,28 @@ void main() {
});

group('Platform callbacks', () {
testWidgets('ignores a pending completion seek result after disposal', (
WidgetTester tester,
) async {
final controller = VideoPlayerController.networkUrl(_localhostUri);
addTearDown(controller.dispose);
Comment thread
Neelansh-ns marked this conversation as resolved.
await controller.initialize();

final seekCompleter = Completer<void>();
fakeVideoPlayerPlatform.seekCompleter = seekCompleter;
Comment thread
Neelansh-ns marked this conversation as resolved.
fakeVideoPlayerPlatform.streams[controller.playerId]!.add(
VideoEvent(eventType: VideoEventType.completed),
);
await tester.pump();
expect(fakeVideoPlayerPlatform.calls.last, 'seekTo');
final VideoPlayerValue valueBeforeDisposal = controller.value;

await tester.runAsync(controller.dispose);
seekCompleter.complete();
await tester.pump();
expect(controller.value, valueBeforeDisposal);
});

testWidgets('playing completed', (WidgetTester tester) async {
final controller = VideoPlayerController.networkUrl(_localhostUri);

Expand Down Expand Up @@ -2021,6 +2061,7 @@ void main() {

class FakeVideoPlayerPlatform extends VideoPlayerPlatform {
Completer<bool> initialized = Completer<bool>();
Completer<void>? seekCompleter;
List<String> calls = <String>[];
List<DataSource> dataSources = <DataSource>[];
List<VideoViewType> viewTypes = <VideoViewType>[];
Expand Down Expand Up @@ -2108,6 +2149,7 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform {
@override
Future<void> seekTo(int playerId, Duration position) async {
calls.add('seekTo');
await seekCompleter?.future;
_positions[playerId] = position;
}

Expand Down
Loading