Compare commits
8 Commits
fix-bangum
...
feature-li
| Author | SHA1 | Date | |
|---|---|---|---|
| 33d28f51d1 | |||
| a37f3b8b5b | |||
| a57f5e8b2f | |||
| beb640ac83 | |||
| fca0588377 | |||
| da2bbeedff | |||
| 98aaca286b | |||
| 6fdfcb888d |
43
lib/models/live/quality.dart
Normal file
43
lib/models/live/quality.dart
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
enum LiveQuality {
|
||||||
|
dolby,
|
||||||
|
super4K,
|
||||||
|
origin,
|
||||||
|
bluRay,
|
||||||
|
superHD,
|
||||||
|
smooth,
|
||||||
|
flunt,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension LiveQualityCode on LiveQuality {
|
||||||
|
static final List<int> _codeList = [
|
||||||
|
30000,
|
||||||
|
20000,
|
||||||
|
10000,
|
||||||
|
400,
|
||||||
|
250,
|
||||||
|
150,
|
||||||
|
80,
|
||||||
|
];
|
||||||
|
int get code => _codeList[index];
|
||||||
|
|
||||||
|
static LiveQuality? fromCode(int code) {
|
||||||
|
final index = _codeList.indexOf(code);
|
||||||
|
if (index != -1) {
|
||||||
|
return LiveQuality.values[index];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension VideoQualityDesc on LiveQuality {
|
||||||
|
static final List<String> _descList = [
|
||||||
|
'杜比',
|
||||||
|
'4K',
|
||||||
|
'原画',
|
||||||
|
'蓝光',
|
||||||
|
'超清',
|
||||||
|
'高清',
|
||||||
|
'流畅',
|
||||||
|
];
|
||||||
|
get description => _descList[index];
|
||||||
|
}
|
||||||
@ -1,6 +1,8 @@
|
|||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/http/constants.dart';
|
import 'package:pilipala/http/constants.dart';
|
||||||
import 'package:pilipala/http/live.dart';
|
import 'package:pilipala/http/live.dart';
|
||||||
|
import 'package:pilipala/models/live/quality.dart';
|
||||||
import 'package:pilipala/models/live/room_info.dart';
|
import 'package:pilipala/models/live/room_info.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/index.dart';
|
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||||
import '../../models/live/room_info_h5.dart';
|
import '../../models/live/room_info_h5.dart';
|
||||||
@ -19,10 +21,16 @@ class LiveRoomController extends GetxController {
|
|||||||
PlPlayerController.getInstance(videoType: 'live');
|
PlPlayerController.getInstance(videoType: 'live');
|
||||||
Rx<RoomInfoH5Model> roomInfoH5 = RoomInfoH5Model().obs;
|
Rx<RoomInfoH5Model> roomInfoH5 = RoomInfoH5Model().obs;
|
||||||
late bool enableCDN;
|
late bool enableCDN;
|
||||||
|
late int currentQn;
|
||||||
|
int? tempCurrentQn;
|
||||||
|
late List<Map<String, dynamic>> acceptQnList;
|
||||||
|
RxString currentQnDesc = ''.obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
currentQn = setting.get(SettingBoxKey.defaultLiveQa,
|
||||||
|
defaultValue: LiveQuality.values.last.code);
|
||||||
roomId = int.parse(Get.parameters['roomid']!);
|
roomId = int.parse(Get.parameters['roomid']!);
|
||||||
if (Get.arguments != null) {
|
if (Get.arguments != null) {
|
||||||
liveItem = Get.arguments['liveItem'];
|
liveItem = Get.arguments['liveItem'];
|
||||||
@ -57,11 +65,28 @@ class LiveRoomController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future queryLiveInfo() async {
|
Future queryLiveInfo() async {
|
||||||
var res = await LiveHttp.liveRoomInfo(roomId: roomId, qn: 10000);
|
var res = await LiveHttp.liveRoomInfo(roomId: roomId, qn: currentQn);
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
List<CodecItem> codec =
|
List<CodecItem> codec =
|
||||||
res['data'].playurlInfo.playurl.stream.first.format.first.codec;
|
res['data'].playurlInfo.playurl.stream.first.format.first.codec;
|
||||||
CodecItem item = codec.first;
|
CodecItem item = codec.first;
|
||||||
|
// 以服务端返回的码率为准
|
||||||
|
currentQn = item.currentQn!;
|
||||||
|
if (tempCurrentQn != null && tempCurrentQn == currentQn) {
|
||||||
|
SmartDialog.showToast('画质切换失败,请检查登录状态');
|
||||||
|
}
|
||||||
|
List acceptQn = item.acceptQn!;
|
||||||
|
acceptQnList = acceptQn.map((e) {
|
||||||
|
return {
|
||||||
|
'code': e,
|
||||||
|
'desc': LiveQuality.values
|
||||||
|
.firstWhere((element) => element.code == e)
|
||||||
|
.description,
|
||||||
|
};
|
||||||
|
}).toList();
|
||||||
|
currentQnDesc.value = LiveQuality.values
|
||||||
|
.firstWhere((element) => element.code == currentQn)
|
||||||
|
.description;
|
||||||
String videoUrl = enableCDN
|
String videoUrl = enableCDN
|
||||||
? VideoUtils.getCdnUrl(item)
|
? VideoUtils.getCdnUrl(item)
|
||||||
: (item.urlInfo?.first.host)! +
|
: (item.urlInfo?.first.host)! +
|
||||||
@ -90,4 +115,17 @@ class LiveRoomController extends GetxController {
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修改画质
|
||||||
|
void changeQn(int qn) async {
|
||||||
|
tempCurrentQn = currentQn;
|
||||||
|
if (currentQn == qn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentQn = qn;
|
||||||
|
currentQnDesc.value = LiveQuality.values
|
||||||
|
.firstWhere((element) => element.code == currentQn)
|
||||||
|
.description;
|
||||||
|
await queryLiveInfo();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import 'dart:io';
|
|||||||
import 'package:floating/floating.dart';
|
import 'package:floating/floating.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/models/video/play/url.dart';
|
import 'package:pilipala/models/video/play/url.dart';
|
||||||
import 'package:pilipala/pages/live_room/index.dart';
|
import 'package:pilipala/pages/live_room/index.dart';
|
||||||
@ -83,6 +84,30 @@ class _BottomControlState extends State<BottomControl> {
|
|||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
// const SizedBox(width: 4),
|
// const SizedBox(width: 4),
|
||||||
|
SizedBox(
|
||||||
|
width: 30,
|
||||||
|
child: PopupMenuButton<int>(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
onSelected: (value) {
|
||||||
|
widget.liveRoomCtr!.changeQn(value);
|
||||||
|
},
|
||||||
|
child: Obx(
|
||||||
|
() => Text(
|
||||||
|
widget.liveRoomCtr!.currentQnDesc.value,
|
||||||
|
style: const TextStyle(color: Colors.white, fontSize: 13),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
itemBuilder: (BuildContext context) {
|
||||||
|
return widget.liveRoomCtr!.acceptQnList.map((e) {
|
||||||
|
return PopupMenuItem<int>(
|
||||||
|
value: e['code'],
|
||||||
|
child: Text(e['desc']),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
if (Platform.isAndroid) ...[
|
if (Platform.isAndroid) ...[
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 34,
|
width: 34,
|
||||||
@ -110,7 +135,7 @@ class _BottomControlState extends State<BottomControl> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 10),
|
||||||
],
|
],
|
||||||
ComBtn(
|
ComBtn(
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import 'package:pilipala/services/service_locator.dart';
|
|||||||
import 'package:pilipala/utils/global_data.dart';
|
import 'package:pilipala/utils/global_data.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
|
import '../../models/live/quality.dart';
|
||||||
import 'widgets/switch_item.dart';
|
import 'widgets/switch_item.dart';
|
||||||
|
|
||||||
class PlaySetting extends StatefulWidget {
|
class PlaySetting extends StatefulWidget {
|
||||||
@ -22,6 +23,7 @@ class PlaySetting extends StatefulWidget {
|
|||||||
class _PlaySettingState extends State<PlaySetting> {
|
class _PlaySettingState extends State<PlaySetting> {
|
||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
late dynamic defaultVideoQa;
|
late dynamic defaultVideoQa;
|
||||||
|
late dynamic defaultLiveQa;
|
||||||
late dynamic defaultAudioQa;
|
late dynamic defaultAudioQa;
|
||||||
late dynamic defaultDecode;
|
late dynamic defaultDecode;
|
||||||
late int defaultFullScreenMode;
|
late int defaultFullScreenMode;
|
||||||
@ -32,6 +34,8 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
defaultVideoQa = setting.get(SettingBoxKey.defaultVideoQa,
|
defaultVideoQa = setting.get(SettingBoxKey.defaultVideoQa,
|
||||||
defaultValue: VideoQuality.values.last.code);
|
defaultValue: VideoQuality.values.last.code);
|
||||||
|
defaultLiveQa = setting.get(SettingBoxKey.defaultLiveQa,
|
||||||
|
defaultValue: LiveQuality.values.last.code);
|
||||||
defaultAudioQa = setting.get(SettingBoxKey.defaultAudioQa,
|
defaultAudioQa = setting.get(SettingBoxKey.defaultAudioQa,
|
||||||
defaultValue: AudioQuality.values.last.code);
|
defaultValue: AudioQuality.values.last.code);
|
||||||
defaultDecode = setting.get(SettingBoxKey.defaultDecode,
|
defaultDecode = setting.get(SettingBoxKey.defaultDecode,
|
||||||
@ -157,9 +161,9 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
}),
|
}),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('默认画质', style: titleStyle),
|
title: Text('默认视频画质', style: titleStyle),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'当前画质${VideoQualityCode.fromCode(defaultVideoQa)!.description!}',
|
'当前默认画质${VideoQualityCode.fromCode(defaultVideoQa)!.description!}',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
@ -167,7 +171,7 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return SelectDialog<int>(
|
return SelectDialog<int>(
|
||||||
title: '默认画质',
|
title: '默认视频画质',
|
||||||
value: defaultVideoQa,
|
value: defaultVideoQa,
|
||||||
values: VideoQuality.values.reversed.map((e) {
|
values: VideoQuality.values.reversed.map((e) {
|
||||||
return {'title': e.description, 'value': e.code};
|
return {'title': e.description, 'value': e.code};
|
||||||
@ -181,6 +185,32 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
dense: false,
|
||||||
|
title: Text('默认直播画质', style: titleStyle),
|
||||||
|
subtitle: Text(
|
||||||
|
'当前默认画质${LiveQualityCode.fromCode(defaultLiveQa)!.description!}',
|
||||||
|
style: subTitleStyle,
|
||||||
|
),
|
||||||
|
onTap: () async {
|
||||||
|
int? result = await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return SelectDialog<int>(
|
||||||
|
title: '默认直播画质',
|
||||||
|
value: defaultLiveQa,
|
||||||
|
values: LiveQuality.values.reversed.map((e) {
|
||||||
|
return {'title': e.description, 'value': e.code};
|
||||||
|
}).toList());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
defaultLiveQa = result;
|
||||||
|
setting.put(SettingBoxKey.defaultLiveQa, result);
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('默认音质', style: titleStyle),
|
title: Text('默认音质', style: titleStyle),
|
||||||
|
|||||||
10
lib/plugin/pl_player/models/bottom_control_type.dart
Normal file
10
lib/plugin/pl_player/models/bottom_control_type.dart
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
enum BottomControlType {
|
||||||
|
pre,
|
||||||
|
playOrPause,
|
||||||
|
next,
|
||||||
|
time,
|
||||||
|
space,
|
||||||
|
fit,
|
||||||
|
speed,
|
||||||
|
fullscreen,
|
||||||
|
}
|
||||||
@ -8,7 +8,6 @@ import 'package:get/get.dart';
|
|||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
import 'package:media_kit_video/media_kit_video.dart';
|
import 'package:media_kit_video/media_kit_video.dart';
|
||||||
import 'package:nil/nil.dart';
|
|
||||||
import 'package:pilipala/models/common/gesture_mode.dart';
|
import 'package:pilipala/models/common/gesture_mode.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/controller.dart';
|
import 'package:pilipala/plugin/pl_player/controller.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/models/duration.dart';
|
import 'package:pilipala/plugin/pl_player/models/duration.dart';
|
||||||
@ -19,12 +18,14 @@ import 'package:pilipala/utils/storage.dart';
|
|||||||
import 'package:screen_brightness/screen_brightness.dart';
|
import 'package:screen_brightness/screen_brightness.dart';
|
||||||
|
|
||||||
import '../../utils/global_data.dart';
|
import '../../utils/global_data.dart';
|
||||||
|
import 'models/bottom_control_type.dart';
|
||||||
import 'models/bottom_progress_behavior.dart';
|
import 'models/bottom_progress_behavior.dart';
|
||||||
import 'widgets/app_bar_ani.dart';
|
import 'widgets/app_bar_ani.dart';
|
||||||
import 'widgets/backward_seek.dart';
|
import 'widgets/backward_seek.dart';
|
||||||
import 'widgets/bottom_control.dart';
|
import 'widgets/bottom_control.dart';
|
||||||
import 'widgets/common_btn.dart';
|
import 'widgets/common_btn.dart';
|
||||||
import 'widgets/forward_seek.dart';
|
import 'widgets/forward_seek.dart';
|
||||||
|
import 'widgets/play_pause_btn.dart';
|
||||||
|
|
||||||
class PLVideoPlayer extends StatefulWidget {
|
class PLVideoPlayer extends StatefulWidget {
|
||||||
const PLVideoPlayer({
|
const PLVideoPlayer({
|
||||||
@ -32,6 +33,7 @@ class PLVideoPlayer extends StatefulWidget {
|
|||||||
this.headerControl,
|
this.headerControl,
|
||||||
this.bottomControl,
|
this.bottomControl,
|
||||||
this.danmuWidget,
|
this.danmuWidget,
|
||||||
|
this.bottomList,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -39,6 +41,7 @@ class PLVideoPlayer extends StatefulWidget {
|
|||||||
final PreferredSizeWidget? headerControl;
|
final PreferredSizeWidget? headerControl;
|
||||||
final PreferredSizeWidget? bottomControl;
|
final PreferredSizeWidget? bottomControl;
|
||||||
final Widget? danmuWidget;
|
final Widget? danmuWidget;
|
||||||
|
final List<BottomControlType>? bottomList;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PLVideoPlayer> createState() => _PLVideoPlayerState();
|
State<PLVideoPlayer> createState() => _PLVideoPlayerState();
|
||||||
@ -48,26 +51,22 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
with TickerProviderStateMixin {
|
with TickerProviderStateMixin {
|
||||||
late AnimationController animationController;
|
late AnimationController animationController;
|
||||||
late VideoController videoController;
|
late VideoController videoController;
|
||||||
final PLVideoPlayerController _ctr = Get.put(PLVideoPlayerController());
|
|
||||||
|
|
||||||
// bool _mountSeekBackwardButton = false;
|
final RxBool _mountSeekBackwardButton = false.obs;
|
||||||
// bool _mountSeekForwardButton = false;
|
final RxBool _mountSeekForwardButton = false.obs;
|
||||||
// bool _hideSeekBackwardButton = false;
|
final RxBool _hideSeekBackwardButton = false.obs;
|
||||||
// bool _hideSeekForwardButton = false;
|
final RxBool _hideSeekForwardButton = false.obs;
|
||||||
|
|
||||||
// double _brightnessValue = 0.0;
|
final RxDouble _brightnessValue = 0.0.obs;
|
||||||
// bool _brightnessIndicator = false;
|
final RxBool _brightnessIndicator = false.obs;
|
||||||
Timer? _brightnessTimer;
|
Timer? _brightnessTimer;
|
||||||
|
|
||||||
// double _volumeValue = 0.0;
|
final RxDouble _volumeValue = 0.0.obs;
|
||||||
// bool _volumeIndicator = false;
|
final RxBool _volumeIndicator = false.obs;
|
||||||
Timer? _volumeTimer;
|
Timer? _volumeTimer;
|
||||||
|
|
||||||
double _distance = 0.0;
|
final RxDouble _distance = 0.0.obs;
|
||||||
// 初始手指落下位置
|
final RxBool _volumeInterceptEventStream = false.obs;
|
||||||
// double _initTapPositoin = 0.0;
|
|
||||||
|
|
||||||
// bool _volumeInterceptEventStream = false;
|
|
||||||
|
|
||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
late FullScreenMode mode;
|
late FullScreenMode mode;
|
||||||
@ -82,11 +81,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
DateTime? lastFullScreenToggleTime;
|
DateTime? lastFullScreenToggleTime;
|
||||||
|
|
||||||
void onDoubleTapSeekBackward() {
|
void onDoubleTapSeekBackward() {
|
||||||
_ctr.onDoubleTapSeekBackward();
|
_mountSeekBackwardButton.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDoubleTapSeekForward() {
|
void onDoubleTapSeekForward() {
|
||||||
_ctr.onDoubleTapSeekForward();
|
_mountSeekForwardButton.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 双击播放、暂停
|
// 双击播放、暂停
|
||||||
@ -138,10 +137,10 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
Future.microtask(() async {
|
Future.microtask(() async {
|
||||||
try {
|
try {
|
||||||
FlutterVolumeController.updateShowSystemUI(true);
|
FlutterVolumeController.updateShowSystemUI(true);
|
||||||
_ctr.volumeValue.value = (await FlutterVolumeController.getVolume())!;
|
_volumeValue.value = (await FlutterVolumeController.getVolume())!;
|
||||||
FlutterVolumeController.addListener((double value) {
|
FlutterVolumeController.addListener((double value) {
|
||||||
if (mounted && !_ctr.volumeInterceptEventStream.value) {
|
if (mounted && !_volumeInterceptEventStream.value) {
|
||||||
_ctr.volumeValue.value = value;
|
_volumeValue.value = value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
@ -149,10 +148,10 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
|
|
||||||
Future.microtask(() async {
|
Future.microtask(() async {
|
||||||
try {
|
try {
|
||||||
_ctr.brightnessValue.value = await ScreenBrightness().current;
|
_brightnessValue.value = await ScreenBrightness().current;
|
||||||
ScreenBrightness().onCurrentBrightnessChanged.listen((double value) {
|
ScreenBrightness().onCurrentBrightnessChanged.listen((double value) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_ctr.brightnessValue.value = value;
|
_brightnessValue.value = value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
@ -164,14 +163,14 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
FlutterVolumeController.updateShowSystemUI(false);
|
FlutterVolumeController.updateShowSystemUI(false);
|
||||||
await FlutterVolumeController.setVolume(value);
|
await FlutterVolumeController.setVolume(value);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
_ctr.volumeValue.value = value;
|
_volumeValue.value = value;
|
||||||
_ctr.volumeIndicator.value = true;
|
_volumeIndicator.value = true;
|
||||||
_ctr.volumeInterceptEventStream.value = true;
|
_volumeInterceptEventStream.value = true;
|
||||||
_volumeTimer?.cancel();
|
_volumeTimer?.cancel();
|
||||||
_volumeTimer = Timer(const Duration(milliseconds: 200), () {
|
_volumeTimer = Timer(const Duration(milliseconds: 200), () {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_ctr.volumeIndicator.value = false;
|
_volumeIndicator.value = false;
|
||||||
_ctr.volumeInterceptEventStream.value = false;
|
_volumeInterceptEventStream.value = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -180,11 +179,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
try {
|
try {
|
||||||
await ScreenBrightness().setScreenBrightness(value);
|
await ScreenBrightness().setScreenBrightness(value);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
_ctr.brightnessIndicator.value = true;
|
_brightnessIndicator.value = true;
|
||||||
_brightnessTimer?.cancel();
|
_brightnessTimer?.cancel();
|
||||||
_brightnessTimer = Timer(const Duration(milliseconds: 200), () {
|
_brightnessTimer = Timer(const Duration(milliseconds: 200), () {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_ctr.brightnessIndicator.value = false;
|
_brightnessIndicator.value = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
widget.controller.brightness.value = value;
|
widget.controller.brightness.value = value;
|
||||||
@ -197,6 +196,134 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 动态构建底部控制条
|
||||||
|
List<Widget> buildBottomControl() {
|
||||||
|
const TextStyle textStyle = TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 12,
|
||||||
|
);
|
||||||
|
final PlPlayerController _ = widget.controller;
|
||||||
|
Map<BottomControlType, Widget> videoProgressWidgets = {
|
||||||
|
/// 上一集
|
||||||
|
BottomControlType.pre: ComBtn(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.skip_previous_outlined,
|
||||||
|
size: 15,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
fuc: () {},
|
||||||
|
),
|
||||||
|
|
||||||
|
/// 播放暂停
|
||||||
|
BottomControlType.playOrPause: PlayOrPauseButton(
|
||||||
|
controller: _,
|
||||||
|
),
|
||||||
|
|
||||||
|
/// 下一集
|
||||||
|
BottomControlType.next: ComBtn(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.last_page_outlined,
|
||||||
|
size: 15,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
fuc: () {},
|
||||||
|
),
|
||||||
|
|
||||||
|
/// 时间进度
|
||||||
|
BottomControlType.time: Row(
|
||||||
|
children: [
|
||||||
|
Obx(() {
|
||||||
|
return Text(
|
||||||
|
_.durationSeconds.value >= 3600
|
||||||
|
? printDurationWithHours(
|
||||||
|
Duration(seconds: _.positionSeconds.value))
|
||||||
|
: printDuration(Duration(seconds: _.positionSeconds.value)),
|
||||||
|
style: textStyle,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
const SizedBox(width: 2),
|
||||||
|
const Text('/', style: textStyle),
|
||||||
|
const SizedBox(width: 2),
|
||||||
|
Obx(
|
||||||
|
() => Text(
|
||||||
|
_.durationSeconds.value >= 3600
|
||||||
|
? printDurationWithHours(
|
||||||
|
Duration(seconds: _.durationSeconds.value))
|
||||||
|
: printDuration(Duration(seconds: _.durationSeconds.value)),
|
||||||
|
style: textStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
/// 空白占位
|
||||||
|
BottomControlType.space: const Spacer(),
|
||||||
|
|
||||||
|
/// 画面比例
|
||||||
|
BottomControlType.fit: SizedBox(
|
||||||
|
height: 30,
|
||||||
|
child: TextButton(
|
||||||
|
onPressed: () => _.toggleVideoFit(),
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
|
),
|
||||||
|
child: Obx(
|
||||||
|
() => Text(
|
||||||
|
_.videoFitDEsc.value,
|
||||||
|
style: const TextStyle(color: Colors.white, fontSize: 13),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
/// 播放速度
|
||||||
|
BottomControlType.speed: SizedBox(
|
||||||
|
width: 45,
|
||||||
|
height: 34,
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
|
),
|
||||||
|
onPressed: () {},
|
||||||
|
child: Obx(
|
||||||
|
() => Text(
|
||||||
|
'${_.playbackSpeed.toString()}X',
|
||||||
|
style: textStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
/// 字幕
|
||||||
|
/// 全屏
|
||||||
|
BottomControlType.fullscreen: ComBtn(
|
||||||
|
icon: Obx(
|
||||||
|
() => Icon(
|
||||||
|
_.isFullScreen.value
|
||||||
|
? FontAwesomeIcons.compress
|
||||||
|
: FontAwesomeIcons.expand,
|
||||||
|
size: 15,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
fuc: () => _.triggerFullScreen(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
final List<Widget> list = [];
|
||||||
|
var userSpecifyItem = widget.bottomList ??
|
||||||
|
[
|
||||||
|
BottomControlType.playOrPause,
|
||||||
|
BottomControlType.time,
|
||||||
|
BottomControlType.space,
|
||||||
|
BottomControlType.fit,
|
||||||
|
BottomControlType.fullscreen,
|
||||||
|
];
|
||||||
|
for (var i = 0; i < userSpecifyItem.length; i++) {
|
||||||
|
list.add(videoProgressWidgets[userSpecifyItem[i]]!);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final PlPlayerController _ = widget.controller;
|
final PlPlayerController _ = widget.controller;
|
||||||
@ -316,7 +443,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
() => Align(
|
() => Align(
|
||||||
child: AnimatedOpacity(
|
child: AnimatedOpacity(
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
opacity: _ctr.volumeIndicator.value ? 1.0 : 0.0,
|
opacity: _volumeIndicator.value ? 1.0 : 0.0,
|
||||||
duration: const Duration(milliseconds: 150),
|
duration: const Duration(milliseconds: 150),
|
||||||
child: Container(
|
child: Container(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
@ -335,9 +462,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
width: 28.0,
|
width: 28.0,
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
_ctr.volumeValue.value == 0.0
|
_volumeValue.value == 0.0
|
||||||
? Icons.volume_off
|
? Icons.volume_off
|
||||||
: _ctr.volumeValue.value < 0.5
|
: _volumeValue.value < 0.5
|
||||||
? Icons.volume_down
|
? Icons.volume_down
|
||||||
: Icons.volume_up,
|
: Icons.volume_up,
|
||||||
color: const Color(0xFFFFFFFF),
|
color: const Color(0xFFFFFFFF),
|
||||||
@ -346,7 +473,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
'${(_ctr.volumeValue.value * 100.0).round()}%',
|
'${(_volumeValue.value * 100.0).round()}%',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 13.0,
|
fontSize: 13.0,
|
||||||
@ -367,7 +494,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
() => Align(
|
() => Align(
|
||||||
child: AnimatedOpacity(
|
child: AnimatedOpacity(
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
opacity: _ctr.brightnessIndicator.value ? 1.0 : 0.0,
|
opacity: _brightnessIndicator.value ? 1.0 : 0.0,
|
||||||
duration: const Duration(milliseconds: 150),
|
duration: const Duration(milliseconds: 150),
|
||||||
child: Container(
|
child: Container(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
@ -386,9 +513,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
width: 28.0,
|
width: 28.0,
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
_ctr.brightnessValue.value < 1.0 / 3.0
|
_brightnessValue.value < 1.0 / 3.0
|
||||||
? Icons.brightness_low
|
? Icons.brightness_low
|
||||||
: _ctr.brightnessValue.value < 2.0 / 3.0
|
: _brightnessValue.value < 2.0 / 3.0
|
||||||
? Icons.brightness_medium
|
? Icons.brightness_medium
|
||||||
: Icons.brightness_high,
|
: Icons.brightness_high,
|
||||||
color: const Color(0xFFFFFFFF),
|
color: const Color(0xFFFFFFFF),
|
||||||
@ -398,7 +525,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
const SizedBox(width: 2.0),
|
const SizedBox(width: 2.0),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
'${(_ctr.brightnessValue.value * 100.0).round()}%',
|
'${(_brightnessValue.value * 100.0).round()}%',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 13.0,
|
fontSize: 13.0,
|
||||||
@ -489,7 +616,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
pos.clamp(Duration.zero, _.duration.value);
|
pos.clamp(Duration.zero, _.duration.value);
|
||||||
_.onUpdatedSliderProgress(result);
|
_.onUpdatedSliderProgress(result);
|
||||||
_.onChangedSliderStart();
|
_.onChangedSliderStart();
|
||||||
// _initTapPositoin = tapPosition;
|
|
||||||
},
|
},
|
||||||
onHorizontalDragEnd: (DragEndDetails details) {
|
onHorizontalDragEnd: (DragEndDetails details) {
|
||||||
if (_.videoType.value == 'live' || _.controlsLock.value) {
|
if (_.videoType.value == 'live' || _.controlsLock.value) {
|
||||||
@ -521,7 +647,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
: screenWidth * 9 / 16) *
|
: screenWidth * 9 / 16) *
|
||||||
3;
|
3;
|
||||||
final double brightness =
|
final double brightness =
|
||||||
_ctr.brightnessValue.value - delta / level;
|
_brightnessValue.value - delta / level;
|
||||||
final double result = brightness.clamp(0.0, 1.0);
|
final double result = brightness.clamp(0.0, 1.0);
|
||||||
setBrightness(result);
|
setBrightness(result);
|
||||||
} else if (tapPosition < sectionWidth * 2) {
|
} else if (tapPosition < sectionWidth * 2) {
|
||||||
@ -530,29 +656,29 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
const double threshold = 7.0; // 滑动阈值
|
const double threshold = 7.0; // 滑动阈值
|
||||||
final bool flag =
|
final bool flag =
|
||||||
fullScreenGestureMode != FullScreenGestureMode.values.last;
|
fullScreenGestureMode != FullScreenGestureMode.values.last;
|
||||||
if (dy > _distance && dy > threshold) {
|
if (dy > _distance.value && dy > threshold) {
|
||||||
if (_.isFullScreen.value ^ flag) {
|
if (_.isFullScreen.value ^ flag) {
|
||||||
lastFullScreenToggleTime = DateTime.now();
|
lastFullScreenToggleTime = DateTime.now();
|
||||||
// 下滑退出全屏
|
// 下滑退出全屏
|
||||||
await widget.controller.triggerFullScreen(status: flag);
|
await widget.controller.triggerFullScreen(status: flag);
|
||||||
}
|
}
|
||||||
_distance = 0.0;
|
_distance.value = 0.0;
|
||||||
} else if (dy < _distance && dy < -threshold) {
|
} else if (dy < _distance.value && dy < -threshold) {
|
||||||
if (!_.isFullScreen.value ^ flag) {
|
if (!_.isFullScreen.value ^ flag) {
|
||||||
lastFullScreenToggleTime = DateTime.now();
|
lastFullScreenToggleTime = DateTime.now();
|
||||||
// 上滑进入全屏
|
// 上滑进入全屏
|
||||||
await widget.controller.triggerFullScreen(status: !flag);
|
await widget.controller.triggerFullScreen(status: !flag);
|
||||||
}
|
}
|
||||||
_distance = 0.0;
|
_distance.value = 0.0;
|
||||||
}
|
}
|
||||||
_distance = dy;
|
_distance.value = dy;
|
||||||
} else {
|
} else {
|
||||||
// 右边区域 👈
|
// 右边区域 👈
|
||||||
final double level = (_.isFullScreen.value
|
final double level = (_.isFullScreen.value
|
||||||
? Get.size.height
|
? Get.size.height
|
||||||
: screenWidth * 9 / 16) *
|
: screenWidth * 9 / 16) *
|
||||||
3;
|
3;
|
||||||
final double volume = _ctr.volumeValue.value - delta / level;
|
final double volume = _volumeValue.value - delta / level;
|
||||||
final double result = volume.clamp(0.0, 1.0);
|
final double result = volume.clamp(0.0, 1.0);
|
||||||
setVolume(result);
|
setVolume(result);
|
||||||
}
|
}
|
||||||
@ -585,9 +711,10 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
child: widget.bottomControl ??
|
child: widget.bottomControl ??
|
||||||
BottomControl(
|
BottomControl(
|
||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
triggerFullScreen:
|
triggerFullScreen: _.triggerFullScreen,
|
||||||
widget.controller.triggerFullScreen),
|
buildBottomControl: buildBottomControl(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -607,23 +734,23 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
}
|
}
|
||||||
if (defaultBtmProgressBehavior ==
|
if (defaultBtmProgressBehavior ==
|
||||||
BtmProgresBehavior.alwaysHide.code) {
|
BtmProgresBehavior.alwaysHide.code) {
|
||||||
return nil;
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
if (defaultBtmProgressBehavior ==
|
if (defaultBtmProgressBehavior ==
|
||||||
BtmProgresBehavior.onlyShowFullScreen.code &&
|
BtmProgresBehavior.onlyShowFullScreen.code &&
|
||||||
!_.isFullScreen.value) {
|
!_.isFullScreen.value) {
|
||||||
return nil;
|
return const SizedBox();
|
||||||
} else if (defaultBtmProgressBehavior ==
|
} else if (defaultBtmProgressBehavior ==
|
||||||
BtmProgresBehavior.onlyHideFullScreen.code &&
|
BtmProgresBehavior.onlyHideFullScreen.code &&
|
||||||
_.isFullScreen.value) {
|
_.isFullScreen.value) {
|
||||||
return nil;
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_.videoType.value == 'live') {
|
if (_.videoType.value == 'live') {
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
if (value > max || max <= 0) {
|
if (value > max || max <= 0) {
|
||||||
return nil;
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
return Positioned(
|
return Positioned(
|
||||||
bottom: -1.5,
|
bottom: -1.5,
|
||||||
@ -720,18 +847,17 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
/// 点击 快进/快退
|
/// 点击 快进/快退
|
||||||
Obx(
|
Obx(
|
||||||
() => Visibility(
|
() => Visibility(
|
||||||
visible: _ctr.mountSeekBackwardButton.value ||
|
visible:
|
||||||
_ctr.mountSeekForwardButton.value,
|
_mountSeekBackwardButton.value || _mountSeekForwardButton.value,
|
||||||
child: Positioned.fill(
|
child: Positioned.fill(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _ctr.mountSeekBackwardButton.value
|
child: _mountSeekBackwardButton.value
|
||||||
? TweenAnimationBuilder<double>(
|
? TweenAnimationBuilder<double>(
|
||||||
tween: Tween<double>(
|
tween: Tween<double>(
|
||||||
begin: 0.0,
|
begin: 0.0,
|
||||||
end:
|
end: _hideSeekBackwardButton.value ? 0.0 : 1.0,
|
||||||
_ctr.hideSeekBackwardButton.value ? 0.0 : 1.0,
|
|
||||||
),
|
),
|
||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
builder: (BuildContext context, double value,
|
builder: (BuildContext context, double value,
|
||||||
@ -741,17 +867,15 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
onEnd: () {
|
onEnd: () {
|
||||||
if (_ctr.hideSeekBackwardButton.value) {
|
if (_hideSeekBackwardButton.value) {
|
||||||
_ctr.hideSeekBackwardButton.value = false;
|
_hideSeekBackwardButton.value = false;
|
||||||
_ctr.mountSeekBackwardButton.value = false;
|
_mountSeekBackwardButton.value = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: BackwardSeekIndicator(
|
child: BackwardSeekIndicator(
|
||||||
onChanged: (Duration value) {
|
onChanged: (Duration value) => {},
|
||||||
// _seekBarDeltaValueNotifier.value = -value;
|
|
||||||
},
|
|
||||||
onSubmitted: (Duration value) {
|
onSubmitted: (Duration value) {
|
||||||
_ctr.hideSeekBackwardButton.value = true;
|
_hideSeekBackwardButton.value = true;
|
||||||
final Player player =
|
final Player player =
|
||||||
widget.controller.videoPlayerController!;
|
widget.controller.videoPlayerController!;
|
||||||
Duration result = player.state.position - value;
|
Duration result = player.state.position - value;
|
||||||
@ -764,7 +888,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: nil,
|
: const SizedBox(),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
@ -772,11 +896,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _ctr.mountSeekForwardButton.value
|
child: _mountSeekForwardButton.value
|
||||||
? TweenAnimationBuilder<double>(
|
? TweenAnimationBuilder<double>(
|
||||||
tween: Tween<double>(
|
tween: Tween<double>(
|
||||||
begin: 0.0,
|
begin: 0.0,
|
||||||
end: _ctr.hideSeekForwardButton.value ? 0.0 : 1.0,
|
end: _hideSeekForwardButton.value ? 0.0 : 1.0,
|
||||||
),
|
),
|
||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
builder: (BuildContext context, double value,
|
builder: (BuildContext context, double value,
|
||||||
@ -786,17 +910,15 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
onEnd: () {
|
onEnd: () {
|
||||||
if (_ctr.hideSeekForwardButton.value) {
|
if (_hideSeekForwardButton.value) {
|
||||||
_ctr.hideSeekForwardButton.value = false;
|
_hideSeekForwardButton.value = false;
|
||||||
_ctr.mountSeekForwardButton.value = false;
|
_mountSeekForwardButton.value = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: ForwardSeekIndicator(
|
child: ForwardSeekIndicator(
|
||||||
onChanged: (Duration value) {
|
onChanged: (Duration value) => {},
|
||||||
// _seekBarDeltaValueNotifier.value = value;
|
|
||||||
},
|
|
||||||
onSubmitted: (Duration value) {
|
onSubmitted: (Duration value) {
|
||||||
_ctr.hideSeekForwardButton.value = true;
|
_hideSeekForwardButton.value = true;
|
||||||
final Player player =
|
final Player player =
|
||||||
widget.controller.videoPlayerController!;
|
widget.controller.videoPlayerController!;
|
||||||
Duration result = player.state.position + value;
|
Duration result = player.state.position + value;
|
||||||
@ -809,7 +931,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: nil,
|
: const SizedBox(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -820,31 +942,3 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PLVideoPlayerController extends GetxController {
|
|
||||||
RxBool mountSeekBackwardButton = false.obs;
|
|
||||||
RxBool mountSeekForwardButton = false.obs;
|
|
||||||
RxBool hideSeekBackwardButton = false.obs;
|
|
||||||
RxBool hideSeekForwardButton = false.obs;
|
|
||||||
|
|
||||||
RxDouble brightnessValue = 0.0.obs;
|
|
||||||
RxBool brightnessIndicator = false.obs;
|
|
||||||
|
|
||||||
RxDouble volumeValue = 0.0.obs;
|
|
||||||
RxBool volumeIndicator = false.obs;
|
|
||||||
|
|
||||||
RxDouble distance = 0.0.obs;
|
|
||||||
// 初始手指落下位置
|
|
||||||
RxDouble initTapPositoin = 0.0.obs;
|
|
||||||
|
|
||||||
RxBool volumeInterceptEventStream = false.obs;
|
|
||||||
|
|
||||||
// 双击快进 展示样式
|
|
||||||
void onDoubleTapSeekForward() {
|
|
||||||
mountSeekForwardButton.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void onDoubleTapSeekBackward() {
|
|
||||||
mountSeekBackwardButton.value = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,17 +1,20 @@
|
|||||||
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
|
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:nil/nil.dart';
|
import 'package:nil/nil.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/index.dart';
|
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/widgets/play_pause_btn.dart';
|
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
|
|
||||||
class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
||||||
final PlPlayerController? controller;
|
final PlPlayerController? controller;
|
||||||
final Function? triggerFullScreen;
|
final Function? triggerFullScreen;
|
||||||
const BottomControl({this.controller, this.triggerFullScreen, Key? key})
|
final List<Widget>? buildBottomControl;
|
||||||
: super(key: key);
|
const BottomControl({
|
||||||
|
this.controller,
|
||||||
|
this.triggerFullScreen,
|
||||||
|
this.buildBottomControl,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Size get preferredSize => const Size(double.infinity, kToolbarHeight);
|
Size get preferredSize => const Size(double.infinity, kToolbarHeight);
|
||||||
@ -20,11 +23,6 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Color colorTheme = Theme.of(context).colorScheme.primary;
|
Color colorTheme = Theme.of(context).colorScheme.primary;
|
||||||
final _ = controller!;
|
final _ = controller!;
|
||||||
const textStyle = TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 12,
|
|
||||||
);
|
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
height: 90,
|
height: 90,
|
||||||
@ -71,86 +69,89 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [...buildBottomControl!],
|
||||||
PlayOrPauseButton(
|
|
||||||
controller: _,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
// 播放时间
|
|
||||||
Obx(() {
|
|
||||||
return Text(
|
|
||||||
_.durationSeconds.value >= 3600
|
|
||||||
? printDurationWithHours(
|
|
||||||
Duration(seconds: _.positionSeconds.value))
|
|
||||||
: printDuration(
|
|
||||||
Duration(seconds: _.positionSeconds.value)),
|
|
||||||
style: textStyle,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
const SizedBox(width: 2),
|
|
||||||
const Text('/', style: textStyle),
|
|
||||||
const SizedBox(width: 2),
|
|
||||||
Obx(
|
|
||||||
() => Text(
|
|
||||||
_.durationSeconds.value >= 3600
|
|
||||||
? printDurationWithHours(
|
|
||||||
Duration(seconds: _.durationSeconds.value))
|
|
||||||
: printDuration(
|
|
||||||
Duration(seconds: _.durationSeconds.value)),
|
|
||||||
style: textStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
// 倍速
|
|
||||||
// Obx(
|
|
||||||
// () => SizedBox(
|
|
||||||
// width: 45,
|
|
||||||
// height: 34,
|
|
||||||
// child: TextButton(
|
|
||||||
// style: ButtonStyle(
|
|
||||||
// padding: MaterialStateProperty.all(EdgeInsets.zero),
|
|
||||||
// ),
|
|
||||||
// onPressed: () {
|
|
||||||
// _.togglePlaybackSpeed();
|
|
||||||
// },
|
|
||||||
// child: Text(
|
|
||||||
// '${_.playbackSpeed.toString()}X',
|
|
||||||
// style: textStyle,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
SizedBox(
|
|
||||||
height: 30,
|
|
||||||
child: TextButton(
|
|
||||||
onPressed: () => _.toggleVideoFit(),
|
|
||||||
style: ButtonStyle(
|
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
|
||||||
),
|
|
||||||
child: Obx(
|
|
||||||
() => Text(
|
|
||||||
_.videoFitDEsc.value,
|
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 13),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
// 全屏
|
|
||||||
Obx(
|
|
||||||
() => ComBtn(
|
|
||||||
icon: Icon(
|
|
||||||
_.isFullScreen.value
|
|
||||||
? FontAwesomeIcons.compress
|
|
||||||
: FontAwesomeIcons.expand,
|
|
||||||
size: 15,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
fuc: () => triggerFullScreen!(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
// Row(
|
||||||
|
// children: [
|
||||||
|
// PlayOrPauseButton(
|
||||||
|
// controller: _,
|
||||||
|
// ),
|
||||||
|
// const SizedBox(width: 4),
|
||||||
|
// // 播放时间
|
||||||
|
// Obx(() {
|
||||||
|
// return Text(
|
||||||
|
// _.durationSeconds.value >= 3600
|
||||||
|
// ? printDurationWithHours(
|
||||||
|
// Duration(seconds: _.positionSeconds.value))
|
||||||
|
// : printDuration(
|
||||||
|
// Duration(seconds: _.positionSeconds.value)),
|
||||||
|
// style: textStyle,
|
||||||
|
// );
|
||||||
|
// }),
|
||||||
|
// const SizedBox(width: 2),
|
||||||
|
// const Text('/', style: textStyle),
|
||||||
|
// const SizedBox(width: 2),
|
||||||
|
// Obx(
|
||||||
|
// () => Text(
|
||||||
|
// _.durationSeconds.value >= 3600
|
||||||
|
// ? printDurationWithHours(
|
||||||
|
// Duration(seconds: _.durationSeconds.value))
|
||||||
|
// : printDuration(
|
||||||
|
// Duration(seconds: _.durationSeconds.value)),
|
||||||
|
// style: textStyle,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// const Spacer(),
|
||||||
|
// // 倍速
|
||||||
|
// // Obx(
|
||||||
|
// // () => SizedBox(
|
||||||
|
// // width: 45,
|
||||||
|
// // height: 34,
|
||||||
|
// // child: TextButton(
|
||||||
|
// // style: ButtonStyle(
|
||||||
|
// // padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
|
// // ),
|
||||||
|
// // onPressed: () {
|
||||||
|
// // _.togglePlaybackSpeed();
|
||||||
|
// // },
|
||||||
|
// // child: Text(
|
||||||
|
// // '${_.playbackSpeed.toString()}X',
|
||||||
|
// // style: textStyle,
|
||||||
|
// // ),
|
||||||
|
// // ),
|
||||||
|
// // ),
|
||||||
|
// // ),
|
||||||
|
// SizedBox(
|
||||||
|
// height: 30,
|
||||||
|
// child: TextButton(
|
||||||
|
// onPressed: () => _.toggleVideoFit(),
|
||||||
|
// style: ButtonStyle(
|
||||||
|
// padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
|
// ),
|
||||||
|
// child: Obx(
|
||||||
|
// () => Text(
|
||||||
|
// _.videoFitDEsc.value,
|
||||||
|
// style: const TextStyle(color: Colors.white, fontSize: 13),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// const SizedBox(width: 10),
|
||||||
|
// // 全屏
|
||||||
|
// Obx(
|
||||||
|
// () => ComBtn(
|
||||||
|
// icon: Icon(
|
||||||
|
// _.isFullScreen.value
|
||||||
|
// ? FontAwesomeIcons.compress
|
||||||
|
// : FontAwesomeIcons.expand,
|
||||||
|
// size: 15,
|
||||||
|
// color: Colors.white,
|
||||||
|
// ),
|
||||||
|
// fuc: () => triggerFullScreen!(),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -84,6 +84,7 @@ class SettingBoxKey {
|
|||||||
autoUpgradeEnable = 'autoUpgradeEnable',
|
autoUpgradeEnable = 'autoUpgradeEnable',
|
||||||
feedBackEnable = 'feedBackEnable',
|
feedBackEnable = 'feedBackEnable',
|
||||||
defaultVideoQa = 'defaultVideoQa',
|
defaultVideoQa = 'defaultVideoQa',
|
||||||
|
defaultLiveQa = 'defaultLiveQa',
|
||||||
defaultAudioQa = 'defaultAudioQa',
|
defaultAudioQa = 'defaultAudioQa',
|
||||||
autoPlayEnable = 'autoPlayEnable',
|
autoPlayEnable = 'autoPlayEnable',
|
||||||
fullScreenMode = 'fullScreenMode',
|
fullScreenMode = 'fullScreenMode',
|
||||||
|
|||||||
Reference in New Issue
Block a user