feat: 音量、亮度调节

This commit is contained in:
guozhigq
2023-08-02 16:14:09 +08:00
parent 87ddd8a343
commit d512bc0bfd
4 changed files with 343 additions and 125 deletions

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
@ -10,6 +12,8 @@ import 'package:pilipala/plugin/pl_player/models/duration.dart';
import 'package:pilipala/plugin/pl_player/models/play_status.dart';
import 'package:pilipala/plugin/pl_player/utils.dart';
import 'package:pilipala/utils/feed_back.dart';
import 'package:screen_brightness/screen_brightness.dart';
import 'package:volume_controller/volume_controller.dart';
import 'widgets/backward_seek.dart';
import 'widgets/bottom_control.dart';
@ -43,6 +47,16 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
bool _hideSeekBackwardButton = false;
bool _hideSeekForwardButton = false;
double _brightnessValue = 0.0;
bool _brightnessIndicator = false;
Timer? _brightnessTimer;
double _volumeValue = 0.0;
bool _volumeIndicator = false;
Timer? _volumeTimer;
bool _volumeInterceptEventStream = false;
void onDoubleTapSeekBackward() {
setState(() {
_mountSeekBackwardButton = true;
@ -61,6 +75,70 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
animationController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 300));
videoController = widget.controller.videoController!;
Future.microtask(() async {
try {
VolumeController().showSystemUI = false;
_volumeValue = await VolumeController().getVolume();
VolumeController().listener((value) {
if (mounted && !_volumeInterceptEventStream) {
setState(() {
_volumeValue = value;
});
}
});
} catch (_) {}
});
Future.microtask(() async {
try {
_brightnessValue = await ScreenBrightness().current;
ScreenBrightness().onCurrentBrightnessChanged.listen((value) {
if (mounted) {
setState(() {
_brightnessValue = value;
});
}
});
} catch (_) {}
});
}
Future<void> setVolume(double value) async {
try {
VolumeController().setVolume(value);
} catch (_) {}
setState(() {
_volumeValue = value;
_volumeIndicator = true;
_volumeInterceptEventStream = true;
});
_volumeTimer?.cancel();
_volumeTimer = Timer(const Duration(milliseconds: 200), () {
if (mounted) {
setState(() {
_volumeIndicator = false;
_volumeInterceptEventStream = false;
});
}
});
}
Future<void> setBrightness(double value) async {
try {
await ScreenBrightness().setScreenBrightness(value);
} catch (_) {}
setState(() {
_brightnessIndicator = true;
});
_brightnessTimer?.cancel();
_brightnessTimer = Timer(const Duration(milliseconds: 200), () {
if (mounted) {
setState(() {
_brightnessIndicator = false;
});
}
});
}
@override
@ -90,25 +168,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
clipBehavior: Clip.hardEdge,
fit: StackFit.passthrough,
children: [
// Wrap [Video] widget with [MaterialVideoControlsTheme].
// MaterialVideoControlsTheme(
// normal: MaterialVideoControlsThemeData(
// // Modify theme options:
// buttonBarButtonSize: 24.0,
// buttonBarButtonColor: Colors.white,
// ),
// fullscreen: const MaterialVideoControlsThemeData(
// // Modify theme options:
// displaySeekBar: false,
// automaticallyImplySkipNextButton: false,
// automaticallyImplySkipPreviousButton: false,
// ),
// child: Scaffold(
// body: Video(
// controller: videoController,
// ),
// ),
// ),
Video(
controller: videoController,
controls: NoVideoControls,
@ -118,55 +177,267 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
padding: const EdgeInsets.all(24.0),
),
),
Padding(
padding: const EdgeInsets.only(top: 20, bottom: 15),
child: GestureDetector(
onTap: () {
_.controls = !_.showControls.value;
},
// onDoubleTap: () {
// if (_.playerStatus.status.value == PlayerStatus.playing) {
// _.togglePlay();
// } else {
// _.play();
// }
// },
onDoubleTapDown: (details) {
final totalWidth = MediaQuery.of(context).size.width;
final tapPosition = details.localPosition.dx;
final sectionWidth = totalWidth / 3;
if (tapPosition < sectionWidth) {
// 双击左边区域 👈
onDoubleTapSeekBackward();
} else if (tapPosition < sectionWidth * 2) {
if (_.playerStatus.status.value == PlayerStatus.playing) {
_.togglePlay();
} else {
_.play();
}
} else {
// 双击右边区域 👈
onDoubleTapSeekForward();
}
},
onLongPressStart: (detail) {
feedBack();
double currentSpeed = _.playbackSpeed;
_.setDoubleSpeedStatus(true);
_.setPlaybackSpeed(currentSpeed * 2);
},
onLongPressEnd: (details) {
double currentSpeed = _.playbackSpeed;
_.setDoubleSpeedStatus(false);
_.setPlaybackSpeed(currentSpeed / 2);
},
// 水平位置 快进
onHorizontalDragUpdate: (DragUpdateDetails details) {},
onHorizontalDragEnd: (DragEndDetails details) {},
// 垂直方向 音量/亮度调节
onVerticalDragUpdate: (DragUpdateDetails details) {},
onVerticalDragEnd: (DragEndDetails details) {}),
/// 长按倍速
Obx(
() => Align(
alignment: Alignment.topCenter,
child: FractionalTranslation(
translation: const Offset(0.0, 1), // 上下偏移量(负数向上偏移)
child: AnimatedOpacity(
curve: Curves.easeInOut,
opacity: _.doubleSpeedStatus.value ? 1.0 : 0.0,
duration: const Duration(milliseconds: 150),
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: const Color(0x88000000),
borderRadius: BorderRadius.circular(64.0),
),
height: 34.0,
width: 86.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const SizedBox(width: 3),
Image.asset(
'assets/images/run-pokemon.gif',
height: 20,
),
const Text(
'倍速中',
style: TextStyle(color: Colors.white, fontSize: 12),
),
const SizedBox(width: 4),
],
),
),
),
),
),
),
/// 时间进度
Obx(
() => Align(
alignment: Alignment.topCenter,
child: FractionalTranslation(
translation: const Offset(0.0, 1.0), // 上下偏移量(负数向上偏移)
child: AnimatedOpacity(
curve: Curves.easeInOut,
opacity: _.isSliderMoving.value ? 1.0 : 0.0,
duration: const Duration(milliseconds: 150),
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: const Color(0x88000000),
borderRadius: BorderRadius.circular(64.0),
),
height: 34.0,
width: 100.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Obx(() {
return Text(
_.sliderTempPosition.value.inMinutes >= 60
? printDurationWithHours(
_.sliderTempPosition.value)
: printDuration(_.sliderTempPosition.value),
style: textStyle,
);
}),
const SizedBox(width: 2),
const Text('/', style: textStyle),
const SizedBox(width: 2),
Obx(
() => Text(
_.duration.value.inMinutes >= 60
? printDurationWithHours(_.duration.value)
: printDuration(_.duration.value),
style: textStyle,
),
),
],
),
),
),
),
),
),
/// 音量🔊 控制条展示
Align(
alignment: Alignment.center,
child: AnimatedOpacity(
curve: Curves.easeInOut,
opacity: _volumeIndicator ? 1.0 : 0.0,
duration: const Duration(milliseconds: 150),
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: const Color(0x88000000),
borderRadius: BorderRadius.circular(64.0),
),
height: 34.0,
width: 70.0,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 34.0,
width: 28.0,
alignment: Alignment.centerRight,
child: Icon(
_volumeValue == 0.0
? Icons.volume_off
: _volumeValue < 0.5
? Icons.volume_down
: Icons.volume_up,
color: const Color(0xFFFFFFFF),
size: 20.0,
),
),
Expanded(
child: Text(
'${(_volumeValue * 100.0).round()}%',
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 13.0,
color: Color(0xFFFFFFFF),
),
),
),
const SizedBox(width: 6.0),
],
),
),
),
),
/// 亮度🌞 控制条展示
Align(
alignment: Alignment.center,
child: AnimatedOpacity(
curve: Curves.easeInOut,
opacity: _brightnessIndicator ? 1.0 : 0.0,
duration: const Duration(milliseconds: 150),
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: const Color(0x88000000),
borderRadius: BorderRadius.circular(64.0),
),
height: 34.0,
width: 70.0,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 30.0,
width: 28.0,
alignment: Alignment.centerRight,
child: Icon(
_brightnessValue < 1.0 / 3.0
? Icons.brightness_low
: _brightnessValue < 2.0 / 3.0
? Icons.brightness_medium
: Icons.brightness_high,
color: const Color(0xFFFFFFFF),
size: 18.0,
),
),
const SizedBox(width: 2.0),
Expanded(
child: Text(
'${(_brightnessValue * 100.0).round()}%',
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 13.0,
color: Color(0xFFFFFFFF),
),
),
),
const SizedBox(width: 6.0),
],
),
),
),
),
/// 手势
Positioned.fill(
left: 16,
top: 25,
right: 15,
bottom: 15,
child: GestureDetector(
onTap: () {
_.controls = !_.showControls.value;
},
onDoubleTapDown: (details) {
final totalWidth = MediaQuery.of(context).size.width;
final tapPosition = details.localPosition.dx;
final sectionWidth = totalWidth / 3;
if (tapPosition < sectionWidth) {
// 双击左边区域 👈
onDoubleTapSeekBackward();
} else if (tapPosition < sectionWidth * 2) {
if (_.playerStatus.status.value == PlayerStatus.playing) {
_.togglePlay();
} else {
_.play();
}
} else {
// 双击右边区域 👈
onDoubleTapSeekForward();
}
},
onLongPressStart: (detail) {
feedBack();
double currentSpeed = _.playbackSpeed;
_.setDoubleSpeedStatus(true);
_.setPlaybackSpeed(currentSpeed * 2);
},
onLongPressEnd: (details) {
double currentSpeed = _.playbackSpeed;
_.setDoubleSpeedStatus(false);
_.setPlaybackSpeed(currentSpeed / 2);
},
// 水平位置 快进
onHorizontalDragUpdate: (DragUpdateDetails details) {},
onHorizontalDragEnd: (DragEndDetails details) {},
// 垂直方向 音量/亮度调节
onVerticalDragUpdate: (DragUpdateDetails details) {
final totalWidth = MediaQuery.of(context).size.width;
final tapPosition = details.localPosition.dx;
final sectionWidth = totalWidth / 3;
if (tapPosition < sectionWidth) {
// 左边区域 👈
final delta = details.delta.dy;
final brightness = _brightnessValue - delta / 100.0;
final result = brightness.clamp(0.0, 1.0);
setBrightness(result);
} else if (tapPosition < sectionWidth * 2) {
// 全屏
print('全屏');
} else {
// 右边区域 👈
final delta = details.delta.dy;
final volume = _volumeValue - delta / 100.0;
final result = volume.clamp(0.0, 1.0);
setVolume(result);
}
},
onVerticalDragEnd: (DragEndDetails details) {},
),
),
// 头部、底部控制条
if (_.controlsEnabled)
Obx(
@ -245,26 +516,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
);
},
),
// 长按倍速
Obx(
() => Align(
alignment: Alignment.topCenter,
child: FractionalTranslation(
translation: const Offset(0.0, 1.5), // 上下偏移量(负数向上偏移)
child: Visibility(
visible: _.doubleSpeedStatus.value,
child: const Text(
'** 倍速中 **',
style: TextStyle(
fontSize: 13,
backgroundColor: Color(0xaa000000),
color: Colors.white,
),
),
),
),
),
),
// 锁
if (_.controlsEnabled)
Obx(
@ -301,44 +553,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
return Container();
}
}),
// 时间进度
/// TDDO 样式
Obx(
() => Align(
alignment: Alignment.topCenter,
child: FractionalTranslation(
translation: const Offset(0.0, 2.5), // 上下偏移量(负数向上偏移)
child: Visibility(
visible: _.isSliderMoving.value,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Obx(() {
return Text(
_.sliderTempPosition.value.inMinutes >= 60
? printDurationWithHours(_.sliderTempPosition.value)
: printDuration(_.sliderTempPosition.value),
style: textStyle,
);
}),
const SizedBox(width: 2),
const Text('/', style: textStyle),
const SizedBox(width: 2),
Obx(
() => Text(
_.duration.value.inMinutes >= 60
? printDurationWithHours(_.duration.value)
: printDuration(_.duration.value),
style: textStyle,
),
),
],
),
),
),
),
),
// 点击 快进/快退
/// 点击 快进/快退
if (_mountSeekBackwardButton || _mountSeekForwardButton)
Positioned.fill(
child: Row(