import 'package:common_utils/common_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:video_player/video_player.dart'; import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/pages/video/lookvideo/bloc/look_video_bloc.dart'; import 'package:wow_english/route/route.dart'; import '../../../section/subsection/base_section/event.dart'; import '../../../section/subsection/base_section/state.dart'; import 'video_opera_widget.dart'; class VideoWidget extends StatefulWidget { const VideoWidget({super.key, this.videoUrl = '', this.typeTitle, this.courseLessonId = '', this.isTopic = false}); final String videoUrl; final String? typeTitle; final String courseLessonId; final bool isTopic; @override State createState() { return _VideoWidgetState(); } } class _VideoWidgetState extends State { VideoPlayerController? _controller; String _currentTime = '00:00'; String _totalTime = '00:00'; double _playDegree = 0.0; bool _hiddenTipView = false; TimerUtil? timerUtil; String formatDuration(Duration duration) { String hours = duration.inHours.toString().padLeft(2, '0'); String minutes = duration.inMinutes.remainder(60).toString().padLeft( 2, '0'); String seconds = duration.inSeconds.remainder(60).toString().padLeft( 2, '0'); return "$hours:$minutes:$seconds"; } void _addListener() { _controller!.addListener(() { if (_controller!.value.isInitialized) { if (_controller!.value.isPlaying) { setState(() { double currentSecond = getCurrentPositionSeconds().toDouble(); int totalSecond = _controller!.value.duration.inMinutes.remainder(60) * 60 + _controller!.value.duration.inSeconds.remainder(60); _currentTime = formatDuration(_controller!.value.position); _playDegree = currentSecond / totalSecond; if (_playDegree > 1.0) { _playDegree = 1.0; } if (_playDegree < 0) { _playDegree = 0.0; } }); } else if (widget.isTopic && //受限于video_player库没有唯一的播放完成状态标识,发现isBuffering=false的时候暂时是安全唯一的 _controller?.value.isBuffering == false && _controller!.value.isCompleted && _controller!.value.position.inSeconds == _controller!.value.duration.inSeconds) { final lookVideoBloc = context.read(); lookVideoBloc.sectionComplete(() { popPage(data: { 'courseLessonId': widget.courseLessonId, 'currentTime': getCurrentPositionSeconds(), 'isCompleted': true, 'nextSection': widget.isTopic }); } as VoidCallback, againSectionTap: (() { lookVideoBloc.add(SectionAgainEvent()); }), context: context); } } }); } //开始倒计时 void startTimer() { if (timerUtil == null) { timerUtil = TimerUtil(mInterval: 1000, mTotalTime: 1000 * 10); timerUtil!.setOnTimerTickCallback((int tick) { double currentTick = tick / 1000; if (currentTick.toInt() == 0) { //倒计时结束 setState(() { _hiddenTipView = true; }); timerUtil!.cancel(); timerUtil = null; } }); timerUtil!.startCountDown(); } } //取消倒计时 void cancelTimer() { timerUtil!.cancel(); timerUtil = null; } void actionType(OperationType type) async { if (type == OperationType.back) { if (widget.courseLessonId.isEmpty) { popPage(); } else { if (_controller == null) { popPage(); return; } popPage(data: { 'courseLessonId': widget.courseLessonId, 'currentTime': getCurrentPositionSeconds(), }); } } else if (type == OperationType.playState) { if (_controller!.value.isPlaying) { _controller!.pause(); } else { _controller!.play(); } setState(() {}); } } @override void initState() { super.initState(); Uri uri = Uri.parse(widget.videoUrl); _controller = VideoPlayerController.networkUrl(uri) ..initialize().then((_) { startTimer(); setState(() { _currentTime = formatDuration(_controller!.value.position); _totalTime = formatDuration(_controller!.value.duration); _controller!.setLooping(!widget.isTopic); _controller!.setVolume(100); _controller!.play(); }); _addListener(); }); } @override Widget build(BuildContext context) { return BlocListener( listener: (context, state) async { if (state is SectionAgainState) { await _controller?.seekTo(Duration.zero); _controller?.play(); } }, child: GestureDetector( onTap: () { setState(() { _hiddenTipView = !_hiddenTipView; if (!_hiddenTipView) { startTimer(); } else { if (timerUtil!.isActive()) { cancelTimer(); } } }); }, onDoubleTap: () { if (_controller!.value.isInitialized) { if (_controller!.value.isPlaying) { _controller!.pause(); } else { _controller!.play(); } setState(() {}); } }, child: Center( child: _controller!.value.isInitialized ? Stack( alignment: Alignment.center, children: [ SizedBox( height: double.infinity, width: double.infinity, child: AspectRatio( aspectRatio: _controller!.value.aspectRatio, child: VideoPlayer(_controller!), ), ), Offstage( offstage: _hiddenTipView, child: VideoOperaWidget( title: widget.typeTitle ?? 'song', degree: _playDegree, totalTime: _totalTime, currentTime: _currentTime, isPlay: _controller!.value.isPlaying, actionEvent: (OperationType type) { actionType(type); }, sliderChangeEvent: (double degree) { int totalSecond = _controller! .value.duration.inMinutes .remainder(60) * 60 + _controller!.value.duration.inSeconds .remainder(60); int positionSecond = (totalSecond * degree).toInt(); _controller! .seekTo(Duration(seconds: positionSecond)); }, ), ), Offstage( offstage: _controller!.value.isPlaying, child: IconButton( onPressed: () { _controller!.play(); }, icon: Image.asset( 'video_stop'.assetPng, width: 70.w, height: 70.h, ), ), ) ], ) : Container( color: Colors.white, child: const CircularProgressIndicator(), // Text( // '视频加载中....', // style: TextStyle(fontSize: 20.sp, color: Colors.black), // ), ), ), ), ); } @override void dispose() { _controller?.dispose(); _controller?.removeListener(() {}); if (timerUtil != null) { timerUtil!.cancel(); timerUtil = null; } super.dispose(); } ///获取当前进度秒数 int getCurrentPositionSeconds() { return (_controller!.value.position.inMinutes.remainder(60) * 60 + _controller!.value.position.inSeconds.remainder(60)); } }