Skip to content

[video_player] Calling dispose() twice on the same VideoPlayerController throws an exception #104062

@navaronbracke

Description

@navaronbracke

I have a use case where I need a carrousel of videos that displays only one video at a time, with the option to go back and forth between all the videos. When going to another video, I call await controller.dispose() on the old controller before making the new one.

However, calling dispose() twice on the same VideoPlayerController results in a crash. (not sure how I ended up in that situation though)

It seems that internally VideoPlayerController.dispose() does not check _isDisposed in the beginning, while in other places (like in removeListener()) it is checked first.

This should probably be added at the top of VideoPlayerController.dispose()?

if(_isDisposed){
  return;
}

cc @stuartmorgan

Steps to Reproduce

  1. Execute flutter run on the code sample
  2. Tap the button to dispose of the video controller, the controller gets disposed.
  3. Tap the button again
  4. The crash occurs.

Expected results: No exception was thrown.

Actual results: The following exception is thrown:

Error: A VideoPlayerController was used after being disposed.
Once you have called dispose() on a VideoPlayerController, it can no longer be used.
    at Object.throw_ [as throw] (http://localhost:53460/dart_sdk.js:5067:11)
    at http://localhost:53460/packages/flutter/src/foundation/change_notifier.dart.lib.js:72:21
    at video_player.VideoPlayerController.network.[_debugAssertNotDisposed] (http://localhost:53460/packages/flutter/src/foundation/change_notifier.dart.lib.js:75:25)
    at video_player.VideoPlayerController.network.dispose (http://localhost:53460/packages/flutter/src/foundation/change_notifier.dart.lib.js:129:41)
    at video_player.VideoPlayerController.network.[dispose] (http://localhost:53460/packages/video_player/video_player.dart.lib.js:998:20)
    at video_player.VideoPlayerController.network.dispose$ (http://localhost:53460/packages/video_player/video_player.dart.lib.js:855:22)
    at dispose$.next (<anonymous>)
    at http://localhost:53460/dart_sdk.js:40571:33
    at _RootZone.runUnary (http://localhost:53460/dart_sdk.js:40441:59)
    at _FutureListener.thenAwait.handleValue (http://localhost:53460/dart_sdk.js:35363:29)
    at handleValueCallback (http://localhost:53460/dart_sdk.js:35931:49)
    at Function._propagateToListeners (http://localhost:53460/dart_sdk.js:35969:17)
    at async._AsyncCallbackEntry.new.callback (http://localhost:53460/dart_sdk.js:35698:27)
    at Object._microtaskLoop (http://localhost:53460/dart_sdk.js:40708:13)
    at _startMicrotaskLoop (http://localhost:53460/dart_sdk.js:40714:13)
    at http://localhost:53460/dart_sdk.js:36191:9
Code sample
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';

void main() => runApp(VideoApp());

class VideoApp extends StatefulWidget {
  @override
  _VideoAppState createState() => _VideoAppState();
}

class _VideoAppState extends State<VideoApp> {
  late VideoPlayerController _controller;

  @override
  void initState() {
    super.initState();
    _controller = VideoPlayerController.network(
        'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4')
      ..initialize().then((_) {
        // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
        setState(() {});
      });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Video Demo',
      home: Scaffold(
        body: Column(
          children: [
            Padding(
              padding: const EdgeInsets.all(8),
              child: Center(
                child: TextButton(
                  onPressed: _controller.value.isInitialized ? () async {
                    await _controller.dispose();
                  }: null,
                  child: const Text('Dispose of controller'),
                ),
              ),
            ),
            Expanded(
              child: Center(
                child: _controller.value.isInitialized
                    ? AspectRatio(
                        aspectRatio: _controller.value.aspectRatio,
                        child: VideoPlayer(_controller),
                      )
                    : Container(),
              ),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    _controller.dispose();
  }
}
Logs
[✓] Flutter (Channel stable, 2.10.3, on macOS 12.2.1 21D62 darwin-x64, locale en-BE)
    • Flutter version 2.10.3 at /Users/navaronbracke/Documents/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 7e9793dee1 (3 months ago), 2022-03-02 11:23:12 -0600
    • Engine revision bd539267b4
    • Dart version 2.16.1
    • DevTools version 2.9.2

[✓] Android toolchain - develop for Android devices (Android SDK version 32.1.0-rc1)
    • Android SDK at /Users/navaronbracke/Library/Android/sdk
    • Platform android-32, build-tools 32.1.0-rc1
    • ANDROID_HOME = /Users/navaronbracke/Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.11+0-b60-7590822)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 13.2.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • CocoaPods version 1.11.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2021.1)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.11+0-b60-7590822)

[✓] VS Code (version 1.67.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.40.0

[✓] Connected device (1 available)
    • Chrome (web) • chrome • web-javascript • Google Chrome 101.0.4951.64

[✓] HTTP Host Availability
    • All required HTTP hosts are available

• No issues found!

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projectfound in release: 3.0Found to occur in 3.0found in release: 3.1Found to occur in 3.1has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: video_playerThe Video Player pluginpackageflutter/packages repository. See also p: labels.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions