You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用youtube_player_flutter统计视频观看时长计时异常求助

问题描述

我使用youtube_player_flutter加载YouTube视频,尝试添加计时器统计用户的视频观看时长。设置了watchTime变量,期望在_controller.value.isPlaying为true时每秒递增1,但实际输出不符合预期,数值增长过快,并非每秒加1。

原代码

class _WatchVideoState extends State<WatchVideo> {

  final _pageController = PageController();
  late YoutubePlayerController _controller;


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

  int watchTime = 0 ;
  late Timer _timer;


  @override
  void initState() {
    _controller = YoutubePlayerController(
        initialVideoId: widget.videoID,
        flags: const YoutubePlayerFlags(
          autoPlay: false,
    )
    )..addListener(() {
      if (mounted && _controller.value.isPlaying){
          _timer = Timer.periodic(const Duration(seconds: 1), (_) {
            watchTime++;
            print('The Watch time is:  ${watchTime++}');
          });

      }
      else {
        _timer.cancel();
        print('Video is Stopped${watchTime}');
      }
    });
    super.initState();
  }



  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: true,
      appBar: AppBar(
        title: Text(widget.courseLessons.week.toString()),
        backgroundColor: pallet_orange,
      ),
      body:
          Column(
            children:[
              Expanded(
                child: PageView.builder(
                    physics: const NeverScrollableScrollPhysics(),
                  controller: _pageController,
                  itemCount: 10,
                  itemBuilder: (context, index) {
                    return Column(
                      children: [
                        Container(
                          decoration: const BoxDecoration(
                            color: Colors.grey,
                          ),
                          child: Row(
                            mainAxisAlignment: MainAxisAlignment.spaceAround,
                            children: [
                              ElevatedButton(style: ElevatedButton.styleFrom(
                                  backgroundColor: pallet_orange),
                                  onPressed: () {

                                  },
                                  child: Icon(Icons.arrow_back_ios_new,)),
                              SizedBox(width: 10,),
                              Flexible(child: Text(widget.courseLessons
                                  .lessonTitle.toString(), overflow: TextOverflow
                                  .ellipsis,)),
                              SizedBox(width: 10,),
                              ElevatedButton(style: ElevatedButton.styleFrom(
                                  backgroundColor: pallet_orange),
                                  onPressed: () {
                                  },
                                  child: Icon(Icons.arrow_forward_ios,)),
                            ],

                          ),
                        ),
                        SizedBox(height: 20,),
                          YoutubePlayer(
                                progressColors: const ProgressBarColors(
                                    playedColor: pallet_orange,
                                    handleColor: pallet_orange),
                                controller: _controller,
                                showVideoProgressIndicator: true,
                                onReady: () => print('Ready'),
                          ),
                      ],
                    );
                  }
                ),
              ),

            ]
          ),
    );
  }
}

问题原因

  1. 重复创建计时器:视频播放期间,_controller的监听器会被频繁触发(比如进度更新、播放状态微调等),每次触发都会创建新的Timer.periodic,多个计时器同时运行导致watchTime被多次累加。
  2. 重复递增数值:代码中watchTime++执行了两次(回调里一次,打印语句里一次),每次计时器触发都会让数值加2。
  3. 未处理计时器初始化状态:视频未播放时_timer未初始化,直接调用_timer.cancel()会触发空指针异常。

修复后的代码

class _WatchVideoState extends State<WatchVideo> {

  final _pageController = PageController();
  late YoutubePlayerController _controller;

  int watchTime = 0 ;
  Timer? _timer; // 改为可空类型,避免未初始化问题
  bool _isTimerRunning = false; // 标记计时器是否已启动

  @override
  void dispose() {
    _timer?.cancel(); // 销毁前先取消计时器
    _controller.dispose();
    super.dispose();
  }

  @override
  void initState() {
    _controller = YoutubePlayerController(
        initialVideoId: widget.videoID,
        flags: const YoutubePlayerFlags(
          autoPlay: false,
        )
    )..addListener(() {
      if (mounted && _controller.value.isPlaying){
        if (!_isTimerRunning) { // 只有计时器未运行时才创建
          _isTimerRunning = true;
          _timer = Timer.periodic(const Duration(seconds: 1), (_) {
            setState(() { // 需要setState更新UI(如果需要显示的话)
              watchTime++;
            });
            print('The Watch time is:  $watchTime'); // 只打印,不再递增
          });
        }
      }
      else {
        if (_isTimerRunning) { // 只有计时器运行时才取消
          _timer?.cancel();
          _isTimerRunning = false;
          print('Video is Stopped: $watchTime');
        }
      }
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: true,
      appBar: AppBar(
        title: Text(widget.courseLessons.week.toString()),
        backgroundColor: pallet_orange,
      ),
      body: Column(
        children:[
          Expanded(
            child: PageView.builder(
                physics: const NeverScrollableScrollPhysics(),
                controller: _pageController,
                itemCount: 10,
                itemBuilder: (context, index) {
                  return Column(
                    children: [
                      Container(
                        decoration: const BoxDecoration(
                          color: Colors.grey,
                        ),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceAround,
                          children: [
                            ElevatedButton(
                              style: ElevatedButton.styleFrom(
                                  backgroundColor: pallet_orange),
                              onPressed: () {},
                              child: const Icon(Icons.arrow_back_ios_new),
                            ),
                            const SizedBox(width: 10,),
                            Flexible(
                              child: Text(
                                widget.courseLessons.lessonTitle.toString(),
                                overflow: TextOverflow.ellipsis,
                              ),
                            ),
                            const SizedBox(width: 10,),
                            ElevatedButton(
                              style: ElevatedButton.styleFrom(
                                  backgroundColor: pallet_orange),
                              onPressed: () {},
                              child: const Icon(Icons.arrow_forward_ios),
                            ),
                          ],
                        ),
                      ),
                      const SizedBox(height: 20,),
                      YoutubePlayer(
                        progressColors: const ProgressBarColors(
                            playedColor: pallet_orange,
                            handleColor: pallet_orange),
                        controller: _controller,
                        showVideoProgressIndicator: true,
                        onReady: () => print('Ready'),
                      ),
                    ],
                  );
                }
            ),
          ),
        ]
      ),
    );
  }
}

关键修复点

  • 将_timer改为可空类型Timer?,避免未初始化时调用cancel()引发异常。
  • 添加_isTimerRunning标记,确保同一时间只有一个计时器在运行,防止重复创建。
  • 移除打印语句中的watchTime++,保证每次计时器回调只让watchTime递增1。
  • 在dispose()中先取消计时器,避免内存泄漏。
  • 若需要在UI上显示watchTime,需在递增时调用setState()更新状态。

内容的提问来源于stack exchange,提问作者Tahir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 03:21:13