2023-11-18 10:38:13 +08:00
|
|
|
|
import 'package:common_utils/common_utils.dart';
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
2024-08-21 14:12:15 +08:00
|
|
|
|
import 'package:get/get.dart';
|
2023-11-18 10:38:13 +08:00
|
|
|
|
|
|
|
|
|
|
import 'package:video_player/video_player.dart';
|
|
|
|
|
|
|
|
|
|
|
|
import '../after_layout.dart';
|
|
|
|
|
|
import 'controller_widget.dart';
|
|
|
|
|
|
import 'video_player_control.dart';
|
|
|
|
|
|
|
|
|
|
|
|
class VideoPlayerPan extends StatefulWidget {
|
|
|
|
|
|
VideoPlayerPan({
|
|
|
|
|
|
// this.controlKey,
|
|
|
|
|
|
required this.child,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// final GlobalKey<VideoPlayerControlState> controlKey;
|
|
|
|
|
|
final Widget child;
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
_VideoPlayerPanState createState() => _VideoPlayerPanState();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class _VideoPlayerPanState extends State<VideoPlayerPan>
|
|
|
|
|
|
with AfterLayoutMixin<VideoPlayerPan> {
|
|
|
|
|
|
late Offset startPosition; // 起始位置
|
|
|
|
|
|
late double movePan; // 偏移量累计总和
|
|
|
|
|
|
late double layoutWidth; // 组件宽度
|
|
|
|
|
|
late double layoutHeight; // 组件高度
|
|
|
|
|
|
String volumePercentage = ''; // 组件位移描述
|
|
|
|
|
|
double playDialogOpacity = 0.0;
|
|
|
|
|
|
bool allowHorizontal = false; // 是否允许快进
|
2024-08-21 14:12:15 +08:00
|
|
|
|
Duration position = const Duration(seconds: 0); // 当前时间
|
2023-11-18 10:38:13 +08:00
|
|
|
|
double brightness = 0.0; //亮度
|
|
|
|
|
|
bool brightnessOk = false; // 是否允许调节亮度
|
|
|
|
|
|
|
|
|
|
|
|
VideoPlayerController get controller => ControllerWidget.of(context)!.controller;
|
|
|
|
|
|
bool get videoInit => ControllerWidget.of(context)!.videoInit;
|
|
|
|
|
|
String get title=>ControllerWidget.of(context)!.title;
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
void afterFirstLayout(BuildContext context) {
|
|
|
|
|
|
_reset(context);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
void dispose() {
|
|
|
|
|
|
super.dispose();
|
|
|
|
|
|
brightnessOk = false;
|
|
|
|
|
|
allowHorizontal = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
|
return GestureDetector(
|
|
|
|
|
|
onVerticalDragStart: _onVerticalDragStart,
|
|
|
|
|
|
onVerticalDragUpdate: _onVerticalDragUpdate,
|
|
|
|
|
|
onVerticalDragEnd: _onVerticalDragEnd,
|
|
|
|
|
|
onHorizontalDragStart: _onHorizontalDragStart,
|
|
|
|
|
|
onHorizontalDragUpdate: _onHorizontalDragUpdate,
|
|
|
|
|
|
onHorizontalDragEnd: _onHorizontalDragEnd,
|
|
|
|
|
|
child: Container(
|
|
|
|
|
|
child: Stack(
|
|
|
|
|
|
children: <Widget>[
|
|
|
|
|
|
widget.child,
|
|
|
|
|
|
Center(
|
|
|
|
|
|
child: AnimatedOpacity(
|
|
|
|
|
|
opacity: playDialogOpacity,
|
2024-08-21 14:12:15 +08:00
|
|
|
|
duration: const Duration(milliseconds: 500),
|
2023-11-18 10:38:13 +08:00
|
|
|
|
child: Container(
|
2024-08-21 14:12:15 +08:00
|
|
|
|
padding: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 6.0),
|
|
|
|
|
|
decoration: const BoxDecoration(
|
2023-11-18 10:38:13 +08:00
|
|
|
|
color: Colors.black87,
|
|
|
|
|
|
borderRadius: BorderRadius.all(Radius.circular(5.0))),
|
|
|
|
|
|
child: Text(
|
|
|
|
|
|
volumePercentage,
|
2024-08-21 14:12:15 +08:00
|
|
|
|
style: const TextStyle(color: Colors.white, fontSize: 12),
|
2023-11-18 10:38:13 +08:00
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
VideoPlayerControl(
|
|
|
|
|
|
key: ControllerWidget.of(context)!.controlKey,
|
|
|
|
|
|
)
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _onVerticalDragStart(details) async {
|
|
|
|
|
|
_reset(context);
|
|
|
|
|
|
startPosition = details.globalPosition;
|
|
|
|
|
|
if (startPosition.dx < (layoutWidth / 2)) {
|
|
|
|
|
|
/// 左边触摸
|
|
|
|
|
|
// brightness = await Screen.brightness;
|
|
|
|
|
|
brightnessOk = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _onVerticalDragUpdate(details) {
|
|
|
|
|
|
if (!videoInit) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 累计计算偏移量(下滑减少百分比,上滑增加百分比)
|
|
|
|
|
|
movePan += (-details.delta.dy);
|
|
|
|
|
|
if (startPosition.dx < (layoutWidth / 2)) {
|
|
|
|
|
|
/// 左边触摸
|
|
|
|
|
|
if (brightnessOk = true) {
|
|
|
|
|
|
setState(() {
|
2024-08-21 14:12:15 +08:00
|
|
|
|
volumePercentage = '${'亮度'.tr}:${(_setBrightnessValue() * 100).toInt()}%';
|
2023-11-18 10:38:13 +08:00
|
|
|
|
playDialogOpacity = 1.0;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
/// 右边触摸
|
|
|
|
|
|
setState(() {
|
2024-08-21 14:12:15 +08:00
|
|
|
|
volumePercentage = '${'音量'.tr}:${(_setVerticalValue(num: 2) * 100).toInt()}%';
|
2023-11-18 10:38:13 +08:00
|
|
|
|
playDialogOpacity = 1.0;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _onVerticalDragEnd(_) async {
|
|
|
|
|
|
if (!videoInit) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (startPosition.dx < (layoutWidth / 2)) {
|
|
|
|
|
|
if (brightnessOk) {
|
|
|
|
|
|
// await Screen.setBrightness(_setBrightnessValue());
|
|
|
|
|
|
brightnessOk = false;
|
|
|
|
|
|
// 左边触摸
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
playDialogOpacity = 0.0;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 右边触摸
|
|
|
|
|
|
await controller.setVolume(_setVerticalValue());
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
playDialogOpacity = 0.0;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double _setBrightnessValue() {
|
|
|
|
|
|
// 亮度百分控制
|
|
|
|
|
|
double value =
|
|
|
|
|
|
double.parse((movePan / layoutHeight + brightness).toStringAsFixed(2));
|
|
|
|
|
|
if (value >= 1.00) {
|
|
|
|
|
|
value = 1.00;
|
|
|
|
|
|
} else if (value <= 0.00) {
|
|
|
|
|
|
value = 0.00;
|
|
|
|
|
|
}
|
|
|
|
|
|
return value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double _setVerticalValue({int num = 1}) {
|
|
|
|
|
|
// 声音亮度百分控制
|
|
|
|
|
|
double value = double.parse(
|
|
|
|
|
|
(movePan / layoutHeight + controller.value.volume)
|
|
|
|
|
|
.toStringAsFixed(num));
|
|
|
|
|
|
if (value >= 1.0) {
|
|
|
|
|
|
value = 1.0;
|
|
|
|
|
|
} else if (value <= 0.0) {
|
|
|
|
|
|
value = 0.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
return value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _reset(BuildContext context) {
|
2024-08-21 14:12:15 +08:00
|
|
|
|
startPosition = const Offset(0, 0);
|
2023-11-18 10:38:13 +08:00
|
|
|
|
movePan = 0;
|
|
|
|
|
|
layoutHeight = context.size!.height;
|
|
|
|
|
|
layoutWidth = context.size!.width;
|
|
|
|
|
|
volumePercentage = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _onHorizontalDragStart(DragStartDetails details) async {
|
|
|
|
|
|
_reset(context);
|
|
|
|
|
|
if (!videoInit) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 获取当前时间
|
|
|
|
|
|
position = controller.value.position;
|
|
|
|
|
|
// 暂停成功后才允许快进手势
|
|
|
|
|
|
allowHorizontal = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _onHorizontalDragUpdate(DragUpdateDetails details) {
|
|
|
|
|
|
if (!videoInit && !allowHorizontal) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 累计计算偏移量
|
|
|
|
|
|
movePan += details.delta.dx;
|
|
|
|
|
|
double value = _setHorizontalValue();
|
|
|
|
|
|
// 用百分比计算出当前的秒数
|
|
|
|
|
|
String currentSecond = DateUtil.formatDateMs(
|
|
|
|
|
|
(value * controller.value.duration.inMilliseconds).toInt(),
|
|
|
|
|
|
format: 'mm:ss',
|
|
|
|
|
|
);
|
|
|
|
|
|
if (value >= 0) {
|
|
|
|
|
|
setState(() {
|
2024-08-21 14:12:15 +08:00
|
|
|
|
volumePercentage = '${'快进至'.tr}:$currentSecond';
|
2023-11-18 10:38:13 +08:00
|
|
|
|
playDialogOpacity = 1.0;
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
setState(() {
|
2024-08-21 14:12:15 +08:00
|
|
|
|
volumePercentage = '${'快退至'.tr}:${(value * 100).toInt()}%';
|
2023-11-18 10:38:13 +08:00
|
|
|
|
playDialogOpacity = 1.0;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _onHorizontalDragEnd(DragEndDetails details) async {
|
|
|
|
|
|
if (!videoInit && !allowHorizontal) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
double value = _setHorizontalValue();
|
|
|
|
|
|
int current =
|
|
|
|
|
|
(value * controller.value.duration.inMilliseconds).toInt();
|
|
|
|
|
|
await controller.seekTo(Duration(milliseconds: current));
|
|
|
|
|
|
allowHorizontal = false;
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
playDialogOpacity = 0.0;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double _setHorizontalValue() {
|
|
|
|
|
|
// 进度条百分控制
|
|
|
|
|
|
double valueHorizontal =
|
|
|
|
|
|
double.parse((movePan / layoutWidth).toStringAsFixed(2));
|
|
|
|
|
|
// 当前进度条百分比
|
|
|
|
|
|
double currentValue = position.inMilliseconds /
|
|
|
|
|
|
controller.value.duration.inMilliseconds;
|
|
|
|
|
|
double value =
|
|
|
|
|
|
double.parse((currentValue + valueHorizontal).toStringAsFixed(2));
|
|
|
|
|
|
if (value >= 1.00) {
|
|
|
|
|
|
value = 1.00;
|
|
|
|
|
|
} else if (value <= 0.00) {
|
|
|
|
|
|
value = 0.00;
|
|
|
|
|
|
}
|
|
|
|
|
|
return value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|