<p dir="auto">There is a race condition in the video_player plugin code that prevents videos from displaying on a Widget.</p>
<h2 dir="auto">Steps to Reproduce</h2>
<ol dir="auto">
<li>Download a video on your phone: <a href="http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_20mb.mp4" rel="nofollow">http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_20mb.mp4</a></li>
<li>Edit the <code class="notranslate">image_picker</code> sample app (<a href="https://github.com/flutter/plugins/blob/master/packages/image_picker/example/lib/main.dart#L50">https://github.com/flutter/plugins/blob/master/packages/image_picker/example/lib/main.dart#L50</a>) and add the following in line 50 just after <code class="notranslate">if (file != null &amp;&amp; mounted) {</code>:</li>
</ol>
<div class="highlight highlight-source-dart notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="print(file);"><pre class="notranslate"><span class="pl-en">print</span>(file);</pre></div>
<ol start="3" dir="auto">
<li>Run the <code class="notranslate">image_picker</code> sample app, enable read permissions from external storage and choose the video from the gallery. Keep note of the video path. It will look something like <code class="notranslate">/storage/emulated/0/Download/big_buck_bunny_720p_20mb.mp4</code> (remove the file:// prefix if needed)</li>
<li>Replace the main.dart content in <code class="notranslate">image_picker</code> by the code below (Based on the <code class="notranslate">video_player</code> sample app found <a href="https://github.com/flutter/plugins/tree/master/packages/video_player">here</a>.</li>
<li>Open and close the app until the video no longer shows. You should see a blank screen.</li>
</ol>
<div class="highlight highlight-source-dart notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import 'dart:io';

import 'package:video_player/video_player.dart';
import 'package:flutter/material.dart';

void main() =&gt; runApp(VideoApp());

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

class _VideoAppState extends State&lt;VideoApp&gt; {
  VideoPlayerController _controller;

  @override
  void initState() {
    try {
      super.initState();
      // _controller = VideoPlayerController.network('http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_20mb.mp4',)
      _controller = VideoPlayerController.file(
          new File('/storage/emulated/0/Download/big_buck_bunny_720p_20mb.mp4'))
        ..addListener(() {
          print('controller listener: $_controller');
          // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
          setState(() {});
        })
        ..initialize().then((_) {
          print('initialized listener: $_controller');
          setState(() {});
        }).catchError((error) {
          print('Unexpected error1: $error');
        });
      print('controller is set');
    } catch (e) {
      print('Unexpected error: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    try {
      print('entering build. Needs _controller $_controller');
      return MaterialApp(
        title: 'Video Demo',
        home: Scaffold(
          body: Center(
            child: _controller.value.initialized
                ? AspectRatio(
                    aspectRatio: _controller.value.aspectRatio,
                    child: VideoPlayer(_controller),
                  )
                : Container(),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: _controller.value.isPlaying
                ? _controller.pause
                : _controller.play,
            child: Icon(
              _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
            ),
          ),
        ),
      );
    } catch (e) {
      print('Unexpected error: $e');
    }
  }
}
"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-s">'dart:io'</span>;

<span class="pl-k">import</span> <span class="pl-s">'package:video_player/video_player.dart'</span>;
<span class="pl-k">import</span> <span class="pl-s">'package:flutter/material.dart'</span>;

<span class="pl-k">void</span> <span class="pl-en">main</span>() <span class="pl-k">=&gt;</span> <span class="pl-en">runApp</span>(<span class="pl-c1">VideoApp</span>());

<span class="pl-k">class</span> <span class="pl-c1">VideoApp</span> <span class="pl-k">extends</span> <span class="pl-c1">StatefulWidget</span> {
  <span class="pl-k">@override</span>
  <span class="pl-c1">_VideoAppState</span> <span class="pl-en">createState</span>() <span class="pl-k">=&gt;</span> <span class="pl-c1">_VideoAppState</span>();
}

<span class="pl-k">class</span> <span class="pl-c1">_VideoAppState</span> <span class="pl-k">extends</span> <span class="pl-c1">State</span>&lt;<span class="pl-c1">VideoApp</span>&gt; {
  <span class="pl-c1">VideoPlayerController</span> _controller;

  <span class="pl-k">@override</span>
  <span class="pl-k">void</span> <span class="pl-en">initState</span>() {
    <span class="pl-k">try</span> {
      <span class="pl-c1">super</span>.<span class="pl-en">initState</span>();
      <span class="pl-c">// _controller = VideoPlayerController.network('http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_20mb.mp4',)</span>
      _controller <span class="pl-k">=</span> <span class="pl-c1">VideoPlayerController</span>.<span class="pl-en">file</span>(
          <span class="pl-k">new</span> <span class="pl-c1">File</span>(<span class="pl-s">'/storage/emulated/0/Download/big_buck_bunny_720p_20mb.mp4'</span>))
        ..<span class="pl-en">addListener</span>(() {
          <span class="pl-en">print</span>(<span class="pl-s">'controller listener: $<span class="pl-v">_controller</span>'</span>);
          <span class="pl-c">// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.</span>
          <span class="pl-en">setState</span>(() {});
        })
        ..<span class="pl-en">initialize</span>().<span class="pl-en">then</span>((_) {
          <span class="pl-en">print</span>(<span class="pl-s">'initialized listener: $<span class="pl-v">_controller</span>'</span>);
          <span class="pl-en">setState</span>(() {});
        }).<span class="pl-en">catchError</span>((error) {
          <span class="pl-en">print</span>(<span class="pl-s">'Unexpected error1: $<span class="pl-v">error</span>'</span>);
        });
      <span class="pl-en">print</span>(<span class="pl-s">'controller is set'</span>);
    } <span class="pl-k">catch</span> (e) {
      <span class="pl-en">print</span>(<span class="pl-s">'Unexpected error: $<span class="pl-v">e</span>'</span>);
    }
  }

  <span class="pl-k">@override</span>
  <span class="pl-c1">Widget</span> <span class="pl-en">build</span>(<span class="pl-c1">BuildContext</span> context) {
    <span class="pl-k">try</span> {
      <span class="pl-en">print</span>(<span class="pl-s">'entering build. Needs _controller $<span class="pl-v">_controller</span>'</span>);
      <span class="pl-k">return</span> <span class="pl-c1">MaterialApp</span>(
        title<span class="pl-k">:</span> <span class="pl-s">'Video Demo'</span>,
        home<span class="pl-k">:</span> <span class="pl-c1">Scaffold</span>(
          body<span class="pl-k">:</span> <span class="pl-c1">Center</span>(
            child<span class="pl-k">:</span> _controller.value.initialized
                <span class="pl-k">?</span> <span class="pl-c1">AspectRatio</span>(
                    aspectRatio<span class="pl-k">:</span> _controller.value.aspectRatio,
                    child<span class="pl-k">:</span> <span class="pl-c1">VideoPlayer</span>(_controller),
                  )
                <span class="pl-k">:</span> <span class="pl-c1">Container</span>(),
          ),
          floatingActionButton<span class="pl-k">:</span> <span class="pl-c1">FloatingActionButton</span>(
            onPressed<span class="pl-k">:</span> _controller.value.isPlaying
                <span class="pl-k">?</span> _controller.pause
                <span class="pl-k">:</span> _controller.play,
            child<span class="pl-k">:</span> <span class="pl-c1">Icon</span>(
              _controller.value.isPlaying <span class="pl-k">?</span> <span class="pl-c1">Icons</span>.pause <span class="pl-k">:</span> <span class="pl-c1">Icons</span>.play_arrow,
            ),
          ),
        ),
      );
    } <span class="pl-k">catch</span> (e) {
      <span class="pl-en">print</span>(<span class="pl-s">'Unexpected error: $<span class="pl-v">e</span>'</span>);
    }
  }
}
</pre></div>

<p dir="auto">Buggy app:</p>
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/1159610/43369080-7193f5fa-935f-11e8-8b68-f689a061317a.png"><img src="https://user-images.githubusercontent.com/1159610/43369080-7193f5fa-935f-11e8-8b68-f689a061317a.png" alt="buggy" style="max-width: 100%;"></a></p>
<p dir="auto">Expected:<br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/1159610/43369083-7b986f86-935f-11e8-9a5a-a3baab00cd36.png"><img src="https://user-images.githubusercontent.com/1159610/43369083-7b986f86-935f-11e8-9a5a-a3baab00cd36.png" alt="not-buggy" style="max-width: 100%;"></a></p>
<h2 dir="auto">Logs</h2>
<p dir="auto">When the video works. You'll see in the logs:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="I/flutter ( 8147): controller listener: VideoPlayerController#fff7e(VideoPlayerValue(duration: 0:01:57.312000, size: Size(1280.0, 720.0), position: 0:00:00.000000, buffered: [], isPlaying: false, isLooping: false, isBuffering: falsevolume: 1.0, errorDescription: null,rotationDegrees: 0))
I/flutter ( 8147): initialized listener: VideoPlayerController#fff7e(VideoPlayerValue(duration: 0:01:57.312000, size: Size(1280.0, 720.0), position: 0:00:00.000000, buff"><pre class="notranslate"><code class="notranslate">I/flutter ( 8147): controller listener: VideoPlayerController#fff7e(VideoPlayerValue(duration: 0:01:57.312000, size: Size(1280.0, 720.0), position: 0:00:00.000000, buffered: [], isPlaying: false, isLooping: false, isBuffering: falsevolume: 1.0, errorDescription: null,rotationDegrees: 0))
I/flutter ( 8147): initialized listener: VideoPlayerController#fff7e(VideoPlayerValue(duration: 0:01:57.312000, size: Size(1280.0, 720.0), position: 0:00:00.000000, buff
</code></pre></div>
<p dir="auto">Otherwise you will see something like (the _controller.value is an empty/default video instance). Notice the initialized listener is never called:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="I/flutter (10928): controller listener: VideoPlayerController#0235e(VideoPlayerValue(duration: null, size: null, position: 0:00:00.000000, buffered: [], isPlaying: false, isLooping: false, isBuffering: falsevolume: 1.0, errorDescription: null,rotationDegrees: null))"><pre lang="LOG" class="notranslate"><code class="notranslate">I/flutter (10928): controller listener: VideoPlayerController#0235e(VideoPlayerValue(duration: null, size: null, position: 0:00:00.000000, buffered: [], isPlaying: false, isLooping: false, isBuffering: falsevolume: 1.0, errorDescription: null,rotationDegrees: null))
</code></pre></div>
<h2 dir="auto">Root Cause</h2>
<p dir="auto">The <code class="notranslate">initialized</code> event is not always received in the dart code. This event is sent from the ExoPlayer to the Java code, from there to the Dart world through an Event Channel. However it is possible that the init event from the ExoPlayer is fired before the dart code had a chance to listen to any broadcasts from the Java code (eventSink is null in <a href="https://github.com/flutter/plugins/blob/master/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java#L213">https://github.com/flutter/plugins/blob/master/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java#L213</a>). In such a case, the init event is not fired and the video cannot be played nor displayed.</p>

<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="flutter doctor -v
[✓] Flutter (Channel master, v0.5.8-pre.152, on Mac OS X 10.12.6 16G1510, locale fr-FR)
    • Flutter version 0.5.8-pre.152 at /Users/rodrigo/Libs/flutter
    • Framework revision 8b05629658 (3 days ago), 2018-07-26 19:12:04 -0700
    • Engine revision 6f7bdc9fab
    • Dart version 2.0.0-dev.69.3.flutter-937ee2e8ca

[✓] Android toolchain - develop for Android devices (Android SDK 27.0.3)
    • Android SDK at /Users/rodrigo/Library/Android/sdk
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-27, build-tools 27.0.3
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b01)
    • All Android licenses accepted.

[✓] iOS toolchain - develop for iOS devices (Xcode 9.2)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 9.2, Build version 9C40b
    • ios-deploy 1.9.2
    • CocoaPods version 1.5.3

[✓] Android Studio (version 3.1)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 26.0.1
    • Dart plugin version 173.4700
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b01)

[!] IntelliJ IDEA Community Edition (version 2018.1.1)
    • IntelliJ at /Applications/IntelliJ IDEA CE.app
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
    • For information about installing plugins, see
      https://flutter.io/intellij-setup/#installing-the-plugins

[!] VS Code (version 1.25.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter

[✓] Connected devices (1 available)
    • SM G920F • 02157df2bc245b2a • android-arm64 • Android 7.0 (API 24)

! Doctor found issues in 2 categories."><pre lang="LOG" class="notranslate"><code class="notranslate">flutter doctor -v
[✓] Flutter (Channel master, v0.5.8-pre.152, on Mac OS X 10.12.6 16G1510, locale fr-FR)
    • Flutter version 0.5.8-pre.152 at /Users/rodrigo/Libs/flutter
    • Framework revision 8b05629658 (3 days ago), 2018-07-26 19:12:04 -0700
    • Engine revision 6f7bdc9fab
    • Dart version 2.0.0-dev.69.3.flutter-937ee2e8ca

[✓] Android toolchain - develop for Android devices (Android SDK 27.0.3)
    • Android SDK at /Users/rodrigo/Library/Android/sdk
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-27, build-tools 27.0.3
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b01)
    • All Android licenses accepted.

[✓] iOS toolchain - develop for iOS devices (Xcode 9.2)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 9.2, Build version 9C40b
    • ios-deploy 1.9.2
    • CocoaPods version 1.5.3

[✓] Android Studio (version 3.1)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 26.0.1
    • Dart plugin version 173.4700
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b01)

[!] IntelliJ IDEA Community Edition (version 2018.1.1)
    • IntelliJ at /Applications/IntelliJ IDEA CE.app
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
    • For information about installing plugins, see
      https://flutter.io/intellij-setup/#installing-the-plugins

[!] VS Code (version 1.25.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter

[✓] Connected devices (1 available)
    • SM G920F • 02157df2bc245b2a • android-arm64 • Android 7.0 (API 24)

! Doctor found issues in 2 categories.
</code></pre></div>
<p dir="auto">Video player widget is not sometimes displayed in an application that essentially follows <a href="https://github.com/flutter/plugins/tree/master/packages/video_player#example">the example code</a> for VideoPlayer plugin.</p>
<p dir="auto">Sequence of events:</p>
<ol dir="auto">
<li>VideoPlayerController is created asynchronously.</li>
<li>The widgets builds. But since the controller has not yet finished asynchronous initialization at this point, VideoPlayer widget <a href="https://github.com/flutter/plugins/blob/f98d0d010ea38bb68f2d39afc8c2be3a610b8b97/packages/video_player/lib/video_player.dart#L430">renders to nothing</a>.</li>
<li>VideoPlayerController finishes initialization, but VideoPlayer wants to hear nothing of it. It keeps showing a blank Container.</li>
</ol>
<p dir="auto">Unfortunately, making VideoPlayer stateful and making it rebuild itself once the controller becomes initialized doesn't immediately help.</p>
<p dir="auto">Sequence of events:</p>
<ol dir="auto">
<li>Dart VideoPlayerController is created: <a href="https://github.com/flutter/plugins/tree/master/packages/video_player#example">https://github.com/flutter/plugins/tree/master/packages/video_player#example</a></li>
<li>It <a href="https://github.com/flutter/plugins/blob/f98d0d010ea38bb68f2d39afc8c2be3a610b8b97/packages/video_player/lib/video_player.dart#L203">calls</a> plugin "create" method asynchronously.</li>
<li>VideoPlayerPlugin <a href="https://github.com/flutter/plugins/blob/f98d0d010ea38bb68f2d39afc8c2be3a610b8b97/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java#L98">creates</a> ExoPlayer.</li>
<li>Exoplayer initializes quickly and calls <a href="https://github.com/flutter/plugins/blob/f98d0d010ea38bb68f2d39afc8c2be3a610b8b97/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java#L161">onPlayerStateChanged</a></li>
<li>VideoPlayerPlugin tries to send initialized event to Dart, but there is <a href="https://github.com/flutter/plugins/blob/f98d0d010ea38bb68f2d39afc8c2be3a610b8b97/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java#L215">nothing listening</a>.</li>
<li>Dart VideoPlayerController <a href="https://github.com/flutter/plugins/blob/f98d0d010ea38bb68f2d39afc8c2be3a610b8b97/packages/video_player/lib/video_player.dart#L257">starts listening</a> to the events on the channel.</li>
</ol>
<p dir="auto">But it's too late. It missed the initialized event. So...</p>
<ol start="8" dir="auto">
<li>The listener is not invoked and VideoPlayer is never rebuilt with the correct texture.</li>
</ol>