video_widget.dart 8.29 KB
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<StatefulWidget> createState() {
    return _VideoWidgetState();
  }
}

class _VideoWidgetState extends State<VideoWidget> {
  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>();
          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<LookVideoBloc, BaseSectionState>(
      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));
  }
}