36

I am very new to Flutter and don't know much about it.

I am using video_player and chewie package in Flutter. I want to send analytics data when video starts and ends.

So, I want to know both timing. how can I detect the video start / end timing?

My code is following.

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

class VideoItem extends StatefulWidget {
  final VideoPlayerController videoPlayerController;
  final bool looping;

  VideoItem({
    @required this.videoPlayerController,
    this.looping,
    Key key,
  }) : super(key: key);

  @override
  _VideoState createState() => _VideoState();

}

class _VideoState extends State<VideoItem> {
  ChewieController _chewieController;

  @override
  void initState() {
    super.initState();

    _chewieController = ChewieController(
      videoPlayerController: widget.videoPlayerController,
      aspectRatio: 1 / 1,
      autoPlay: true,
      autoInitialize: true,
      looping: widget.looping,
      allowFullScreen: false,
      allowMuting: true,
      errorBuilder: (context, errorMessage) {
        return Center(
          child: Text(
            errorMessage,
            style: TextStyle(color: Colors.white),
          ),
        );
      },
    );
  }

...

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(0.0),
      child: Chewie(
        controller: _chewieController,
      ),
    );
  }
}

6 Answers 6

37

Add a Listener function to your videoPlayerController , and inside that function check the the current position of your VideoPlayer :

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

class VideoItem extends StatefulWidget {
  final VideoPlayerController videoPlayerController;
  final bool looping;

  VideoItem({
    @required this.videoPlayerController,
    this.looping,
    Key key,
  }) : super(key: key);

  @override
  _VideoState createState() => _VideoState();

}

class _VideoState extends State<VideoItem> {
  ChewieController _chewieController;

  @override
  void initState() {
    super.initState();

    widget.videoPlayerController.addListener(checkVideo);

    _chewieController = ChewieController(
      videoPlayerController: widget.videoPlayerController,
      aspectRatio: 1 / 1,
      autoPlay: true,
      autoInitialize: true,
      looping: widget.looping,
      allowFullScreen: false,
      allowMuting: true,
      errorBuilder: (context, errorMessage) {
        return Center(
          child: Text(
            errorMessage,
            style: TextStyle(color: Colors.white),
          ),
        );
      },
    );
  }

...

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(0.0),
      child: Chewie(
        controller: _chewieController,
      ),
    );
  }

    void checkVideo(){
    // Implement your calls inside these conditions' bodies : 
    if(videoPlayerController.value.position == Duration(seconds: 0, minutes: 0, hours: 0)) {
      print('video Started');
    }

    if(videoPlayerController.value.position == videoPlayerController.value.duration) {
      print('video Ended');
    }

  }
} 
2
  • checkVideo() only call first time.
    – Samar Ali
    Commented Feb 18, 2020 at 6:39
  • how can I detect seek to changes? is there something I can override or create a listener? Commented Apr 8, 2021 at 11:49
23

What you can do is initialize the video_player's controller and add a custom listener to it. Here's that snippet.

  _videoController = VideoPlayerController.file(File(s));

  _videoController.initialize().then((value) => {
        _videoController.addListener(() {                       //custom Listner
          setState(() {
            if (!_videoController.value.isPlaying &&_videoController.value.initialized &&
                (_videoController.value.duration ==_videoController.value.position)) { //checking the duration and position every time
              setState(() {});
            }
          });
        })
      });

Here I am not sure for how many seconds it hits but it works pretty fine for me.

2
  • Why is there a conditional inside of a setState which has an additional setState inside of it? Tia~! Commented Jan 24, 2023 at 11:19
  • First one is to check if the video has initialized and started properly., another one is for to check if the video is ended Commented Jan 25, 2023 at 5:31
4

We can do the same using flutter_hooks:

@override
  Widget build(BuildContext context) {
    final VideoPlayerController _videoPlayerController =
        VideoPlayerController.network(dataSource);
    final ValueNotifier<bool> _isVideoFinished = useState(false);

    useEffect(() {
      void checkIsFinished() {
        _isVideoFinished.value = _videoPlayerController.value.isInitialized &&
            _videoPlayerController.value.position ==
                _videoPlayerController.value.duration;
      }

      _videoPlayerController.addListener(checkIsFinished);
      return () {
        _videoPlayerController.removeListener(checkIsFinished);
      };
    }, <VideoPlayerController>[_videoPlayerController]);

    return Scaffold(
      resizeToAvoidBottomInset: false,
      body: IntroductionScreen(
        pages: <PageViewModel>[
          PageViewModel(
            title: "",
            image: VideoItem(
              videoPlayerController: _videoPlayerController,
              autoPlay: true,
            ),
            body: "",
            decoration: const PageDecoration(fullScreen: true),
          ),
        ],
        done: const Text("Done", style: TextStyle(fontWeight: FontWeight.w600)),
        showDoneButton: _isVideoFinished.value,
        onDone: onDone,
        showNextButton: false,
      ),
    );
  }

Don't forget to use it in a HookWidget.

2
if (widget.url != null) {
  _controller = VideoPlayerController.network(widget.url)
    ..initialize().then((_) {
      // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
      setState(() {});
      _controller!.addListener(() {
        if (_controller!.value.position == _controller!.value.duration) {
          setState(() {
            hidden = false;
          });
        }
      });
    });
} else if (widget.file != null) {
  _controller = VideoPlayerController.file(widget.file)
    ..initialize().then((_) {
      // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
      setState(() {});
      _controller!.addListener(() {
        if (_controller!.value.position == _controller!.value.duration) {
          setState(() {
            hidden = false;
          });
        }
      });
    });
}
1

New Update! Add code like this try this .. Cheers

controller.addListener(() {
     // checkVideo();
     if (controller.value.isCompleted) {
       startTime();
     }
 });
0
  • After the video controller is initialized add listeners to listen to every movement of the video like play, pause, video duration, etc.

try this code

  • This code worked for me.

  • Use of Provider state management.

1
  • Hello Jenish, welcome to StackOverflow! I believe your answer could be improved if you removed the bulleted list structure and included the actual code using proper code markup. Also, it would be of great help if you included some explanation (or references) to Provider State Management. If you need any guidance on how to properly format your answers (Markdown), please refer to: stackoverflow.com/editing-help. You can find a section named Syntax highlighting for code there. I hope you have a great experience on SO!
    – NickS1
    Commented Apr 27 at 5:10

Not the answer you're looking for? Browse other questions tagged or ask your own question.