使用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'), ), ], ); } ), ), ] ), ); } }
问题原因
- 重复创建计时器:视频播放期间,
_controller的监听器会被频繁触发(比如进度更新、播放状态微调等),每次触发都会创建新的Timer.periodic,多个计时器同时运行导致watchTime被多次累加。 - 重复递增数值:代码中
watchTime++执行了两次(回调里一次,打印语句里一次),每次计时器触发都会让数值加2。 - 未处理计时器初始化状态:视频未播放时
_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
相关产品推荐
相关产品推荐

