Blame view

lib/pages/video/lookvideo/widgets/video_widget.dart 8.29 KB
119ba920   liangchengyou   feat:视频播放器
1
  import 'package:common_utils/common_utils.dart';
119ba920   liangchengyou   feat:视频播放器
2
  import 'package:flutter/material.dart';
46675a89   吴启风   feat:过渡页-视频环节
3
  import 'package:flutter_bloc/flutter_bloc.dart';
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
4
  import 'package:flutter_screenutil/flutter_screenutil.dart';
119ba920   liangchengyou   feat:视频播放器
5
  import 'package:video_player/video_player.dart';
91fe517a   liangchengyou   feat:看视频功能开发
6
  import 'package:wow_english/common/extension/string_extension.dart';
46675a89   吴启风   feat:过渡页-视频环节
7
  import 'package:wow_english/pages/video/lookvideo/bloc/look_video_bloc.dart';
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
8
  import 'package:wow_english/route/route.dart';
4b358e22   liangchengyou   feat:调整文件结构
9
  
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
10
11
  import '../../../section/subsection/base_section/event.dart';
  import '../../../section/subsection/base_section/state.dart';
4b358e22   liangchengyou   feat:调整文件结构
12
  import 'video_opera_widget.dart';
119ba920   liangchengyou   feat:视频播放器
13
14
  
  class VideoWidget extends StatefulWidget {
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
15
16
17
18
19
    const VideoWidget({super.key,
      this.videoUrl = '',
      this.typeTitle,
      this.courseLessonId = '',
      this.isTopic = false});
119ba920   liangchengyou   feat:视频播放器
20
21
  
    final String videoUrl;
842b7132   liangchengyou   feat:磨耳朵/练习页面调整
22
    final String? typeTitle;
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
23
    final String courseLessonId;
46675a89   吴启风   feat:过渡页-视频环节
24
    final bool isTopic;
119ba920   liangchengyou   feat:视频播放器
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  
    @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');
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
42
43
44
45
      String minutes = duration.inMinutes.remainder(60).toString().padLeft(
          2, '0');
      String seconds = duration.inSeconds.remainder(60).toString().padLeft(
          2, '0');
119ba920   liangchengyou   feat:视频播放器
46
47
48
49
50
      return "$hours:$minutes:$seconds";
    }
  
    void _addListener() {
      _controller!.addListener(() {
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
51
        if (_controller!.value.isInitialized) {
119ba920   liangchengyou   feat:视频播放器
52
53
          if (_controller!.value.isPlaying) {
            setState(() {
66a7e3e7   吴启风   feat:退出课堂和结束课堂优化
54
              double currentSecond = getCurrentPositionSeconds().toDouble();
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
55
56
57
              int totalSecond =
                  _controller!.value.duration.inMinutes.remainder(60) * 60 +
                      _controller!.value.duration.inSeconds.remainder(60);
119ba920   liangchengyou   feat:视频播放器
58
              _currentTime = formatDuration(_controller!.value.position);
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
59
              _playDegree = currentSecond / totalSecond;
3840b7fe   liangchengyou   feat:更新设置页面
60
61
62
              if (_playDegree > 1.0) {
                _playDegree = 1.0;
              }
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
63
              if (_playDegree < 0) {
3840b7fe   liangchengyou   feat:更新设置页面
64
65
                _playDegree = 0.0;
              }
119ba920   liangchengyou   feat:视频播放器
66
            });
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
67
68
69
70
71
72
73
74
          } 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(() {
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
75
76
              popPage(data: {
                'courseLessonId': widget.courseLessonId,
66a7e3e7   吴启风   feat:退出课堂和结束课堂优化
77
78
                'currentTime': getCurrentPositionSeconds(),
                'isCompleted': true,
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
79
80
81
82
83
84
                'nextSection': widget.isTopic
              });
            } as VoidCallback,
                againSectionTap: (() {
                  lookVideoBloc.add(SectionAgainEvent());
                }), context: context);
119ba920   liangchengyou   feat:视频播放器
85
86
87
88
89
90
          }
        }
      });
    }
  
    //开始倒计时
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
91
92
93
    void startTimer() {
      if (timerUtil == null) {
        timerUtil = TimerUtil(mInterval: 1000, mTotalTime: 1000 * 10);
119ba920   liangchengyou   feat:视频播放器
94
95
        timerUtil!.setOnTimerTickCallback((int tick) {
          double currentTick = tick / 1000;
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
96
97
          if (currentTick.toInt() == 0) {
            //倒计时结束
119ba920   liangchengyou   feat:视频播放器
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
            setState(() {
              _hiddenTipView = true;
            });
            timerUtil!.cancel();
            timerUtil = null;
          }
        });
        timerUtil!.startCountDown();
      }
    }
  
    //取消倒计时
    void cancelTimer() {
      timerUtil!.cancel();
      timerUtil = null;
    }
  
91fe517a   liangchengyou   feat:看视频功能开发
115
116
    void actionType(OperationType type) async {
      if (type == OperationType.back) {
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
117
118
119
120
121
122
123
        if (widget.courseLessonId.isEmpty) {
          popPage();
        } else {
          if (_controller == null) {
            popPage();
            return;
          }
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
124
125
          popPage(data: {
            'courseLessonId': widget.courseLessonId,
66a7e3e7   吴启风   feat:退出课堂和结束课堂优化
126
            'currentTime': getCurrentPositionSeconds(),
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
127
          });
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
128
        }
91fe517a   liangchengyou   feat:看视频功能开发
129
130
131
132
133
134
      } else if (type == OperationType.playState) {
        if (_controller!.value.isPlaying) {
          _controller!.pause();
        } else {
          _controller!.play();
        }
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
135
        setState(() {});
91fe517a   liangchengyou   feat:看视频功能开发
136
137
138
      }
    }
  
119ba920   liangchengyou   feat:视频播放器
139
140
141
    @override
    void initState() {
      super.initState();
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
142
143
      Uri uri = Uri.parse(widget.videoUrl);
      _controller = VideoPlayerController.networkUrl(uri)
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
144
        ..initialize().then((_) {
119ba920   liangchengyou   feat:视频播放器
145
146
147
148
          startTimer();
          setState(() {
            _currentTime = formatDuration(_controller!.value.position);
            _totalTime = formatDuration(_controller!.value.duration);
46675a89   吴启风   feat:过渡页-视频环节
149
            _controller!.setLooping(!widget.isTopic);
119ba920   liangchengyou   feat:视频播放器
150
151
152
153
154
155
156
157
158
            _controller!.setVolume(100);
            _controller!.play();
          });
          _addListener();
        });
    }
  
    @override
    Widget build(BuildContext context) {
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
159
160
161
162
163
164
      return BlocListener<LookVideoBloc, BaseSectionState>(
        listener: (context, state) async {
          if (state is SectionAgainState) {
            await _controller?.seekTo(Duration.zero);
            _controller?.play();
          }
119ba920   liangchengyou   feat:视频播放器
165
        },
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
166
167
        child: GestureDetector(
          onTap: () {
91fe517a   liangchengyou   feat:看视频功能开发
168
            setState(() {
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
169
170
171
172
173
174
175
176
              _hiddenTipView = !_hiddenTipView;
              if (!_hiddenTipView) {
                startTimer();
              } else {
                if (timerUtil!.isActive()) {
                  cancelTimer();
                }
              }
91fe517a   liangchengyou   feat:看视频功能开发
177
            });
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
          },
          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!),
                  ),
91fe517a   liangchengyou   feat:看视频功能开发
201
                ),
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
                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));
                    },
91fe517a   liangchengyou   feat:看视频功能开发
224
                  ),
119ba920   liangchengyou   feat:视频播放器
225
                ),
aa0d2360   吴启风   feat:过渡页-视频环节(再来一次)
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
                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,
66a7e3e7   吴启风   feat:退出课堂和结束课堂优化
243
244
245
246
247
              child: const CircularProgressIndicator(),
              // Text(
              //   '视频加载中....',
              //   style: TextStyle(fontSize: 20.sp, color: Colors.black),
              // ),
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
248
            ),
119ba920   liangchengyou   feat:视频播放器
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
          ),
        ),
      );
    }
  
    @override
    void dispose() {
      _controller?.dispose();
      _controller?.removeListener(() {});
      if (timerUtil != null) {
        timerUtil!.cancel();
        timerUtil = null;
      }
      super.dispose();
    }
66a7e3e7   吴启风   feat:退出课堂和结束课堂优化
264
265
266
267
268
269
  
    ///获取当前进度秒数
    int getCurrentPositionSeconds() {
      return (_controller!.value.position.inMinutes.remainder(60) * 60 +
          _controller!.value.position.inSeconds.remainder(60));
    }
119ba920   liangchengyou   feat:视频播放器
270
  }