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

Commit af758d5

Browse files
bselweditman
andauthored
[video_player] Fix a disposed VideoPlayerController throwing an exception when being replaced in the VideoPlayer (#4344)
* fix: do not removeListener if VideoPlayerController is already disposed Co-authored-by: David Iglesias Teixeira <ditman@gmail.com>
1 parent bb3ebb2 commit af758d5

File tree

4 files changed

+106
-1
lines changed

4 files changed

+106
-1
lines changed

packages/video_player/video_player/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.2.2
2+
3+
* Fix a disposed `VideoPlayerController` throwing an exception when being replaced in the `VideoPlayer`.
4+
15
## 2.2.1
26

37
* Specify Java 8 for Android build.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:flutter/foundation.dart';
8+
import 'package:flutter/material.dart';
9+
import 'package:integration_test/integration_test.dart';
10+
import 'package:flutter_test/flutter_test.dart';
11+
import 'package:video_player/video_player.dart';
12+
13+
const Duration _playDuration = Duration(seconds: 1);
14+
15+
void main() {
16+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
17+
testWidgets(
18+
'can substitute one controller by another without crashing',
19+
(WidgetTester tester) async {
20+
VideoPlayerController controller = VideoPlayerController.network(
21+
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
22+
);
23+
VideoPlayerController another = VideoPlayerController.network(
24+
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
25+
);
26+
await controller.initialize();
27+
await another.initialize();
28+
await controller.setVolume(0);
29+
await another.setVolume(0);
30+
31+
final Completer<void> started = Completer();
32+
final Completer<void> ended = Completer();
33+
bool startedBuffering = false;
34+
bool endedBuffering = false;
35+
36+
another.addListener(() {
37+
if (another.value.isBuffering && !startedBuffering) {
38+
startedBuffering = true;
39+
started.complete();
40+
}
41+
if (startedBuffering && !another.value.isBuffering && !endedBuffering) {
42+
endedBuffering = true;
43+
ended.complete();
44+
}
45+
});
46+
47+
// Inject a widget with `controller`...
48+
await tester.pumpWidget(renderVideoWidget(controller));
49+
await controller.play();
50+
await tester.pumpAndSettle(_playDuration);
51+
await controller.pause();
52+
53+
// Disposing controller causes the Widget to crash in the next line
54+
// (Issue https://github.com/flutter/flutter/issues/90046)
55+
await controller.dispose();
56+
57+
// Now replace it with `another` controller...
58+
await tester.pumpWidget(renderVideoWidget(another));
59+
await another.play();
60+
await another.seekTo(const Duration(seconds: 5));
61+
await tester.pumpAndSettle(_playDuration);
62+
await another.pause();
63+
64+
// Expect that `another` played.
65+
expect(another.value.position,
66+
(Duration position) => position > const Duration(seconds: 0));
67+
68+
await started;
69+
expect(startedBuffering, true);
70+
71+
await ended;
72+
expect(endedBuffering, true);
73+
},
74+
skip: !(kIsWeb || defaultTargetPlatform == TargetPlatform.android),
75+
);
76+
}
77+
78+
Widget renderVideoWidget(VideoPlayerController controller) {
79+
return Material(
80+
elevation: 0,
81+
child: Directionality(
82+
textDirection: TextDirection.ltr,
83+
child: Center(
84+
child: AspectRatio(
85+
key: Key('same'),
86+
aspectRatio: controller.value.aspectRatio,
87+
child: VideoPlayer(controller),
88+
),
89+
),
90+
),
91+
);
92+
}

packages/video_player/video_player/lib/video_player.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,15 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
594594
value = value.copyWith(caption: _getCaptionAt(position));
595595
}
596596

597+
@override
598+
void removeListener(VoidCallback listener) {
599+
// Prevent VideoPlayer from causing an exception to be thrown when attempting to
600+
// remove its own listener after the controller has already been disposed.
601+
if (!_isDisposed) {
602+
super.removeListener(listener);
603+
}
604+
}
605+
597606
bool get _isDisposedOrNotInitialized => _isDisposed || !value.isInitialized;
598607
}
599608

packages/video_player/video_player/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter
33
widgets on Android, iOS, and web.
44
repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player
55
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
6-
version: 2.2.1
6+
version: 2.2.2
77

88
environment:
99
sdk: ">=2.12.0 <3.0.0"

0 commit comments

Comments
 (0)