5

I am using the flutter video_player package to play a short video file using in my application. I inspired from the flutter cookbook: Play and pause a video.

I would like to allow the user to tap on the video to restart it from beginning. So I wrapped the VideoPlayer with a GestureDetector.

I currently have the following code:

class MyVideoPlayer extends StatefulWidget {
  final File videoFile;

  MyVideoPlayer({Key key, this.videoFile}) : super(key: key);

  @override
  _MyVideoPlayerState createState() => _MyVideoPlayerState();
}

class _MyVideoPlayerState extends State<MyVideoPlayer> {
  VideoPlayerController _controller;
  Future<void> _initializeVideoPlayerFuture;

  @override
  void initState() {
    _controller = VideoPlayerController.file(widget.videoFile);
    _initializeVideoPlayerFuture = _controller.initialize();
    super.initState();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _initializeVideoPlayerFuture,
      builder: (context, snapshot) {
        print(snapshot.connectionState);
        if (snapshot.connectionState == ConnectionState.done) {
          // Play video once it's loaded
          _controller.play();

          return AspectRatio(
            aspectRatio: _controller.value.aspectRatio,
            child: GestureDetector(
              onTap: () async {
                await _controller.seekTo(Duration.zero);
                _controller.play();
              },
              child: VideoPlayer(_controller),
            ),
          );
        } else {
          return CircularProgressIndicator();
        }
      },
    );
  }
}

The video plays well once the video file is loaded (once the connection state passed to done), however, when I try to tap on the video to replay it a second time, it doesn't replay the video from start. The audio starts playing again, but video doesn't restart playing. Any idea?

EDIT 1

Following @marcosboaventura suggestion, I tried to wrap the calls in a setState to trigger the build method again:

return AspectRatio(
  aspectRatio: _controller.value.aspectRatio,
  child: GestureDetector(
    onTap: () async {
      await _controller.seekTo(Duration.zero);
      setState(() {
        _controller.play();
      });
    },
    child: VideoPlayer(_controller),
  ),
);

But still the video doesn't replay, only the sound. Any other idea?

skuallpa
  • 1,147
  • 3
  • 13
  • 28

4 Answers4

7

I finally found a solution to my issue by calling initialize() again on the controller on the tap event if the video is no longer playing (i.e. the video finished already).

return AspectRatio(
  aspectRatio: _controller.value.aspectRatio,
  child: GestureDetector(
    onTap: () {
      if (!_controller.value.isPlaying) {
         setState(() {});
         _controller.initialize();
      }
    },
    child: VideoPlayer(_controller),
  ),
);
Maz341
  • 2,232
  • 15
  • 20
skuallpa
  • 1,147
  • 3
  • 13
  • 28
2

You need to rebuild the VideoPlayer if you changes anything in video playback. The most simple solution to your case is just fire build method again with a setState call.

/// ... after some code
child: GestureDetector(
          onTap: () async {
            await _controller.seekTo(Duration.zero);
            setState( () {
              _controller.play();
            } );
          },
          child: VideoPlayer(_controller),
        ),
Marcos Boaventura
  • 4,641
  • 1
  • 20
  • 27
  • I tried wrapping the call to reset the position to zero and replay the video in the setState. However, the following exception is thrown: The setState() method on _MyVideoPlayerState was called with a closure or method that returned a Future. Maybe it is marked as "async". I edited my question with more details about this trial. – skuallpa Jan 08 '20 at 09:01
  • See the edited code. async keyword must be in onTap callback not in setState callback. Sorry my bad! One more thing if the video is playing and user touch over your GestureDetector you don't need to call controller.play() again. Just seekTo beginning and setState. – Marcos Boaventura Jan 08 '20 at 12:33
  • Thx, I tried that as well (see the edited code) above, but still only the sounds is playing back, not the video – skuallpa Jan 08 '20 at 14:35
  • if you're testing it on iOS simulator you will not be able to see video just the audio will play. – Marcos Boaventura Jan 08 '20 at 14:46
  • I'm aware of that, I'm testing on a real device. The video only plays the first time, once loaded. – skuallpa Jan 08 '20 at 15:42
1

I solve this problem in this way.

GestureDetector(
    onTap() {  
    if(_controller.value.position==_controller.value.duration){
            _controller.initialize();  
       }
     }
)

_controller.value.duration store the video duration, _controller.value.position store the actual position of the video and if the video reaches to the end the _controller.value.position will be equal by _controller.value.duration.

0

you can do something like this onTap

/// get the duration of the video
 final duration = await _controller.position;

/// check if video has ended
 if (duration.inSeconds ==_controller.value.duration.inSeconds) {
 /// restart the video by setting current position to 0
    _controller.seekTo(Duration.zero);
 } else {
    _controller.value.isPlaying
        ? _controller.pause()
        : _controller.play();
 }
Mahesh Jamdade
  • 17,235
  • 8
  • 110
  • 131