Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[video_player] Fix a disposed VideoPlayerController throwing an exception when being replaced in the VideoPlayer #4344

Merged
merged 5 commits into from
Sep 15, 2021
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
4 changes: 4 additions & 0 deletions packages/video_player/video_player/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.2.2

* Fix a disposed `VideoPlayerController` throwing an exception when being replaced in the `VideoPlayer`.

## 2.2.1

* Specify Java 8 for Android build.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:integration_test/integration_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:video_player/video_player.dart';

const Duration _playDuration = Duration(seconds: 1);

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets(
'can substitute one controller by another without crashing',
(WidgetTester tester) async {
VideoPlayerController controller = VideoPlayerController.network(
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
);
VideoPlayerController another = VideoPlayerController.network(
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
);
await controller.initialize();
await another.initialize();
await controller.setVolume(0);
await another.setVolume(0);

final Completer<void> started = Completer();
final Completer<void> ended = Completer();
bool startedBuffering = false;
bool endedBuffering = false;

another.addListener(() {
if (another.value.isBuffering && !startedBuffering) {
startedBuffering = true;
started.complete();
}
if (startedBuffering && !another.value.isBuffering && !endedBuffering) {
endedBuffering = true;
ended.complete();
}
});

// Inject a widget with `controller`...
await tester.pumpWidget(renderVideoWidget(controller));
await controller.play();
await tester.pumpAndSettle(_playDuration);
await controller.pause();

// Disposing controller causes the Widget to crash in the next line
// (Issue https://github.com/flutter/flutter/issues/90046)
await controller.dispose();

// Now replace it with `another` controller...
await tester.pumpWidget(renderVideoWidget(another));
await another.play();
await another.seekTo(const Duration(seconds: 5));
await tester.pumpAndSettle(_playDuration);
await another.pause();

// Expect that `another` played.
expect(another.value.position,
(Duration position) => position > const Duration(seconds: 0));

await started;
expect(startedBuffering, true);

await ended;
expect(endedBuffering, true);
},
skip: !(kIsWeb || defaultTargetPlatform == TargetPlatform.android),
);
}

Widget renderVideoWidget(VideoPlayerController controller) {
return Material(
elevation: 0,
child: Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: AspectRatio(
key: Key('same'),
aspectRatio: controller.value.aspectRatio,
child: VideoPlayer(controller),
),
),
),
);
}
9 changes: 9 additions & 0 deletions packages/video_player/video_player/lib/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,15 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
value = value.copyWith(caption: _getCaptionAt(position));
}

@override
void removeListener(VoidCallback listener) {
// Prevent VideoPlayer from causing an exception to be thrown when attempting to
// remove its own listener after the controller has already been disposed.
if (!_isDisposed) {
super.removeListener(listener);
}
}

bool get _isDisposedOrNotInitialized => _isDisposed || !value.isInitialized;
}

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, and web.
repository: https://github.com/flutter/plugins/tree/master/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.2.1
version: 2.2.2

environment:
sdk: ">=2.12.0 <3.0.0"
Expand Down