video_widget.dart 8.67 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));
  }
}