opt: video page

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2024-12-22 10:50:00 +08:00
parent a01d54cd80
commit 0b311d37c8
6 changed files with 612 additions and 529 deletions

View File

@@ -129,7 +129,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
builder: (BuildContext context, AsyncSnapshot snapshot) { builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData && snapshot.data['status']) { if (snapshot.hasData && snapshot.data['status']) {
return PLVideoPlayer( return PLVideoPlayer(
controller: plPlayerController, plPlayerController: plPlayerController,
bottomControl: BottomControl( bottomControl: BottomControl(
controller: plPlayerController, controller: plPlayerController,
liveRoomCtr: _liveRoomController, liveRoomCtr: _liveRoomController,

View File

@@ -165,6 +165,7 @@ class _RecommendSettingState extends State<RecommendSetting> {
children: [ children: [
const Text('使用|隔开,如:尝试|测试'), const Text('使用|隔开,如:尝试|测试'),
TextField( TextField(
autofocus: true,
controller: textController, controller: textController,
textInputAction: TextInputAction.newline, textInputAction: TextInputAction.newline,
minLines: 1, minLines: 1,

View File

@@ -892,7 +892,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
() => !videoDetailController.autoPlay.value () => !videoDetailController.autoPlay.value
? const SizedBox() ? const SizedBox()
: PLVideoPlayer( : PLVideoPlayer(
controller: plPlayerController!, plPlayerController: plPlayerController!,
videoIntroController: videoIntroController:
videoDetailController.videoType == SearchType.video videoDetailController.videoType == SearchType.video
? videoIntroController ? videoIntroController
@@ -936,38 +936,73 @@ class _VideoDetailPageState extends State<VideoDetailPage>
primary: false, primary: false,
foregroundColor: Colors.white, foregroundColor: Colors.white,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
actions: videoDetailController.userInfo == null // automaticallyImplyLeading: false,
? null // title: Row(
: [ // children: [
PopupMenuButton<String>( // SizedBox(
onSelected: (String type) async { // width: 42,
switch (type) { // height: 34,
case 'later': // child: IconButton(
var res = await UserHttp.toViewLater( // tooltip: '返回',
bvid: videoDetailController.bvid); // icon: const Icon(
SmartDialog.showToast(res['msg']); // FontAwesomeIcons.arrowLeft,
break; // size: 15,
case 'report': // color: Colors.white,
Get.toNamed('/webviewnew', parameters: { // ),
'url': // onPressed: Get.back,
'https://www.bilibili.com/appeal/?avid=${IdUtils.bv2av(videoDetailController.bvid)}&bvid=${videoDetailController.bvid}' // ),
}); // ),
break; // SizedBox(
} // width: 42,
}, // height: 34,
itemBuilder: (BuildContext context) => // child: IconButton(
<PopupMenuEntry<String>>[ // tooltip: '返回主页',
const PopupMenuItem<String>( // icon: const Icon(
value: 'later', // FontAwesomeIcons.house,
child: Text('稍后再看'), // size: 15,
), // color: Colors.white,
const PopupMenuItem<String>( // ),
value: 'report', // onPressed: () {
child: Text('举报'), // Get.until((route) => route.isFirst);
), // },
], // ),
), // ),
], // ],
// ),
actions: [
PopupMenuButton<String>(
onSelected: (String type) async {
switch (type) {
case 'later':
var res = await UserHttp.toViewLater(
bvid: videoDetailController.bvid);
SmartDialog.showToast(res['msg']);
break;
case 'report':
if (videoDetailController.userInfo == null) {
SmartDialog.showToast('账号未登录');
} else {
Get.toNamed('/webviewnew', parameters: {
'url':
'https://www.bilibili.com/appeal/?avid=${IdUtils.bv2av(videoDetailController.bvid)}&bvid=${videoDetailController.bvid}'
});
}
break;
}
},
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<String>>[
const PopupMenuItem<String>(
value: 'later',
child: Text('稍后再看'),
),
const PopupMenuItem<String>(
value: 'report',
child: Text('举报'),
),
],
),
],
), ),
), ),
Positioned( Positioned(
@@ -998,7 +1033,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
plPlayerController!.videoController == null plPlayerController!.videoController == null
? nil ? nil
: PLVideoPlayer( : PLVideoPlayer(
controller: plPlayerController!, plPlayerController: plPlayerController!,
videoIntroController: videoIntroController:
videoDetailController.videoType == SearchType.video videoDetailController.videoType == SearchType.video
? videoIntroController ? videoIntroController

View File

@@ -132,7 +132,12 @@ class _HeaderControlState extends State<HeaderControl> {
color: Theme.of(context).colorScheme.surface, color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.all(Radius.circular(12)), borderRadius: const BorderRadius.all(Radius.circular(12)),
), ),
margin: const EdgeInsets.all(12), margin: EdgeInsets.only(
left: 12,
top: 12,
right: 12,
bottom: 12 + MediaQuery.paddingOf(context).bottom,
),
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
SizedBox( SizedBox(
@@ -152,331 +157,367 @@ class _HeaderControlState extends State<HeaderControl> {
), ),
), ),
Expanded( Expanded(
child: Material( child: Material(
child: ListView( child: MediaQuery.removePadding(
children: [ context: context,
// ListTile( removeBottom: true,
// onTap: () {}, child: ListView(
// dense: true, children: [
// enabled: false, // ListTile(
// leading: // onTap: () {},
// const Icon(Icons.network_cell_outlined, size: 20), // dense: true,
// title: Text('省流模式', style: titleStyle), // enabled: false,
// subtitle: Text('低画质 减少视频缓存', style: subTitleStyle), // leading:
// trailing: Transform.scale( // const Icon(Icons.network_cell_outlined, size: 20),
// scale: 0.75, // title: Text('省流模式', style: titleStyle),
// child: Switch( // subtitle: Text('低画质 减少视频缓存', style: subTitleStyle),
// thumbIcon: WidgetStateProperty.resolveWith<Icon?>( // trailing: Transform.scale(
// (Set<WidgetState> states) { // scale: 0.75,
// if (states.isNotEmpty && // child: Switch(
// states.first == WidgetState.selected) { // thumbIcon: WidgetStateProperty.resolveWith<Icon?>(
// return const Icon(Icons.done); // (Set<WidgetState> states) {
// } // if (states.isNotEmpty &&
// return null; // All other states will use the default thumbIcon. // states.first == WidgetState.selected) {
// }), // return const Icon(Icons.done);
// value: false, // }
// onChanged: (value) => {}, // return null; // All other states will use the default thumbIcon.
// ), // }),
// ), // value: false,
// ), // onChanged: (value) => {},
if (widget.videoDetailCtr?.userInfo != null) // ),
ListTile( // ),
onTap: () { // ),
Get.back(); // if (widget.videoDetailCtr?.userInfo != null)
Get.toNamed('/webviewnew', parameters: { ListTile(
'url': dense: true,
'https://www.bilibili.com/appeal/?avid=${IdUtils.bv2av(widget.videoDetailCtr!.bvid)}&bvid=${widget.videoDetailCtr!.bvid}' onTap: () {
}); if (widget.videoDetailCtr?.userInfo == null) {
}, SmartDialog.showToast('账号未登录');
dense: true, return;
leading: const Icon(Icons.error_outline, size: 20), }
title: const Text('举报', style: titleStyle), Get.back();
), Get.toNamed('/webviewnew', parameters: {
ListTile( 'url':
onTap: () async { 'https://www.bilibili.com/appeal/?avid=${IdUtils.bv2av(widget.videoDetailCtr!.bvid)}&bvid=${widget.videoDetailCtr!.bvid}'
Get.back(); });
final res = await UserHttp.toViewLater(
bvid: widget.videoDetailCtr!.bvid);
SmartDialog.showToast(res['msg']);
},
dense: true,
leading: const Icon(Icons.watch_later_outlined, size: 20),
title: const Text('添加至「稍后再看」', style: titleStyle),
),
ListTile(
onTap: () => {Get.back(), scheduleExit()},
dense: true,
leading:
const Icon(Icons.hourglass_top_outlined, size: 20),
title: const Text('定时关闭', style: titleStyle),
),
ListTile(
onTap: () =>
{Get.back(), widget.videoDetailCtr!.queryVideoUrl()},
dense: true,
leading: const Icon(Icons.refresh_outlined, size: 20),
title: const Text('重载视频', style: titleStyle),
),
ListTile(
title: const Text('CDN 设置', style: titleStyle),
leading: Icon(MdiIcons.cloudPlusOutline, size: 20),
subtitle: Text(
'当前:${CDNServiceCode.fromCode(defaultCDNService)!.description},无法播放请切换',
style: subTitleStyle,
),
onTap: () async {
Get.back();
String? result = await showDialog(
context: context,
builder: (context) {
return SelectDialog<String>(
title: 'CDN 设置',
value: defaultCDNService,
values: CDNService.values.map((e) {
return {
'title': e.description,
'value': e.code
};
}).toList());
}, },
); leading: const Icon(Icons.error_outline, size: 20),
if (result != null) { title: const Text('举报', style: titleStyle),
defaultCDNService = result; ),
setting.put(SettingBoxKey.CDNService, result); ListTile(
SmartDialog.showToast( dense: true,
'已设置为 ${CDNServiceCode.fromCode(result)!.description},正在重载视频'); onTap: () async {
setState(() {}); Get.back();
widget.videoDetailCtr!.queryVideoUrl(); final res = await UserHttp.toViewLater(
} bvid: widget.videoDetailCtr!.bvid);
}, SmartDialog.showToast(res['msg']);
), },
ListTile( leading:
onTap: () { const Icon(Icons.watch_later_outlined, size: 20),
Get.back(); title: const Text('添加至「稍后再看」', style: titleStyle),
Player? player = ),
widget.controller?.videoPlayerController; ListTile(
if (player == null) { dense: true,
SmartDialog.showToast('播放器未初始化'); onTap: () => {Get.back(), scheduleExit()},
return; leading: const Icon(Icons.hourglass_top_outlined,
} size: 20),
var pp = player.platform as NativePlayer; title: const Text('定时关闭', style: titleStyle),
pp.setProperty("video", "no"); ),
}, ListTile(
dense: true, dense: true,
leading: const Icon(Icons.headphones_outlined, size: 20), onTap: () => {
title: const Text('听视频(需返回首页才能终止该状态)', style: titleStyle), Get.back(),
), widget.videoDetailCtr!.queryVideoUrl()
ListTile( },
onTap: () => {Get.back(), showSetVideoQa()}, leading: const Icon(Icons.refresh_outlined, size: 20),
dense: true, title: const Text('重载视频', style: titleStyle),
leading: const Icon(Icons.play_circle_outline, size: 20), ),
title: const Text('选择画质', style: titleStyle), ListTile(
subtitle: Text( dense: true,
'当前画质 ${widget.videoDetailCtr!.currentVideoQa.description}', title: const Text('CDN 设置', style: titleStyle),
style: subTitleStyle), leading: Icon(MdiIcons.cloudPlusOutline, size: 20),
), subtitle: Text(
if (widget.videoDetailCtr!.currentAudioQa != null) '当前:${CDNServiceCode.fromCode(defaultCDNService)!.description},无法播放请切换',
ListTile( style: subTitleStyle,
onTap: () => {Get.back(), showSetAudioQa()}, ),
dense: true, onTap: () async {
leading: const Icon(Icons.album_outlined, size: 20), Get.back();
title: const Text('选择音质', style: titleStyle), String? result = await showDialog(
subtitle: Text( context: context,
'当前音质 ${widget.videoDetailCtr!.currentAudioQa!.description}', builder: (context) {
style: subTitleStyle), return SelectDialog<String>(
), title: 'CDN 设置',
ListTile( value: defaultCDNService,
onTap: () => {Get.back(), showSetDecodeFormats()}, values: CDNService.values.map((e) {
dense: true, return {
leading: const Icon(Icons.av_timer_outlined, size: 20), 'title': e.description,
title: const Text('解码格式', style: titleStyle), 'value': e.code
subtitle: Text( };
'当前解码格式 ${widget.videoDetailCtr!.currentDecodeFormats.description}', }).toList());
style: subTitleStyle), },
), );
ListTile( if (result != null) {
onTap: () => {Get.back(), showSetRepeat()}, defaultCDNService = result;
dense: true, setting.put(SettingBoxKey.CDNService, result);
leading: const Icon(Icons.repeat, size: 20), SmartDialog.showToast(
title: const Text('播放顺序', style: titleStyle), '已设置为 ${CDNServiceCode.fromCode(result)!.description},正在重载视频');
subtitle: Text(widget.controller!.playRepeat.description, setState(() {});
style: subTitleStyle), widget.videoDetailCtr!.queryVideoUrl();
), }
ListTile( },
onTap: () => {Get.back(), showSetDanmaku()}, ),
dense: true, ListTile(
leading: const Icon(Icons.subtitles_outlined, size: 20), dense: true,
title: const Text('弹幕设置', style: titleStyle), onTap: () {
), Get.back();
ListTile( Player? player =
title: const Text('播放信息', style: titleStyle), widget.controller?.videoPlayerController;
leading: const Icon(Icons.info_outline, size: 20), if (player == null) {
onTap: () { SmartDialog.showToast('播放器未初始化');
Player? player = return;
widget.controller?.videoPlayerController; }
if (player == null) { var pp = player.platform as NativePlayer;
SmartDialog.showToast('播放器未初始化'); pp.setProperty("video", "no");
return; },
} leading:
showDialog( const Icon(Icons.headphones_outlined, size: 20),
context: context, title: const Text('听视频(需返回首页才能终止该状态)',
builder: (context) { style: titleStyle),
return AlertDialog( ),
title: const Text('播放信息'), ListTile(
content: SizedBox( dense: true,
width: double.maxFinite, onTap: () => {Get.back(), showSetVideoQa()},
child: ListView( leading:
children: [ const Icon(Icons.play_circle_outline, size: 20),
ListTile( title: const Text('选择画质', style: titleStyle),
title: const Text("Resolution"), subtitle: Text(
subtitle: Text( '当前画质 ${widget.videoDetailCtr!.currentVideoQa.description}',
'${player.state.width}x${player.state.height}'), style: subTitleStyle),
onTap: () { ),
Clipboard.setData( if (widget.videoDetailCtr!.currentAudioQa != null)
ClipboardData( ListTile(
text: dense: true,
"Resolution\n${player.state.width}x${player.state.height}", onTap: () => {Get.back(), showSetAudioQa()},
), leading: const Icon(Icons.album_outlined, size: 20),
); title: const Text('选择音质', style: titleStyle),
}, subtitle: Text(
), '当前音质 ${widget.videoDetailCtr!.currentAudioQa!.description}',
ListTile( style: subTitleStyle),
title: const Text("VideoParams"), ),
subtitle: Text(player.state.videoParams ListTile(
.toString()), dense: true,
onTap: () { onTap: () => {Get.back(), showSetDecodeFormats()},
Clipboard.setData( leading:
ClipboardData( const Icon(Icons.av_timer_outlined, size: 20),
text: title: const Text('解码格式', style: titleStyle),
"VideoParams\n${player.state.videoParams}", subtitle: Text(
), '当前解码格式 ${widget.videoDetailCtr!.currentDecodeFormats.description}',
); style: subTitleStyle),
}, ),
), ListTile(
ListTile( dense: true,
title: const Text("AudioParams"), onTap: () => {Get.back(), showSetRepeat()},
subtitle: Text(player.state.audioParams leading: const Icon(Icons.repeat, size: 20),
.toString()), title: const Text('播放顺序', style: titleStyle),
onTap: () { subtitle: Text(
Clipboard.setData( widget.controller!.playRepeat.description,
ClipboardData( style: subTitleStyle),
text: ),
"AudioParams\n${player.state.audioParams}", ListTile(
), dense: true,
); onTap: () => {Get.back(), showSetDanmaku()},
}, leading:
), const Icon(Icons.subtitles_outlined, size: 20),
ListTile( title: const Text('弹幕设置', style: titleStyle),
title: const Text("Media"), ),
subtitle: Text( ListTile(
player.state.playlist.toString()), dense: true,
onTap: () { title: const Text('播放信息', style: titleStyle),
Clipboard.setData( leading: const Icon(Icons.info_outline, size: 20),
ClipboardData( onTap: () {
text: Player? player =
"Media\n${player.state.playlist}", widget.controller?.videoPlayerController;
), if (player == null) {
); SmartDialog.showToast('播放器未初始化');
}, return;
), }
ListTile( showDialog(
title: const Text("AudioTrack"), context: context,
subtitle: Text(player.state.track.audio builder: (context) {
.toString()), return AlertDialog(
onTap: () { title: const Text('播放信息'),
Clipboard.setData( content: SizedBox(
ClipboardData( width: double.maxFinite,
text: child: ListView(
"AudioTrack\n${player.state.track.audio}", children: [
), ListTile(
); dense: true,
}, title: const Text("Resolution"),
),
ListTile(
title: const Text("VideoTrack"),
subtitle: Text(player.state.track.video
.toString()),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"VideoTrack\n${player.state.track.audio}",
),
);
},
),
ListTile(
title: const Text("pitch"),
subtitle: Text( subtitle: Text(
player.state.pitch.toString()), '${player.state.width}x${player.state.height}'),
onTap: () { onTap: () {
Clipboard.setData( Clipboard.setData(
ClipboardData( ClipboardData(
text: text:
"pitch\n${player.state.pitch}", "Resolution\n${player.state.width}x${player.state.height}",
), ),
); );
}), },
ListTile( ),
title: const Text("rate"), ListTile(
subtitle: Text( dense: true,
player.state.rate.toString()), title: const Text("VideoParams"),
subtitle: Text(player
.state.videoParams
.toString()),
onTap: () { onTap: () {
Clipboard.setData( Clipboard.setData(
ClipboardData( ClipboardData(
text: text:
"rate\n${player.state.rate}", "VideoParams\n${player.state.videoParams}",
), ),
); );
}), },
ListTile( ),
title: const Text("AudioBitrate"), ListTile(
subtitle: Text(player.state.audioBitrate dense: true,
.toString()), title: const Text("AudioParams"),
onTap: () { subtitle: Text(player
Clipboard.setData( .state.audioParams
ClipboardData( .toString()),
text: onTap: () {
"AudioBitrate\n${player.state.audioBitrate}", Clipboard.setData(
), ClipboardData(
); text:
}, "AudioParams\n${player.state.audioParams}",
), ),
ListTile( );
title: const Text("Volume"), },
subtitle: Text( ),
player.state.volume.toString()), ListTile(
onTap: () { dense: true,
Clipboard.setData( title: const Text("Media"),
ClipboardData( subtitle: Text(
text: player.state.playlist.toString()),
"Volume\n${player.state.volume}", onTap: () {
), Clipboard.setData(
); ClipboardData(
}, text:
), "Media\n${player.state.playlist}",
], ),
), );
), },
actions: [ ),
TextButton( ListTile(
onPressed: () => Get.back(), dense: true,
child: Text( title: const Text("AudioTrack"),
'确定', subtitle: Text(player
style: TextStyle( .state.track.audio
color: Theme.of(context) .toString()),
.colorScheme onTap: () {
.outline), Clipboard.setData(
ClipboardData(
text:
"AudioTrack\n${player.state.track.audio}",
),
);
},
),
ListTile(
dense: true,
title: const Text("VideoTrack"),
subtitle: Text(player
.state.track.video
.toString()),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"VideoTrack\n${player.state.track.audio}",
),
);
},
),
ListTile(
dense: true,
title: const Text("pitch"),
subtitle: Text(
player.state.pitch.toString()),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"pitch\n${player.state.pitch}",
),
);
}),
ListTile(
dense: true,
title: const Text("rate"),
subtitle: Text(
player.state.rate.toString()),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"rate\n${player.state.rate}",
),
);
}),
ListTile(
dense: true,
title: const Text("AudioBitrate"),
subtitle: Text(player
.state.audioBitrate
.toString()),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"AudioBitrate\n${player.state.audioBitrate}",
),
);
},
),
ListTile(
dense: true,
title: const Text("Volume"),
subtitle: Text(
player.state.volume.toString()),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"Volume\n${player.state.volume}",
),
);
},
),
],
), ),
), ),
], actions: [
); TextButton(
}, onPressed: () => Get.back(),
); child: Text(
}) '确定',
], style: TextStyle(
color: Theme.of(context)
.colorScheme
.outline),
),
),
],
);
},
);
},
),
],
),
),
), ),
)) ),
], ],
), ),
); );
@@ -524,6 +565,7 @@ class _HeaderControlState extends State<HeaderControl> {
const SizedBox(height: 10), const SizedBox(height: 10),
for (final int choice in scheduleTimeChoices) ...<Widget>[ for (final int choice in scheduleTimeChoices) ...<Widget>[
ListTile( ListTile(
dense: true,
onTap: () { onTap: () {
shutdownTimerService.scheduledExitInMinutes = shutdownTimerService.scheduledExitInMinutes =
choice; choice;
@@ -531,7 +573,6 @@ class _HeaderControlState extends State<HeaderControl> {
Get.back(); Get.back();
}, },
contentPadding: const EdgeInsets.only(), contentPadding: const EdgeInsets.only(),
dense: true,
title: Text(choice == -1 ? "禁用" : "$choice分钟后"), title: Text(choice == -1 ? "禁用" : "$choice分钟后"),
trailing: shutdownTimerService trailing: shutdownTimerService
.scheduledExitInMinutes == .scheduledExitInMinutes ==
@@ -551,12 +592,12 @@ class _HeaderControlState extends State<HeaderControl> {
)), )),
const SizedBox(height: 10), const SizedBox(height: 10),
ListTile( ListTile(
dense: true,
onTap: () { onTap: () {
shutdownTimerService.waitForPlayingCompleted = shutdownTimerService.waitForPlayingCompleted =
!shutdownTimerService.waitForPlayingCompleted; !shutdownTimerService.waitForPlayingCompleted;
setState(() {}); setState(() {});
}, },
dense: true,
contentPadding: const EdgeInsets.only(), contentPadding: const EdgeInsets.only(),
title: const Text("额外等待视频播放完毕", style: titleStyle), title: const Text("额外等待视频播放完毕", style: titleStyle),
trailing: Switch( trailing: Switch(
@@ -681,6 +722,7 @@ class _HeaderControlState extends State<HeaderControl> {
children: [ children: [
for (int i = 0; i < totalQaSam; i++) ...[ for (int i = 0; i < totalQaSam; i++) ...[
ListTile( ListTile(
dense: true,
onTap: () { onTap: () {
if (currentVideoQa.code == if (currentVideoQa.code ==
videoFormat[i].quality) { videoFormat[i].quality) {
@@ -701,7 +743,6 @@ class _HeaderControlState extends State<HeaderControl> {
widget.videoDetailCtr!.updatePlayer(); widget.videoDetailCtr!.updatePlayer();
Get.back(); Get.back();
}, },
dense: true,
// 可能包含会员解锁画质 // 可能包含会员解锁画质
enabled: i >= totalQaSam - userfulQaSam, enabled: i >= totalQaSam - userfulQaSam,
contentPadding: contentPadding:
@@ -761,6 +802,7 @@ class _HeaderControlState extends State<HeaderControl> {
children: <Widget>[ children: <Widget>[
for (final AudioItem i in audio) ...<Widget>[ for (final AudioItem i in audio) ...<Widget>[
ListTile( ListTile(
dense: true,
onTap: () { onTap: () {
if (currentAudioQa.code == i.id) { if (currentAudioQa.code == i.id) {
return; return;
@@ -779,7 +821,6 @@ class _HeaderControlState extends State<HeaderControl> {
widget.videoDetailCtr!.updatePlayer(); widget.videoDetailCtr!.updatePlayer();
Get.back(); Get.back();
}, },
dense: true,
contentPadding: contentPadding:
const EdgeInsets.only(left: 20, right: 20), const EdgeInsets.only(left: 20, right: 20),
title: Text(i.quality!), title: Text(i.quality!),
@@ -847,6 +888,7 @@ class _HeaderControlState extends State<HeaderControl> {
children: [ children: [
for (var i in list) ...[ for (var i in list) ...[
ListTile( ListTile(
dense: true,
onTap: () { onTap: () {
if (i.startsWith(currentDecodeFormats.code)) return; if (i.startsWith(currentDecodeFormats.code)) return;
widget.videoDetailCtr!.currentDecodeFormats = widget.videoDetailCtr!.currentDecodeFormats =
@@ -854,7 +896,6 @@ class _HeaderControlState extends State<HeaderControl> {
widget.videoDetailCtr!.updatePlayer(); widget.videoDetailCtr!.updatePlayer();
Get.back(); Get.back();
}, },
dense: true,
contentPadding: contentPadding:
const EdgeInsets.only(left: 20, right: 20), const EdgeInsets.only(left: 20, right: 20),
title: Text(VideoDecodeFormatsCode.fromString(i)! title: Text(VideoDecodeFormatsCode.fromString(i)!
@@ -940,7 +981,9 @@ class _HeaderControlState extends State<HeaderControl> {
left: 12, left: 12,
top: 12, top: 12,
right: 12, right: 12,
bottom: widget.controller?.isFullScreen.value == true ? 70 : 12, bottom:
(widget.controller?.isFullScreen.value == true ? 70 : 12) +
MediaQuery.paddingOf(context).bottom,
), ),
padding: const EdgeInsets.only(left: 14, right: 14), padding: const EdgeInsets.only(left: 14, right: 14),
child: SingleChildScrollView( child: SingleChildScrollView(
@@ -1427,11 +1470,11 @@ class _HeaderControlState extends State<HeaderControl> {
children: <Widget>[ children: <Widget>[
for (final PlayRepeat i in PlayRepeat.values) ...[ for (final PlayRepeat i in PlayRepeat.values) ...[
ListTile( ListTile(
dense: true,
onTap: () { onTap: () {
widget.controller!.setPlayRepeat(i); widget.controller!.setPlayRepeat(i);
Get.back(); Get.back();
}, },
dense: true,
contentPadding: contentPadding:
const EdgeInsets.only(left: 20, right: 20), const EdgeInsets.only(left: 20, right: 20),
title: Text(i.description), title: Text(i.description),
@@ -1481,45 +1524,45 @@ class _HeaderControlState extends State<HeaderControl> {
title: Row( title: Row(
children: [ children: [
SizedBox( SizedBox(
width: 42,
height: 34,
child: IconButton(
tooltip: '返回',
icon: const Icon(
FontAwesomeIcons.arrowLeft,
size: 15,
color: Colors.white,
),
onPressed: () {
if (isFullScreen) {
widget.controller!.triggerFullScreen(status: false);
} else if (MediaQuery.of(context).orientation ==
Orientation.landscape &&
!horizontalScreen) {
verticalScreenForTwoSeconds();
} else {
Get.back();
}
},
),
),
if (!isFullScreen ||
MediaQuery.of(context).orientation != Orientation.portrait)
SizedBox(
width: 42, width: 42,
height: 34, height: 34,
child: IconButton( child: IconButton(
tooltip: '上一', tooltip: '返回主',
icon: const Icon( icon: const Icon(
FontAwesomeIcons.arrowLeft, FontAwesomeIcons.house,
size: 15, size: 15,
color: Colors.white, color: Colors.white,
), ),
onPressed: () { onPressed: () {
if (isFullScreen) { Get.until((route) => route.isFirst);
widget.controller!.triggerFullScreen(status: false);
} else if (MediaQuery.of(context).orientation ==
Orientation.landscape &&
!horizontalScreen) {
verticalScreenForTwoSeconds();
} else {
Get.back();
}
}, },
)), ),
if (!isFullScreen || ),
MediaQuery.of(context).orientation != Orientation.portrait)
SizedBox(
width: 42,
height: 34,
child: IconButton(
tooltip: '返回主页',
icon: const Icon(
FontAwesomeIcons.house,
size: 15,
color: Colors.white,
),
onPressed: () async {
// 销毁播放器实例
// await widget.controller!.dispose(type: 'all');
Get.until((route) => route.isFirst);
},
)),
if ((videoIntroController.videoDetail.value.title != null) && if ((videoIntroController.videoDetail.value.title != null) &&
(isFullScreen || equivalentFullScreen)) (isFullScreen || equivalentFullScreen))
Column( Column(
@@ -1579,9 +1622,7 @@ class _HeaderControlState extends State<HeaderControl> {
), ),
), ),
), ),
const SizedBox( const SizedBox(width: 15),
width: 15,
),
], ],
// ComBtn( // ComBtn(
// icon: const Icon( // icon: const Icon(
@@ -1780,7 +1821,7 @@ class _HeaderControlState extends State<HeaderControl> {
style: ButtonStyle( style: ButtonStyle(
padding: WidgetStateProperty.all(EdgeInsets.zero), padding: WidgetStateProperty.all(EdgeInsets.zero),
), ),
onPressed: () => showSettingSheet(), onPressed: showSettingSheet,
icon: const Icon( icon: const Icon(
Icons.more_vert_outlined, Icons.more_vert_outlined,
size: 19, size: 19,

View File

@@ -38,7 +38,7 @@ import 'widgets/play_pause_btn.dart';
class PLVideoPlayer extends StatefulWidget { class PLVideoPlayer extends StatefulWidget {
const PLVideoPlayer({ const PLVideoPlayer({
required this.controller, required this.plPlayerController,
this.videoIntroController, this.videoIntroController,
this.bangumiIntroController, this.bangumiIntroController,
this.headerControl, this.headerControl,
@@ -52,7 +52,7 @@ class PLVideoPlayer extends StatefulWidget {
super.key, super.key,
}); });
final PlPlayerController controller; final PlPlayerController plPlayerController;
final VideoIntroController? videoIntroController; final VideoIntroController? videoIntroController;
final BangumiIntroController? bangumiIntroController; final BangumiIntroController? bangumiIntroController;
final PreferredSizeWidget? headerControl; final PreferredSizeWidget? headerControl;
@@ -123,7 +123,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
// 双击播放、暂停 // 双击播放、暂停
void onDoubleTapCenter() { void onDoubleTapCenter() {
final PlPlayerController plPlayerController = widget.controller;
plPlayerController.videoPlayerController!.playOrPause(); plPlayerController.videoPlayerController!.playOrPause();
} }
@@ -152,12 +151,12 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
super.initState(); super.initState();
animationController = AnimationController( animationController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 100)); vsync: this, duration: const Duration(milliseconds: 100));
videoController = widget.controller.videoController!; videoController = plPlayerController.videoController!;
videoIntroController = widget.videoIntroController; videoIntroController = widget.videoIntroController;
bangumiIntroController = widget.bangumiIntroController; bangumiIntroController = widget.bangumiIntroController;
widget.controller.headerControl = widget.headerControl; plPlayerController.headerControl = widget.headerControl;
widget.controller.bottomControl = widget.bottomControl; plPlayerController.bottomControl = widget.bottomControl;
widget.controller.danmuWidget = widget.danmuWidget; plPlayerController.danmuWidget = widget.danmuWidget;
defaultBtmProgressBehavior = setting.get(SettingBoxKey.btmProgressBehavior, defaultBtmProgressBehavior = setting.get(SettingBoxKey.btmProgressBehavior,
defaultValue: BtmProgressBehavior.values.first.code); defaultValue: BtmProgressBehavior.values.first.code);
enableQuickDouble = enableQuickDouble =
@@ -218,7 +217,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
_brightnessIndicator.value = false; _brightnessIndicator.value = false;
} }
}); });
widget.controller.brightness.value = value; plPlayerController.brightness.value = value;
} }
@override @override
@@ -230,7 +229,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
// 动态构建底部控制条 // 动态构建底部控制条
List<Widget> buildBottomControl() { List<Widget> buildBottomControl() {
final PlPlayerController plPlayerController = widget.controller;
bool isSeason = videoIntroController?.videoDetail.value.ugcSeason != null; bool isSeason = videoIntroController?.videoDetail.value.ugcSeason != null;
bool isPage = videoIntroController?.videoDetail.value.pages != null && bool isPage = videoIntroController?.videoDetail.value.pages != null &&
videoIntroController!.videoDetail.value.pages!.length > 1; videoIntroController!.videoDetail.value.pages!.length > 1;
@@ -239,7 +237,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
Map<BottomControlType, Widget> videoProgressWidgets = { Map<BottomControlType, Widget> videoProgressWidgets = {
/// 上一集 /// 上一集
BottomControlType.pre: Container( BottomControlType.pre: Container(
width: 35, width: isFullScreen ? 42 : 35,
height: 30, height: 30,
alignment: Alignment.center, alignment: Alignment.center,
child: ComBtn( child: ComBtn(
@@ -271,7 +269,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
/// 下一集 /// 下一集
BottomControlType.next: Container( BottomControlType.next: Container(
width: 35, width: isFullScreen ? 42 : 35,
height: 30, height: 30,
alignment: Alignment.center, alignment: Alignment.center,
child: ComBtn( child: ComBtn(
@@ -338,7 +336,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
() => plPlayerController.viewPointList.isEmpty () => plPlayerController.viewPointList.isEmpty
? const SizedBox.shrink() ? const SizedBox.shrink()
: Container( : Container(
width: 35, width: isFullScreen ? 42 : 35,
height: 30, height: 30,
alignment: Alignment.center, alignment: Alignment.center,
child: ComBtn( child: ComBtn(
@@ -358,7 +356,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
/// 选集 /// 选集
BottomControlType.episode: Container( BottomControlType.episode: Container(
width: 35, width: isFullScreen ? 42 : 35,
height: 30, height: 30,
alignment: Alignment.center, alignment: Alignment.center,
child: ComBtn( child: ComBtn(
@@ -370,8 +368,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
), ),
fuc: () { fuc: () {
int? index; int? index;
int currentCid = widget.controller.cid; int currentCid = plPlayerController.cid;
String bvid = widget.controller.bvid; String bvid = plPlayerController.bvid;
List episodes = []; List episodes = [];
// late Function changeFucCall; // late Function changeFucCall;
if (isPage) { if (isPage) {
@@ -385,7 +383,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
for (int i = 0; i < sections.length; i++) { for (int i = 0; i < sections.length; i++) {
final List<EpisodeItem> episodesList = sections[i].episodes!; final List<EpisodeItem> episodesList = sections[i].episodes!;
for (int j = 0; j < episodesList.length; j++) { for (int j = 0; j < episodesList.length; j++) {
if (episodesList[j].cid == widget.controller.cid) { if (episodesList[j].cid == plPlayerController.cid) {
index = i; index = i;
episodes = episodesList; episodes = episodesList;
break; break;
@@ -413,7 +411,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
/// 画面比例 /// 画面比例
BottomControlType.fit: SizedBox( BottomControlType.fit: SizedBox(
width: 35, width: isFullScreen ? 42 : 35,
height: 30, height: 30,
child: TextButton( child: TextButton(
onPressed: () => plPlayerController.toggleVideoFit(), onPressed: () => plPlayerController.toggleVideoFit(),
@@ -434,7 +432,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
() => plPlayerController.vttSubtitles.isEmpty () => plPlayerController.vttSubtitles.isEmpty
? const SizedBox.shrink() ? const SizedBox.shrink()
: SizedBox( : SizedBox(
width: 35, width: isFullScreen ? 42 : 35,
height: 30, height: 30,
child: PopupMenuButton<int>( child: PopupMenuButton<int>(
onSelected: (int value) { onSelected: (int value) {
@@ -475,9 +473,10 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
), ),
/// 播放速度 /// 播放速度
BottomControlType.speed: SizedBox( BottomControlType.speed: Container(
width: 35,
height: 30, height: 30,
margin: const EdgeInsets.symmetric(horizontal: 10),
alignment: Alignment.center,
child: PopupMenuButton<double>( child: PopupMenuButton<double>(
onSelected: (double value) { onSelected: (double value) {
plPlayerController.setPlaybackSpeed(value); plPlayerController.setPlaybackSpeed(value);
@@ -498,33 +497,25 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
); );
}).toList(); }).toList();
}, },
child: Container( child: Text("${plPlayerController.playbackSpeed}X",
width: 35, style: const TextStyle(color: Colors.white, fontSize: 13),
height: 30, semanticsLabel: "${plPlayerController.playbackSpeed}倍速"),
alignment: Alignment.center,
child: Obx(() => Text("${plPlayerController.playbackSpeed}X",
style: const TextStyle(color: Colors.white, fontSize: 13),
semanticsLabel: "${plPlayerController.playbackSpeed}倍速")),
),
), ),
), ),
/// 全屏 /// 全屏
BottomControlType.fullscreen: SizedBox( BottomControlType.fullscreen: SizedBox(
width: 35, width: isFullScreen ? 42 : 35,
height: 30, height: 30,
child: Obx(() => ComBtn( child: Obx(() => ComBtn(
icon: Icon( icon: Icon(
plPlayerController.isFullScreen.value isFullScreen ? Icons.fullscreen_exit : Icons.fullscreen,
? Icons.fullscreen_exit semanticLabel: isFullScreen ? '退出全屏' : '全屏',
: Icons.fullscreen,
semanticLabel:
plPlayerController.isFullScreen.value ? '退出全屏' : '全屏',
size: 24, size: 24,
color: Colors.white, color: Colors.white,
), ),
fuc: () => plPlayerController.triggerFullScreen( fuc: () =>
status: !plPlayerController.isFullScreen.value), plPlayerController.triggerFullScreen(status: !isFullScreen),
)), )),
), ),
}; };
@@ -538,7 +529,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
BottomControlType.space, BottomControlType.space,
BottomControlType.viewPoints, BottomControlType.viewPoints,
if (anySeason) BottomControlType.episode, if (anySeason) BottomControlType.episode,
if (plPlayerController.isFullScreen.value) BottomControlType.fit, if (isFullScreen) BottomControlType.fit,
BottomControlType.subtitle, BottomControlType.subtitle,
BottomControlType.speed, BottomControlType.speed,
BottomControlType.fullscreen, BottomControlType.fullscreen,
@@ -558,12 +549,14 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
return list; return list;
} }
PlPlayerController get plPlayerController => widget.controller; PlPlayerController get plPlayerController => widget.plPlayerController;
bool get isFullScreen => plPlayerController.isFullScreen.value;
TextStyle get subTitleStyle => TextStyle( TextStyle get subTitleStyle => TextStyle(
height: 1.5, height: 1.5,
fontSize: 16 * fontSize: 16 *
(plPlayerController.isFullScreen.value (isFullScreen
? plPlayerController.subtitleFontScaleFS.value ? plPlayerController.subtitleFontScaleFS.value
: plPlayerController.subtitleFontScale.value), : plPlayerController.subtitleFontScale.value),
letterSpacing: 0.1, letterSpacing: 0.1,
@@ -585,7 +578,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (plPlayerController.isFullScreen.value) { if (isFullScreen) {
plPlayerController.subtitleFontScaleFS.listen((value) { plPlayerController.subtitleFontScaleFS.listen((value) {
_updateSubtitle(value); _updateSubtitle(value);
}); });
@@ -701,15 +694,13 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
if (cumulativeDy > threshold) { if (cumulativeDy > threshold) {
_gestureType = 'center_down'; _gestureType = 'center_down';
if (plPlayerController.isFullScreen.value ^ if (isFullScreen ^ fullScreenGestureReverse) {
fullScreenGestureReverse) {
fullScreenTrigger(fullScreenGestureReverse); fullScreenTrigger(fullScreenGestureReverse);
} }
// debugPrint('center_down:$cumulativeDy'); // debugPrint('center_down:$cumulativeDy');
} else if (cumulativeDy < -threshold) { } else if (cumulativeDy < -threshold) {
_gestureType = 'center_up'; _gestureType = 'center_up';
if (!plPlayerController.isFullScreen.value ^ if (!isFullScreen ^ fullScreenGestureReverse) {
fullScreenGestureReverse) {
fullScreenTrigger(!fullScreenGestureReverse); fullScreenTrigger(!fullScreenGestureReverse);
} }
// debugPrint('center_up:$cumulativeDy'); // debugPrint('center_up:$cumulativeDy');
@@ -765,20 +756,23 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
opacity: plPlayerController.doubleSpeedStatus.value ? 1.0 : 0.0, opacity: plPlayerController.doubleSpeedStatus.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,
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0x88000000), color: const Color(0x88000000),
borderRadius: BorderRadius.circular(16.0), borderRadius: BorderRadius.circular(16.0),
),
height: 32.0,
width: 70.0,
child: Center(
child: Obx(
() => Text(
'${plPlayerController.enableAutoLongPressSpeed ? plPlayerController.playbackSpeed * 2 : plPlayerController.longPressSpeed}倍速中',
style:
const TextStyle(color: Colors.white, fontSize: 13),
),
), ),
height: 32.0, ),
width: 70.0, ),
child: Center(
child: Obx(() => Text(
'${plPlayerController.enableAutoLongPressSpeed ? plPlayerController.playbackSpeed * 2 : plPlayerController.longPressSpeed}倍速中',
style: const TextStyle(
color: Colors.white, fontSize: 13),
)),
)),
), ),
), ),
), ),
@@ -1022,7 +1016,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
position: 'bottom', position: 'bottom',
child: widget.bottomControl ?? child: widget.bottomControl ??
BottomControl( BottomControl(
controller: widget.controller, controller: plPlayerController,
buildBottomControl: buildBottomControl(), buildBottomControl: buildBottomControl(),
), ),
), ),
@@ -1047,11 +1041,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
} }
if (defaultBtmProgressBehavior == if (defaultBtmProgressBehavior ==
BtmProgressBehavior.onlyShowFullScreen.code && BtmProgressBehavior.onlyShowFullScreen.code &&
!plPlayerController.isFullScreen.value) { !isFullScreen) {
return const SizedBox(); return const SizedBox();
} else if (defaultBtmProgressBehavior == } else if (defaultBtmProgressBehavior ==
BtmProgressBehavior.onlyHideFullScreen.code && BtmProgressBehavior.onlyHideFullScreen.code &&
plPlayerController.isFullScreen.value) { isFullScreen) {
return const SizedBox(); return const SizedBox();
} }
@@ -1149,28 +1143,33 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
// 锁 // 锁
Obx( Obx(
() => Visibility( () => Visibility(
visible: plPlayerController.videoType.value != 'live' && visible:
plPlayerController.isFullScreen.value, plPlayerController.videoType.value != 'live' && isFullScreen,
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: FractionalTranslation( child: FractionalTranslation(
translation: const Offset(1, -0.4), translation: const Offset(1, -0.4),
child: Visibility( child: Visibility(
visible: plPlayerController.showControls.value && visible: plPlayerController.showControls.value &&
(plPlayerController.isFullScreen.value || (isFullScreen || plPlayerController.controlsLock.value),
plPlayerController.controlsLock.value), child: DecoratedBox(
child: ComBtn( decoration: BoxDecoration(
icon: Icon( color: const Color(0x45000000),
plPlayerController.controlsLock.value borderRadius: BorderRadius.circular(8),
? FontAwesomeIcons.lock ),
: FontAwesomeIcons.lockOpen, child: ComBtn(
semanticLabel: icon: Icon(
plPlayerController.controlsLock.value ? '解锁' : '锁定', plPlayerController.controlsLock.value
size: 15, ? FontAwesomeIcons.lock
color: Colors.white, : FontAwesomeIcons.lockOpen,
semanticLabel:
plPlayerController.controlsLock.value ? '解锁' : '锁定',
size: 15,
color: Colors.white,
),
fuc: () => plPlayerController.onLockControl(
!plPlayerController.controlsLock.value),
), ),
fuc: () => plPlayerController
.onLockControl(!plPlayerController.controlsLock.value),
), ),
), ),
), ),
@@ -1185,68 +1184,75 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
child: FractionalTranslation( child: FractionalTranslation(
translation: const Offset(-1, -0.4), translation: const Offset(-1, -0.4),
child: Visibility( child: Visibility(
visible: plPlayerController.showControls.value && visible: plPlayerController.showControls.value && isFullScreen,
plPlayerController.isFullScreen.value, child: DecoratedBox(
child: ComBtn( decoration: BoxDecoration(
icon: const Icon( color: const Color(0x45000000),
Icons.photo_camera, borderRadius: BorderRadius.circular(8),
semanticLabel: '截图',
size: 20,
color: Colors.white,
), ),
fuc: () { child: ComBtn(
SmartDialog.showToast('截图中'); icon: const Icon(
plPlayerController.videoPlayerController Icons.photo_camera,
?.screenshot(format: 'image/png') semanticLabel: '截图',
.then((value) { size: 20,
if (value != null && context.mounted) { color: Colors.white,
SmartDialog.showToast('点击弹窗保存截图'); ),
showDialog( fuc: () {
context: context, SmartDialog.showToast('截图中');
builder: (BuildContext context) { plPlayerController.videoPlayerController
return AlertDialog( ?.screenshot(format: 'image/png')
// title: const Text('点击保存'), .then((value) {
titlePadding: EdgeInsets.zero, if (value != null && context.mounted) {
contentPadding: const EdgeInsets.all(8), SmartDialog.showToast('点击弹窗保存截图');
insetPadding: showDialog(
EdgeInsets.only(left: context.width / 2), context: context,
//移除圆角 builder: (BuildContext context) {
shape: const RoundedRectangleBorder(), return AlertDialog(
content: GestureDetector( // title: const Text('点击保存'),
onTap: () async { titlePadding: EdgeInsets.zero,
String name = DateTime.now().toString(); contentPadding: const EdgeInsets.all(8),
final SaveResult result = insetPadding:
await SaverGallery.saveImage( EdgeInsets.only(left: context.width / 2),
value, //移除圆角
fileName: name, shape: const RoundedRectangleBorder(),
androidRelativePath: "Pictures/Screenshots", content: GestureDetector(
skipIfExists: false, onTap: () async {
); String name = DateTime.now().toString();
final SaveResult result =
await SaverGallery.saveImage(
value,
fileName: name,
androidRelativePath:
"Pictures/Screenshots",
skipIfExists: false,
);
if (result.isSuccess) { if (result.isSuccess) {
Get.back(); Get.back();
SmartDialog.showToast('$name.png已保存到相册/截图'); SmartDialog.showToast(
} else { '$name.png已保存到相册/截图');
await SmartDialog.showToast( } else {
'保存失败,${result.errorMessage}'); await SmartDialog.showToast(
} '保存失败,${result.errorMessage}');
}, }
child: ConstrainedBox( },
constraints: BoxConstraints( child: ConstrainedBox(
maxWidth: context.width / 3, constraints: BoxConstraints(
maxHeight: context.height / 3, maxWidth: context.width / 3,
maxHeight: context.height / 3,
),
child: Image.memory(value),
), ),
child: Image.memory(value),
), ),
), );
); },
}, );
); } else {
} else { SmartDialog.showToast('截图失败');
SmartDialog.showToast('截图失败'); }
} });
}); },
}, ),
), ),
), ),
), ),
@@ -1331,16 +1337,16 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
onSubmitted: (Duration value) { onSubmitted: (Duration value) {
_hideSeekBackwardButton.value = true; _hideSeekBackwardButton.value = true;
_mountSeekBackwardButton.value = false; _mountSeekBackwardButton.value = false;
final Player player = final Player player = widget
widget.controller.videoPlayerController!; .plPlayerController.videoPlayerController!;
Duration result = player.state.position - value; Duration result = player.state.position - value;
result = result.clamp( result = result.clamp(
Duration.zero, Duration.zero,
player.state.duration, player.state.duration,
); );
widget.controller plPlayerController.seekTo(result,
.seekTo(result, type: 'slider'); type: 'slider');
widget.controller.play(); plPlayerController.play();
}, },
), ),
) )
@@ -1377,16 +1383,16 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
onSubmitted: (Duration value) { onSubmitted: (Duration value) {
_hideSeekForwardButton.value = true; _hideSeekForwardButton.value = true;
_mountSeekForwardButton.value = false; _mountSeekForwardButton.value = false;
final Player player = final Player player = widget
widget.controller.videoPlayerController!; .plPlayerController.videoPlayerController!;
Duration result = player.state.position + value; Duration result = player.state.position + value;
result = result.clamp( result = result.clamp(
Duration.zero, Duration.zero,
player.state.duration, player.state.duration,
); );
widget.controller plPlayerController.seekTo(result,
.seekTo(result, type: 'slider'); type: 'slider');
widget.controller.play(); plPlayerController.play();
}, },
), ),
) )

View File

@@ -15,7 +15,7 @@ class ComBtn extends StatelessWidget {
return SizedBox( return SizedBox(
width: 34, width: 34,
height: 34, height: 34,
child: InkWell( child: GestureDetector(
onTap: fuc, onTap: fuc,
child: icon!, child: icon!,
), ),