266 lines
7.9 KiB
Dart
Executable File
266 lines
7.9 KiB
Dart
Executable File
import 'dart:async';
|
||
|
||
import 'package:auto_orientation/auto_orientation.dart';
|
||
import 'package:common_utils/common_utils.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter/services.dart';
|
||
import 'package:video_player/video_player.dart';
|
||
|
||
import 'controller_widget.dart';
|
||
import 'video_player_slider.dart';
|
||
|
||
class VideoPlayerControl extends StatefulWidget {
|
||
const VideoPlayerControl({
|
||
Key? key,
|
||
}) : super(key: key);
|
||
|
||
@override
|
||
VideoPlayerControlState createState() => VideoPlayerControlState();
|
||
}
|
||
|
||
class VideoPlayerControlState extends State<VideoPlayerControl> {
|
||
VideoPlayerController get controller => ControllerWidget.of(context)!.controller;
|
||
bool get videoInit => ControllerWidget.of(context)!.videoInit;
|
||
String get title=>ControllerWidget.of(context)!.title;
|
||
// 记录video播放进度
|
||
Duration _position = const Duration(seconds: 0);
|
||
Duration _totalDuration = const Duration(seconds: 0);
|
||
late Timer _timer; // 计时器,用于延迟隐藏控件ui
|
||
bool _hidePlayControl = true; // 控制是否隐藏控件ui
|
||
double _playControlOpacity = 0; // 通过透明度动画显示/隐藏控件ui
|
||
/// 记录是否全屏
|
||
bool get _isFullScreen =>
|
||
MediaQuery.of(context).orientation == Orientation.landscape;
|
||
|
||
@override
|
||
void dispose() {
|
||
super.dispose();
|
||
if (_timer != null) {
|
||
_timer.cancel();
|
||
}
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return GestureDetector(
|
||
onDoubleTap: _playOrPause,
|
||
onTap: _togglePlayControl,
|
||
child: Container(
|
||
width: double.infinity,
|
||
height: double.infinity,
|
||
color: Colors.transparent,
|
||
child: WillPopScope(
|
||
child: Offstage(
|
||
offstage: _hidePlayControl,
|
||
child: AnimatedOpacity(
|
||
// 加入透明度动画
|
||
opacity: _playControlOpacity,
|
||
duration: const Duration(milliseconds: 300),
|
||
child: Column(
|
||
children: <Widget>[_top(), _middle(), _bottom(context)],
|
||
),
|
||
),
|
||
),
|
||
onWillPop: _onWillPop,
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
// 拦截返回键
|
||
Future<bool> _onWillPop() async {
|
||
if (_isFullScreen) {
|
||
_toggleFullScreen();
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// 供父组件调用刷新页面,减少父组件的build
|
||
void setPosition({position, totalDuration}) {
|
||
setState(() {
|
||
_position = position;
|
||
_totalDuration = totalDuration;
|
||
});
|
||
}
|
||
|
||
Widget _bottom(BuildContext context) {
|
||
return Container(
|
||
// 底部控件的容器
|
||
width: double.infinity,
|
||
height: 40,
|
||
decoration: const BoxDecoration(
|
||
gradient: LinearGradient(
|
||
// 来点黑色到透明的渐变优雅一下
|
||
begin: Alignment.bottomCenter,
|
||
end: Alignment.topCenter,
|
||
colors: [Color.fromRGBO(0, 0, 0, .7), Color.fromRGBO(0, 0, 0, .1)],
|
||
),
|
||
),
|
||
child: Row(
|
||
// 加载完成时才渲染,flex布局
|
||
children: <Widget>[
|
||
IconButton(
|
||
// 播放按钮
|
||
padding: EdgeInsets.zero,
|
||
iconSize: 26,
|
||
icon: Icon(
|
||
// 根据控制器动态变化播放图标还是暂停
|
||
controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
|
||
color: Colors.white,
|
||
),
|
||
onPressed: _playOrPause,
|
||
),
|
||
Expanded(
|
||
// 相当于前端的flex: 1
|
||
child: VideoPlayerSlider(
|
||
startPlayControlTimer: _startPlayControlTimer,
|
||
timer: _timer,
|
||
),
|
||
),
|
||
Container(
|
||
// 播放时间
|
||
margin: const EdgeInsets.only(left: 10),
|
||
child: Text(
|
||
'${DateUtil.formatDateMs(
|
||
_position!.inMilliseconds,
|
||
format: 'mm:ss',
|
||
)}/${DateUtil.formatDateMs(
|
||
_totalDuration!.inMilliseconds,
|
||
format: 'mm:ss',
|
||
)}',
|
||
style: const TextStyle(color: Colors.white),
|
||
),
|
||
),
|
||
IconButton(
|
||
// 全屏/横屏按钮
|
||
padding: EdgeInsets.zero,
|
||
iconSize: 26,
|
||
icon: Icon(
|
||
// 根据当前屏幕方向切换图标
|
||
_isFullScreen ? Icons.fullscreen_exit : Icons.fullscreen,
|
||
color: Colors.white,
|
||
),
|
||
onPressed: _toggleFullScreen,
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _middle() {
|
||
return Expanded(
|
||
child: Container(),
|
||
);
|
||
}
|
||
|
||
Widget _top() {
|
||
return Container(
|
||
width: double.infinity,
|
||
height: 40,
|
||
decoration: const BoxDecoration(
|
||
gradient: LinearGradient(
|
||
// 来点黑色到透明的渐变优雅一下
|
||
begin: Alignment.bottomCenter,
|
||
end: Alignment.topCenter,
|
||
colors: [Color.fromRGBO(0, 0, 0, .7), Color.fromRGBO(0, 0, 0, .1)],
|
||
),
|
||
),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: <Widget>[
|
||
//在最上层或者不是横屏则隐藏按钮
|
||
if (ModalRoute.of(context)!.isFirst && !_isFullScreen) Container() else IconButton(
|
||
icon: const Icon(
|
||
Icons.arrow_back,
|
||
color: Colors.white,
|
||
),
|
||
onPressed: backPress),
|
||
Text(
|
||
title,
|
||
style: const TextStyle(color: Colors.white),
|
||
),
|
||
//在最上层或者不是横屏则隐藏按钮
|
||
if (ModalRoute.of(context)!.isFirst && !_isFullScreen) Container() else IconButton(
|
||
icon: const Icon(
|
||
Icons.arrow_back,
|
||
color: Colors.transparent,
|
||
),
|
||
onPressed: () {},
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
void backPress() {
|
||
// 如果是全屏,点击返回键则关闭全屏,如果不是,则系统返回键
|
||
if (_isFullScreen) {
|
||
_toggleFullScreen();
|
||
} else if(ModalRoute.of(context)!.isFirst) {
|
||
SystemNavigator.pop();
|
||
}else{
|
||
Navigator.pop(context);
|
||
}
|
||
}
|
||
|
||
void _playOrPause() {
|
||
/// 同样的,点击动态播放或者暂停
|
||
if (videoInit) {
|
||
controller.value.isPlaying ? controller.pause() : controller.play();
|
||
_startPlayControlTimer(); // 操作控件后,重置延迟隐藏控件的timer
|
||
}
|
||
}
|
||
|
||
void _togglePlayControl() {
|
||
setState(() {
|
||
if (_hidePlayControl) {
|
||
/// 如果隐藏则显示
|
||
_hidePlayControl = false;
|
||
_playControlOpacity = 1;
|
||
_startPlayControlTimer(); // 开始计时器,计时后隐藏
|
||
} else {
|
||
/// 如果显示就隐藏
|
||
if (_timer != null) _timer.cancel(); // 有计时器先移除计时器
|
||
_playControlOpacity = 0;
|
||
Future.delayed(const Duration(milliseconds: 500)).whenComplete(() {
|
||
_hidePlayControl = true; // 延迟500ms(透明度动画结束)后,隐藏
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
void _startPlayControlTimer() {
|
||
/// 计时器,用法和前端js的大同小异
|
||
if (_timer != null) _timer.cancel();
|
||
_timer = Timer(const Duration(seconds: 3), () {
|
||
/// 延迟3s后隐藏
|
||
setState(() {
|
||
_playControlOpacity = 0;
|
||
Future.delayed(const Duration(milliseconds: 500)).whenComplete(() {
|
||
_hidePlayControl = true;
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
void _toggleFullScreen() {
|
||
setState(() {
|
||
if (_isFullScreen) {
|
||
/// 如果是全屏就切换竖屏
|
||
AutoOrientation.portraitAutoMode();
|
||
|
||
///显示状态栏,与底部虚拟操作按钮
|
||
SystemChrome.setEnabledSystemUIMode(
|
||
SystemUiMode.manual, overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom]);
|
||
} else {
|
||
AutoOrientation.landscapeAutoMode();
|
||
|
||
///关闭状态栏,与底部虚拟操作按钮
|
||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
|
||
}
|
||
_startPlayControlTimer(); // 操作完控件开始计时隐藏
|
||
});
|
||
}
|
||
}
|