Rendezvous-Tokyo

Flutterで動画再生後に次の動画を再生させる方法

Chrome webでデバッグしてる時には正常に動いていたけど、
AndroidとiOSの実機で動かしたら最初の動画が始まる前に次の動画が始まってしまう問題が発生。

結論、動画の終了判定が誤っていた。

期待通り動いたコード

こんな感じ。

import 'package:video_player/video_player.dart';

class HogeMoviePage extends StatefulWidget {
  
  State<HogeMoviePage> createState() => _HogeMovieState();
}

class _HogeMovieState extends State<HogeMoviePage>
    with TickerProviderStateMixin {
  late VideoPlayerController _videoController1;
  late VideoPlayerController _videoController2;
  bool _completed = false;

  
  void initState() {
    super.initState();
    _videoController1 =
        VideoPlayerController.asset('assets/douga1.mp4')
          ..initialize().whenComplete(() {
            setState(() {});
          });
    _videoController1.addListener(() => {
          setState(() {
            if (!_videoController1.value.isBuffering &&
                _videoController1.value.isInitialized &&
                !_videoController1.value.isPlaying &&
                _videoController1.value.position ==
                    _videoController1.value.duration) {
              _videoController1.initialize();
              _videoController1.seekTo(Duration.zero);
              this._completed = true;
              this._videoController2.play();
            }
          })
        });
    _videoController2 =
        VideoPlayerController.asset('assets/douga2.mp4')
          ..initialize().whenComplete(() {
            setState(() {});
          });
  }

  
  void dispose() {
    _videoController1.dispose();
    _videoController2.dispose();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return MaterialApp(
      body: ListView(children: [
              Stack(children: <Widget>[
                FittedBox(
                  fit: BoxFit.cover,
                  child: SizedBox(
                    width: _videoController1.value.size.width,
                    height: _videoController1.value.size.height,
                    child: this._completed
                        ? VideoPlayer(_videoController2)
                        : VideoPlayer(_videoController1),
                  ),
                ),
              ])
            ])
          ...

やったこと

  • 2つの動画は別々のVideoPlayerControllerで定義。
  • initialize().whenComplete(() {})の箇所は特に処理はないのだが、記述あるのとないのではChrome webで挙動が変わったので念の為残している。
  • 動画の終わり判定は 各コントローラーのaddListener(() => {})で記述。

動画の終わり判定コード

ちょっと冗長かもしれないが、下のコードで結果うごいた。

        if (!_videoController1.value.isBuffering &&      // 大事
                _videoController1.value.isInitialized && // 大事
                !_videoController1.value.isPlaying && 
                _videoController1.value.position ==
                    _videoController1.value.duration) {
              _videoController1.initialize();           // 無限ループ対策
              _videoController1.seekTo(Duration.zero);  // 無限ループ対策
              this._completed = true;         // 画面切り替え用
              this._videoController2.play();  // 次の動画再生
            }

ネットで調べると isPlayingposition == duration だけで判定してる例が多くて最初はこの2つでしか判定してなかったが、実機で動かすと動画を再生していないのにposition == durationがTrueになってしまっていた。
それに関してはもうしょうがないと諦め、isBufferingisInitialized を追加して解決した。

判定後の処理で initialize()seekTo(Duration.zero) を記述しないと無限にデバッグログ吐かれて鬱陶しいことになる。

以上。