Revert "chore: clean up"

This reverts commit 538494b7ec.
This commit is contained in:
bggRGjQaUbCoE
2025-04-20 18:04:06 +08:00
parent a5c7ec0d60
commit 6f4321ae14
90 changed files with 1814 additions and 493 deletions

View File

@@ -102,6 +102,7 @@ class VideoDetailController extends GetxController
Box get setting => GStorage.setting;
// late bool enableCDN;
int? cacheVideoQa;
late String cacheDecode;
late String cacheSecondDecode;
@@ -284,10 +285,14 @@ class VideoDetailController extends GetxController
}
danmakuCid.value = cid.value;
///
if (Platform.isAndroid) {
floating = Floating();
}
// CDN优化
// enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true);
// 预设的解码格式
cacheDecode = setting.get(SettingBoxKey.defaultDecode,
defaultValue: VideoDecodeFormats.values.last.code);
@@ -998,6 +1003,7 @@ class VideoDetailController extends GetxController
}
/// 更新画质、音质
/// TODO 继续进度播放
updatePlayer() {
isShowCover.value = false;
playedTime = plPlayerController.position.value;
@@ -1274,6 +1280,9 @@ class VideoDetailController extends GetxController
orElse: () => videosList.first);
setVideoHeight();
// videoUrl = enableCDN
// ? VideoUtils.getCdnUrl(firstVideo)
// : (firstVideo.backupUrl ?? firstVideo.baseUrl!);
videoUrl = VideoUtils.getCdnUrl(firstVideo);
/// 优先顺序 设置中指定质量 -> 当前可选的最高质量
@@ -1299,6 +1308,9 @@ class VideoDetailController extends GetxController
}
firstAudio = audiosList.firstWhere((e) => e.id == closestNumber,
orElse: () => audiosList.first);
// audioUrl = enableCDN
// ? VideoUtils.getCdnUrl(firstAudio)
// : (firstAudio.backupUrl ?? firstAudio.baseUrl!);
audioUrl = VideoUtils.getCdnUrl(firstAudio);
if (firstAudio.id != null) {
currentAudioQa = AudioQualityCode.fromCode(firstAudio.id!)!;
@@ -1458,6 +1470,9 @@ class VideoDetailController extends GetxController
Future _querySubtitles() async {
var res = await VideoHttp.subtitlesJson(bvid: bvid, cid: cid.value);
// if (!res["status"]) {
// SmartDialog.showToast('查询字幕错误,${res["msg"]}');
// }
if (res['status']) {
// interactive video
if (graphVersion == null) {

View File

@@ -176,6 +176,11 @@ class VideoIntroController extends GetxController {
lastPlayCid.value == 0) {
lastPlayCid.value = videoDetail.value.pages!.first.cid!;
}
// Get.find<VideoDetailController>(tag: heroTag).tabs.value = [
// '简介',
// '评论 ${result['data']!.stat!.reply}'
// ];
// 获取到粉丝数再返回
queryUserStat();
} else {
SmartDialog.showToast(
@@ -269,10 +274,12 @@ class VideoIntroController extends GetxController {
return;
}
if (videoDetail.value.stat?.like == null) {
// not init
return;
}
var result = await VideoHttp.likeVideo(bvid: bvid, type: !hasLike.value);
if (result['status']) {
// hasLike.value = result["data"] == 1 ? true : false;
if (!hasLike.value) {
SmartDialog.showToast(result['data']['toast']);
hasLike.value = true;
@@ -296,6 +303,7 @@ class VideoIntroController extends GetxController {
var result =
await VideoHttp.dislikeVideo(bvid: bvid, type: !hasDislike.value);
if (result['status']) {
// hasLike.value = result["data"] == 1 ? true : false;
if (!hasDislike.value) {
SmartDialog.showToast('点踩成功');
hasDislike.value = true;
@@ -304,6 +312,7 @@ class VideoIntroController extends GetxController {
SmartDialog.showToast('取消踩');
hasDislike.value = false;
}
// hasDislike.refresh();
} else {
SmartDialog.showToast(result['msg']);
}
@@ -426,6 +435,8 @@ class VideoIntroController extends GetxController {
Get.back();
hasFav.value =
addMediaIdsNew.isNotEmpty || favIds?.length != delMediaIdsNew.length;
// 重新获取收藏状态
// await queryHasFavVideo();
SmartDialog.showToast('操作成功');
} else {
SmartDialog.showToast(result['msg']);
@@ -570,6 +581,14 @@ class VideoIntroController extends GetxController {
},
);
}
// MemberController _ = Get.put<MemberController>(MemberController(mid: mid),
// tag: mid.toString());
// await _.getInfo();
// if (context.mounted) await _.actionRelationMod(context);
// followStatus['attribute'] = _.attribute.value;
// followStatus.refresh();
// Get.delete<MemberController>(tag: mid.toString());
}
// 修改分P或番剧分集
@@ -659,6 +678,10 @@ class VideoIntroController extends GetxController {
bvid: bvid,
cid: lastPlayCid.value,
);
// dynamic result = await GrpcRepo.playerOnline(
// aid: IdUtils.bv2av(bvid),
// cid: lastPlayCid.value,
// );
if (result['status']) {
total.value = result['data'];
}

View File

@@ -54,6 +54,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
with AutomaticKeepAliveClientMixin {
late VideoIntroController videoIntroController;
// 添加页面缓存
@override
bool get wantKeepAlive => true;
@@ -240,9 +241,11 @@ class _VideoInfoState extends State<VideoInfo> {
return;
}
feedBack();
// widget.showIntroDetail();
videoIntroController.expandableCtr?.toggle();
}
// 用户主页
onPushMember() {
feedBack();
int? mid = !widget.loadingStatus
@@ -253,8 +256,16 @@ class _VideoInfoState extends State<VideoInfo> {
_horizontalMemberPage) {
widget.onShowMemberPage(mid);
} else {
// memberHeroTag = Utils.makeHeroTag(mid);
// String face = !loadingStatus
// ? videoDetail.owner!.face
// : videoItem['owner'].face;
Get.toNamed(
'/member?mid=$mid&from_view_aid=${videoDetailCtr.oid.value}',
// arguments: {
// 'face': face,
// 'heroTag': memberHeroTag,
// },
);
}
}
@@ -262,7 +273,7 @@ class _VideoInfoState extends State<VideoInfo> {
@override
Widget build(BuildContext context) {
final ThemeData themeData = Theme.of(context);
final ThemeData t = Theme.of(context);
return SliverLayoutBuilder(
builder: (BuildContext context, SliverConstraints constraints) {
bool isHorizontal = context.orientation == Orientation.landscape &&
@@ -286,242 +297,266 @@ class _VideoInfoState extends State<VideoInfo> {
onTap: () {},
child: Row(
children: [
if (videoItem['staff'] == null) ...[
Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: GestureDetector(
onTap: onPushMember,
behavior: HitTestBehavior.opaque,
child: Row(
Expanded(
child: videoItem['staff'] == null
? Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Obx(
() => Avatar(
avatar: videoIntroController.userStat
.value['card']?['face'] ??
'',
size: 35,
isVip: (videoIntroController
.userStat.value['card']
?['vip']?['status'] ??
-1) >
0,
officialType: videoIntroController
.userStat.value['card']
?['official_verify']?['type'],
garbPendantImage: videoIntroController
.userStat.value['card']
?['pendant']?['image'],
GestureDetector(
onTap: onPushMember,
behavior: HitTestBehavior.opaque,
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Obx(
() => Avatar(
avatar: videoIntroController
.userStat
.value['card']?['face'] ??
'',
size: 35,
isVip: (videoIntroController
.userStat
.value['card']
?['vip']?['status'] ??
-1) >
0,
officialType: videoIntroController
.userStat.value['card']
?['official_verify']?['type'],
garbPendantImage:
videoIntroController.userStat
.value['card']
?['pendant']?['image'],
),
),
const SizedBox(width: 10),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Obx(
() => Text(
videoIntroController.userStat
.value['card']
?['name'] ??
"",
maxLines: 1,
overflow:
TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13,
color: (videoIntroController
.userStat
.value['card']?['vip']
?[
'status'] ??
-1) >
0 &&
videoIntroController
.userStat
.value['card']
?[
'vip']?['type'] ==
2
? context.vipColor
: null,
),
),
),
const SizedBox(height: 0),
Obx(
() => Text(
'${Utils.numFormat(videoIntroController.userStat.value['follower'])}粉丝 ${videoIntroController.userStat.value['archive_count'] != null ? '${Utils.numFormat(videoIntroController.userStat.value['archive_count'])}视频' : ''}',
style: TextStyle(
fontSize: 12,
color:
t.colorScheme.outline,
),
),
),
],
),
],
),
),
const SizedBox(width: 10),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Obx(
() => Text(
videoIntroController.userStat
.value['card']?['name'] ??
"",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13,
color: (videoIntroController.userStat
.value[
'card']?['vip']
?['status'] ??
-1) >
0 &&
videoIntroController
.userStat
.value['card']
?[
'vip']?['type'] ==
2
? context.vipColor
: null,
),
),
),
const SizedBox(height: 0),
Obx(
() => Text(
'${Utils.numFormat(videoIntroController.userStat.value['follower'])}粉丝 ${videoIntroController.userStat.value['archive_count'] != null ? '${Utils.numFormat(videoIntroController.userStat.value['archive_count'])}视频' : ''}',
style: TextStyle(
fontSize: 12,
color:
themeData.colorScheme.outline,
),
),
),
],
),
const Spacer(),
followButton(context, t),
],
),
),
),
),
followButton(context, themeData),
] else
Expanded(
child: SelfSizedHorizontalList(
gapSize: 25,
itemCount: videoItem['staff'].length,
childBuilder: (index) => GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
int? ownerMid = !widget.loadingStatus
? videoDetail.owner?.mid
: videoItem['owner']?.mid;
if (videoItem['staff'][index].mid ==
ownerMid &&
context.orientation ==
Orientation.landscape &&
_horizontalMemberPage) {
widget.onShowMemberPage(ownerMid);
} else {
Get.toNamed(
'/member?mid=${videoItem['staff'][index].mid}&from_view_aid=${videoDetailCtr.oid.value}',
);
}
},
child: Row(
children: [
Stack(
clipBehavior: Clip.none,
)
: SelfSizedHorizontalList(
gapSize: 25,
itemCount: videoItem['staff'].length,
childBuilder: (index) => GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
int? ownerMid = !widget.loadingStatus
? videoDetail.owner?.mid
: videoItem['owner']?.mid;
if (videoItem['staff'][index].mid ==
ownerMid &&
context.orientation ==
Orientation.landscape &&
_horizontalMemberPage) {
widget.onShowMemberPage(ownerMid);
} else {
Get.toNamed(
'/member?mid=${videoItem['staff'][index].mid}&from_view_aid=${videoDetailCtr.oid.value}',
// arguments: {
// 'face':
// videoItem['staff'][index].face,
// 'heroTag': Utils.makeHeroTag(
// videoItem['staff'][index].mid),
// },
);
}
},
child: Row(
children: [
NetworkImgLayer(
type: 'avatar',
src: videoItem['staff'][index].face,
width: 35,
height: 35,
fadeInDuration: Duration.zero,
fadeOutDuration: Duration.zero,
Stack(
clipBehavior: Clip.none,
children: [
NetworkImgLayer(
type: 'avatar',
src: videoItem['staff'][index]
.face,
width: 35,
height: 35,
fadeInDuration: Duration.zero,
fadeOutDuration: Duration.zero,
),
if ((videoItem['staff'][index]
.official?['type'] ??
-1) !=
-1)
Positioned(
right: -2,
bottom: -2,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context)
.colorScheme
.surface,
),
child: Icon(
Icons.offline_bolt,
color: videoItem['staff']
[index]
.official?[
'type'] ==
0
? Colors.yellow
: Colors
.lightBlueAccent,
size: 14,
),
),
),
Positioned(
top: 0,
right: -6,
child: Obx(() => videoIntroController
.staffRelations[
'status'] ==
true &&
videoIntroController
.staffRelations[
'${videoItem['staff'][index].mid}'] ==
null
? Material(
color: Colors.transparent,
child: InkWell(
customBorder:
const CircleBorder(),
onTap: () {
RequestUtils
.actionRelationMod(
context: context,
mid: videoItem[
'staff']
[index]
.mid,
isFollow: false,
callback: (val) {
videoIntroController
.staffRelations[
'${videoItem['staff'][index].mid}'] = true;
},
);
},
child: Ink(
padding:
const EdgeInsets
.all(2),
decoration:
BoxDecoration(
color: Theme.of(
context)
.colorScheme
.secondaryContainer,
shape:
BoxShape.circle,
),
child: Icon(
MdiIcons.plus,
size: 16,
color: Theme.of(
context)
.colorScheme
.onSecondaryContainer,
),
),
),
)
: const SizedBox.shrink()),
),
],
),
if ((videoItem['staff'][index]
.official?['type'] ??
-1) !=
-1)
Positioned(
right: -2,
bottom: -2,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
const SizedBox(width: 8),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
videoItem['staff'][index].name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13,
color: videoItem['staff'][index]
.vip
.status >
0 &&
videoItem['staff']
[index]
.vip
.type ==
2
? context.vipColor
: null,
),
),
Text(
videoItem['staff'][index].title,
style: TextStyle(
fontSize: 12,
color: Theme.of(context)
.colorScheme
.surface,
),
child: Icon(
Icons.offline_bolt,
color: videoItem['staff'][index]
.official?[
'type'] ==
0
? Colors.yellow
: Colors.lightBlueAccent,
size: 14,
.outline,
),
),
),
Positioned(
top: 0,
right: -6,
child: Obx(() => videoIntroController
.staffRelations[
'status'] ==
true &&
videoIntroController
.staffRelations[
'${videoItem['staff'][index].mid}'] ==
null
? Material(
color: Colors.transparent,
child: InkWell(
customBorder:
const CircleBorder(),
onTap: () {
RequestUtils
.actionRelationMod(
context: context,
mid: videoItem['staff']
[index]
.mid,
isFollow: false,
callback: (val) {
videoIntroController
.staffRelations[
'${videoItem['staff'][index].mid}'] = true;
},
);
},
child: Ink(
padding:
const EdgeInsets.all(
2),
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.secondaryContainer,
shape: BoxShape.circle,
),
child: Icon(
MdiIcons.plus,
size: 16,
color: Theme.of(context)
.colorScheme
.onSecondaryContainer,
),
),
),
)
: const SizedBox.shrink()),
],
),
],
),
const SizedBox(width: 8),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
videoItem['staff'][index].name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13,
color: videoItem['staff'][index]
.vip
.status >
0 &&
videoItem['staff'][index]
.vip
.type ==
2
? context.vipColor
: null,
),
),
Text(
videoItem['staff'][index].title,
style: TextStyle(
fontSize: 12,
color: Theme.of(context)
.colorScheme
.outline,
),
),
],
),
],
),
),
),
),
),
),
if (isHorizontal) ...[
const SizedBox(width: 10),
Expanded(
@@ -568,7 +603,7 @@ class _VideoInfoState extends State<VideoInfo> {
value: Utils.numFormat(!widget.loadingStatus
? videoDetail.stat?.view ?? '-'
: videoItem['stat']?.view ?? '-'),
textColor: themeData.colorScheme.outline,
textColor: t.colorScheme.outline,
),
const SizedBox(width: 10),
StatDanMu(
@@ -577,7 +612,7 @@ class _VideoInfoState extends State<VideoInfo> {
value: Utils.numFormat(!widget.loadingStatus
? videoDetail.stat?.danmaku ?? '-'
: videoItem['stat']?.danmu ?? '-'),
textColor: themeData.colorScheme.outline,
textColor: t.colorScheme.outline,
),
const SizedBox(width: 10),
Text(
@@ -588,7 +623,7 @@ class _VideoInfoState extends State<VideoInfo> {
formatType: 'detail'),
style: TextStyle(
fontSize: 12,
color: themeData.colorScheme.outline,
color: t.colorScheme.outline,
),
),
if (MineController.anonymity.value) ...<Widget>[
@@ -596,7 +631,7 @@ class _VideoInfoState extends State<VideoInfo> {
Icon(
MdiIcons.incognito,
size: 15,
color: themeData.colorScheme.outline,
color: t.colorScheme.outline,
semanticLabel: '无痕',
),
],
@@ -607,7 +642,7 @@ class _VideoInfoState extends State<VideoInfo> {
'${videoIntroController.total.value}人在看',
style: TextStyle(
fontSize: 12,
color: themeData.colorScheme.outline,
color: t.colorScheme.outline,
),
),
),
@@ -701,6 +736,7 @@ class _VideoInfoState extends State<VideoInfo> {
SelectableText.rich(
style: const TextStyle(
height: 1.4,
// fontSize: 13,
),
TextSpan(
children: [
@@ -762,6 +798,16 @@ class _VideoInfoState extends State<VideoInfo> {
),
),
),
// 点赞收藏转发 布局样式1
// SingleChildScrollView(
// padding: const EdgeInsets.only(top: 7, bottom: 7),
// scrollDirection: Axis.horizontal,
// child: actionRow(
// context,
// videoIntroController,
// videoDetailCtr,
// ),
// ),
// 点赞收藏转发 布局样式2
if (!isHorizontal) ...[
const SizedBox(height: 8),
@@ -826,7 +872,7 @@ class _VideoInfoState extends State<VideoInfo> {
switch (attr) {
1 => '悄悄关注',
2 => '已关注',
4 || 6 => '已互关',
6 => '已互关',
128 => '已拉黑',
-10 => '特别关注',
_ => '关注'
@@ -840,108 +886,112 @@ class _VideoInfoState extends State<VideoInfo> {
Widget actionGrid(
BuildContext context, VideoIntroController videoIntroController) {
return Container(
margin: const EdgeInsets.only(top: 1),
height: 48,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Obx(
() => ActionItem(
icon: const Icon(FontAwesomeIcons.thumbsUp),
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
onTap: () => handleState(videoIntroController.actionLikeVideo),
onLongPress: () =>
handleState(videoIntroController.actionOneThree),
selectStatus: videoIntroController.hasLike.value,
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Container(
margin: const EdgeInsets.only(top: 1),
height: 48,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Obx(
() => ActionItem(
icon: const Icon(FontAwesomeIcons.thumbsUp),
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
onTap: () => handleState(videoIntroController.actionLikeVideo),
onLongPress: () =>
handleState(videoIntroController.actionOneThree),
selectStatus: videoIntroController.hasLike.value,
loadingStatus: widget.loadingStatus,
semanticsLabel: '点赞',
text: !widget.loadingStatus
? Utils.numFormat(videoDetail.stat!.like!)
: '-',
needAnim: true,
hasTriple: videoIntroController.hasLike.value &&
videoIntroController.hasCoin &&
videoIntroController.hasFav.value,
callBack: (start) {
if (start) {
HapticFeedback.lightImpact();
_coinKey.currentState?.controller?.forward();
_favKey.currentState?.controller?.forward();
} else {
_coinKey.currentState?.controller?.reverse();
_favKey.currentState?.controller?.reverse();
}
},
),
),
Obx(
() => ActionItem(
icon: const Icon(FontAwesomeIcons.thumbsDown),
selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown),
onTap: () =>
handleState(videoIntroController.actionDislikeVideo),
selectStatus: videoIntroController.hasDislike.value,
loadingStatus: widget.loadingStatus,
semanticsLabel: '点踩',
text: "点踩",
),
),
Obx(
() => ActionItem(
key: _coinKey,
icon: const Icon(FontAwesomeIcons.b),
selectIcon: const Icon(FontAwesomeIcons.b),
onTap: () => handleState(videoIntroController.actionCoinVideo),
selectStatus: videoIntroController.hasCoin,
loadingStatus: widget.loadingStatus,
semanticsLabel: '投币',
text: !widget.loadingStatus
? Utils.numFormat(videoDetail.stat!.coin!)
: '-',
needAnim: true,
),
),
Obx(
() => ActionItem(
key: _favKey,
icon: const Icon(FontAwesomeIcons.star),
selectIcon: const Icon(FontAwesomeIcons.solidStar),
onTap: () => videoIntroController.showFavBottomSheet(context),
onLongPress: () => videoIntroController
.showFavBottomSheet(context, type: 'longPress'),
selectStatus: videoIntroController.hasFav.value,
loadingStatus: widget.loadingStatus,
semanticsLabel: '收藏',
text: !widget.loadingStatus
? Utils.numFormat(videoDetail.stat!.favorite!)
: '-',
needAnim: true,
),
),
Obx(
() => ActionItem(
icon: const Icon(FontAwesomeIcons.clock),
selectIcon: const Icon(FontAwesomeIcons.solidClock),
onTap: () => handleState(videoIntroController.viewLater),
selectStatus: videoIntroController.hasLater.value,
loadingStatus: widget.loadingStatus,
semanticsLabel: '再看',
text: '再看',
),
),
ActionItem(
icon: const Icon(FontAwesomeIcons.shareFromSquare),
onTap: () => videoIntroController.actionShareVideo(context),
selectStatus: false,
loadingStatus: widget.loadingStatus,
semanticsLabel: '点赞',
semanticsLabel: '分享',
text: !widget.loadingStatus
? Utils.numFormat(videoDetail.stat!.like!)
: '-',
needAnim: true,
hasTriple: videoIntroController.hasLike.value &&
videoIntroController.hasCoin &&
videoIntroController.hasFav.value,
callBack: (start) {
if (start) {
HapticFeedback.lightImpact();
_coinKey.currentState?.controller?.forward();
_favKey.currentState?.controller?.forward();
} else {
_coinKey.currentState?.controller?.reverse();
_favKey.currentState?.controller?.reverse();
}
},
? Utils.numFormat(videoDetail.stat!.share!)
: '分享',
),
),
Obx(
() => ActionItem(
icon: const Icon(FontAwesomeIcons.thumbsDown),
selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown),
onTap: () => handleState(videoIntroController.actionDislikeVideo),
selectStatus: videoIntroController.hasDislike.value,
loadingStatus: widget.loadingStatus,
semanticsLabel: '点踩',
text: "点踩",
),
),
Obx(
() => ActionItem(
key: _coinKey,
icon: const Icon(FontAwesomeIcons.b),
selectIcon: const Icon(FontAwesomeIcons.b),
onTap: () => handleState(videoIntroController.actionCoinVideo),
selectStatus: videoIntroController.hasCoin,
loadingStatus: widget.loadingStatus,
semanticsLabel: '投币',
text: !widget.loadingStatus
? Utils.numFormat(videoDetail.stat!.coin!)
: '-',
needAnim: true,
),
),
Obx(
() => ActionItem(
key: _favKey,
icon: const Icon(FontAwesomeIcons.star),
selectIcon: const Icon(FontAwesomeIcons.solidStar),
onTap: () => videoIntroController.showFavBottomSheet(context),
onLongPress: () => videoIntroController
.showFavBottomSheet(context, type: 'longPress'),
selectStatus: videoIntroController.hasFav.value,
loadingStatus: widget.loadingStatus,
semanticsLabel: '收藏',
text: !widget.loadingStatus
? Utils.numFormat(videoDetail.stat!.favorite!)
: '-',
needAnim: true,
),
),
Obx(
() => ActionItem(
icon: const Icon(FontAwesomeIcons.clock),
selectIcon: const Icon(FontAwesomeIcons.solidClock),
onTap: () => handleState(videoIntroController.viewLater),
selectStatus: videoIntroController.hasLater.value,
loadingStatus: widget.loadingStatus,
semanticsLabel: '再看',
text: '再看',
),
),
ActionItem(
icon: const Icon(FontAwesomeIcons.shareFromSquare),
onTap: () => videoIntroController.actionShareVideo(context),
selectStatus: false,
loadingStatus: widget.loadingStatus,
semanticsLabel: '分享',
text: !widget.loadingStatus
? Utils.numFormat(videoDetail.stat!.share!)
: '分享',
),
],
),
);
],
),
);
});
}
Widget actionRow(
@@ -949,66 +999,63 @@ class _VideoInfoState extends State<VideoInfo> {
VideoIntroController videoIntroController,
VideoDetailController videoDetailCtr,
) {
return Row(
children: [
Obx(
() => ActionRowItem(
icon: const Icon(FontAwesomeIcons.thumbsUp),
onTap: () => handleState(videoIntroController.actionLikeVideo),
selectStatus: videoIntroController.hasLike.value,
loadingStatus: widget.loadingStatus,
text: !widget.loadingStatus
? videoDetail.stat!.like!.toString()
: '-',
),
),
const SizedBox(width: 8),
Obx(
() => ActionRowItem(
icon: const Icon(FontAwesomeIcons.b),
onTap: () => handleState(videoIntroController.actionCoinVideo),
selectStatus: videoIntroController.hasCoin,
loadingStatus: widget.loadingStatus,
text: !widget.loadingStatus
? videoDetail.stat!.coin!.toString()
: '-',
),
),
const SizedBox(width: 8),
Obx(
() => ActionRowItem(
icon: const Icon(FontAwesomeIcons.heart),
onTap: () => videoIntroController.showFavBottomSheet(context),
onLongPress: () => videoIntroController.showFavBottomSheet(context,
type: 'longPress'),
selectStatus: videoIntroController.hasFav.value,
loadingStatus: widget.loadingStatus,
text: !widget.loadingStatus
? videoDetail.stat!.favorite!.toString()
: '-',
),
),
const SizedBox(width: 8),
ActionRowItem(
icon: const Icon(FontAwesomeIcons.comment),
onTap: () {
videoDetailCtr.tabCtr.animateTo(1);
},
selectStatus: false,
return Row(children: <Widget>[
Obx(
() => ActionRowItem(
icon: const Icon(FontAwesomeIcons.thumbsUp),
onTap: () => handleState(videoIntroController.actionLikeVideo),
selectStatus: videoIntroController.hasLike.value,
loadingStatus: widget.loadingStatus,
text:
!widget.loadingStatus ? videoDetail.stat!.reply!.toString() : '-',
!widget.loadingStatus ? videoDetail.stat!.like!.toString() : '-',
),
const SizedBox(width: 8),
ActionRowItem(
),
const SizedBox(width: 8),
Obx(
() => ActionRowItem(
icon: const Icon(FontAwesomeIcons.b),
onTap: () => handleState(videoIntroController.actionCoinVideo),
selectStatus: videoIntroController.hasCoin,
loadingStatus: widget.loadingStatus,
text:
!widget.loadingStatus ? videoDetail.stat!.coin!.toString() : '-',
),
),
const SizedBox(width: 8),
Obx(
() => ActionRowItem(
icon: const Icon(FontAwesomeIcons.heart),
onTap: () => videoIntroController.showFavBottomSheet(context),
onLongPress: () => videoIntroController.showFavBottomSheet(context,
type: 'longPress'),
selectStatus: videoIntroController.hasFav.value,
loadingStatus: widget.loadingStatus,
text: !widget.loadingStatus
? videoDetail.stat!.favorite!.toString()
: '-',
),
),
const SizedBox(width: 8),
ActionRowItem(
icon: const Icon(FontAwesomeIcons.comment),
onTap: () {
videoDetailCtr.tabCtr.animateTo(1);
},
selectStatus: false,
loadingStatus: widget.loadingStatus,
text: !widget.loadingStatus ? videoDetail.stat!.reply!.toString() : '-',
),
const SizedBox(width: 8),
ActionRowItem(
icon: const Icon(FontAwesomeIcons.share),
onTap: () => videoIntroController.actionShareVideo(context),
selectStatus: false,
loadingStatus: widget.loadingStatus,
text: '转发',
),
],
);
// text: !loadingStatus
// ? videoDetail.stat!.share!.toString()
// : '-',
text: '转发'),
]);
}
InlineSpan buildContent(BuildContext context, VideoDetailData content) {
@@ -1032,6 +1079,7 @@ class _VideoInfoState extends State<VideoInfo> {
String matchStr = match[0]!;
if (RegExp(r'^av\d+$', caseSensitive: false).hasMatch(matchStr)) {
try {
// validate
int aid = int.parse(matchStr.substring(2));
IdUtils.av2bv(aid);
spanChildren.add(
@@ -1051,6 +1099,7 @@ class _VideoInfoState extends State<VideoInfo> {
} else if (RegExp(r'^bv[a-z\d]{10}$', caseSensitive: false)
.hasMatch(matchStr)) {
try {
// validate
IdUtils.bv2av(matchStr);
spanChildren.add(
TextSpan(

View File

@@ -150,9 +150,11 @@ class ActionItemState extends State<ActionItem>
onTapDown: (details) => _isThumbsUp ? _startLongPress() : null,
onTapUp: (details) => _isThumbsUp ? _cancelLongPress() : null,
onTapCancel: () => _isThumbsUp ? _cancelLongPress(true) : null,
// borderRadius: StyleString.mdRadius,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// const SizedBox(height: 2),
Stack(
alignment: Alignment.center,
children: [
@@ -229,6 +231,7 @@ class _ArcPainter extends CustomPainter {
);
const startAngle = -pi / 2;
// const sweepAngle = -2 * pi;
canvas.drawArc(rect, startAngle, sweepAngle, false, paint);
}

View File

@@ -147,6 +147,7 @@ class _FavPanelState extends State<FavPanel> {
);
}
} else {
// 骨架屏
return const Center(
child: CircularProgressIndicator(),
);

View File

@@ -152,6 +152,7 @@ class _GroupPanelState extends State<GroupPanel> {
);
}
} else {
// 骨架屏
return const Center(
child: CircularProgressIndicator(),
);

View File

@@ -67,14 +67,15 @@ class _PagesPanelState extends State<PagesPanel> {
if (!_scrollController.hasClients || pages.isEmpty) {
return;
}
const double itemWidth = 150;
const double itemWidth = 150; // 每个列表项的宽度
final double targetOffset = (pageIndex * itemWidth - itemWidth / 2).clamp(
_scrollController.position.minScrollExtent,
_scrollController.position.maxScrollExtent);
// 滑动至目标位置
_scrollController.animateTo(
targetOffset,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 300), // 滑动动画持续时间
curve: Curves.easeInOut, // 滑动动画曲线
);
}

View File

@@ -61,6 +61,9 @@ class _SeasonPanelState extends State<SeasonPanel> {
}
/// 取对应 season_id 的 episodes
// episodes = widget.ugcSeason.sections!
// .firstWhere((e) => e.seasonId == widget.ugcSeason.id)
// .episodes;
currentIndex.value = episodes.indexWhere(
(EpisodeItem e) => e.cid == _videoDetailController.seasonCid);
_listener = _videoDetailController.cid.listen((int cid) {
@@ -83,6 +86,17 @@ class _SeasonPanelState extends State<SeasonPanel> {
super.dispose();
}
// void changeFucCall(item, int i) async {
// await widget.changeFuc!(
// IdUtils.av2bv(item.aid),
// item.cid,
// item.aid,
// );
// currentIndex = i;
// Get.back();
// setState(() {});
// }
@override
Widget build(BuildContext context) {
if (episodes.isEmpty) {

View File

@@ -50,6 +50,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
String get heroTag => widget.heroTag;
// 添加页面缓存
@override
bool get wantKeepAlive => true;

View File

@@ -73,6 +73,12 @@ class ReplyItemGrpc extends StatelessWidget {
},
onLongPress: () {
feedBack();
// showDialog(
// context: context,
// builder: (context) => AlertDialog(
// content: SelectableText(jsonEncode(replyItem.toProto3Json())),
// ),
// );
showModalBottomSheet(
context: context,
useSafeArea: true,
@@ -113,6 +119,17 @@ class ReplyItemGrpc extends StatelessWidget {
Positioned(
top: 8,
right: 12,
// child: GestureDetector(
// onTap: replyItem.member.garbCardJumpUrl.isNotEmpty
// ? () {
// Get.toNamed(
// 'webview',
// parameters: {
// 'url': replyItem.member.garbCardJumpUrl
// },
// );
// }
// : null,
child: Stack(
alignment: Alignment.centerRight,
children: [
@@ -139,6 +156,7 @@ class ReplyItemGrpc extends StatelessWidget {
],
),
),
// ),
SizedBox(
width: double.infinity,
child: _buildAuthorPanel(context),
@@ -175,11 +193,12 @@ class ReplyItemGrpc extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
children: <Widget>[
/// fix Stack内GestureDetector onTap无效
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
children: <Widget>[
lfAvtar(),
const SizedBox(width: 12),
Column(
@@ -312,8 +331,9 @@ class ReplyItemGrpc extends StatelessWidget {
if (replyLevel != '') buttonAction(context, replyItem.replyControl),
// 一楼的评论
if (replyLevel == '1' &&
(replyItem.replies.isNotEmpty ||
replyItem.replyControl.subReplyEntryText.isNotEmpty)) ...[
( //replyItem.replyControl!.isShow! ||
replyItem.replies.isNotEmpty ||
replyItem.replyControl.subReplyEntryText.isNotEmpty)) ...[
Padding(
padding: const EdgeInsets.only(top: 5, bottom: 12),
child: replyItemRow(
@@ -597,15 +617,19 @@ class ReplyItemGrpc extends StatelessWidget {
var position = textPainter.getPositionForOffset(
Offset(
textSize.width,
maxHeight,
maxHeight, // textSize.height,
),
);
// final endOffset = textPainter.getOffsetBefore(position.offset);
message = message.substring(0, position.offset);
}
// return TextSpan(text: message);
// 投票
if (content.hasVote()) {
message.splitMapJoin(RegExp(r"\{vote:\d+?\}"), onMatch: (Match match) {
// String matchStr = match[0]!;
spanChildren.add(
TextSpan(
text: '投票: ${content.vote.title}',
@@ -631,7 +655,12 @@ class ReplyItemGrpc extends StatelessWidget {
message = message.replaceAll(RegExp(r"\{vote:\d+?\}"), "");
}
message = parse(message).body?.text ?? message;
// .replaceAll('&amp;', '&')
// .replaceAll('&lt;', '<')
// .replaceAll('&gt;', '>')
// .replaceAll('&quot;', '"')
// .replaceAll('&apos;', "'")
// .replaceAll('&nbsp;', ' ');
// 构建正则表达式
final List<String> specialTokens = [
...content.emote.keys,
@@ -657,6 +686,12 @@ class ReplyItemGrpc extends StatelessWidget {
spanChildren.add(TextSpan(
text: str,
));
// TextSpan(
//
// text: str,
// recognizer: TapGestureRecognizer()
// ..onTap = () => replyReply
// ?.call(replyItem.root == 0 ? replyItem : fReplyItem)))));
}
late final bool enableWordRe =
@@ -965,6 +1000,7 @@ class ReplyItemGrpc extends StatelessWidget {
),
);
}
// spanChildren.add(TextSpan(text: matchMember));
return TextSpan(children: spanChildren);
}

View File

@@ -23,6 +23,7 @@ class ZanButtonGrpc extends StatefulWidget {
class _ZanButtonGrpcState extends State<ZanButtonGrpc> {
Future onHateReply() async {
feedBack();
// SmartDialog.showLoading(msg: 'piliplus ...');
final int oid = widget.replyItem.oid.toInt();
final int rpid = widget.replyItem.id.toInt();
// 1 已点赞 2 不喜欢 0 未操作
@@ -45,6 +46,7 @@ class _ZanButtonGrpcState extends State<ZanButtonGrpc> {
}
widget.replyItem.replyControl.action = $fixnum.Int64(2);
} else {
// replyItem.like = replyItem.like! - 1;
widget.replyItem.replyControl.action = $fixnum.Int64(0);
}
setState(() {});
@@ -56,6 +58,7 @@ class _ZanButtonGrpcState extends State<ZanButtonGrpc> {
// 评论点赞
Future onLikeReply() async {
feedBack();
// SmartDialog.showLoading(msg: 'piliplus ...');
final int oid = widget.replyItem.oid.toInt();
final int rpid = widget.replyItem.id.toInt();
// 1 已点赞 2 不喜欢 0 未操作
@@ -67,6 +70,7 @@ class _ZanButtonGrpcState extends State<ZanButtonGrpc> {
rpid: rpid,
action: action,
);
// SmartDialog.dismiss();
if (res['status']) {
SmartDialog.showToast(
widget.replyItem.replyControl.action.toInt() != 1 ? '点赞成功' : '取消赞');

View File

@@ -70,11 +70,14 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
late VideoDetailController videoDetailController;
late VideoReplyController _videoReplyController;
PlPlayerController? plPlayerController;
late StreamController<double> appbarStream;
late VideoIntroController videoIntroController;
late BangumiIntroController bangumiIntroController;
late final _introController = ScrollController();
late String heroTag;
double doubleOffset = 0;
// 自动退出全屏
late bool autoExitFullscreen;
late bool autoPlayEnable;
@@ -83,7 +86,12 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
late bool autoPiP;
late bool pipNoDanmaku;
late bool removeSafeArea;
// late bool showStatusBarBackgroundColor;
// final Floating floating = Floating();
// 生命周期监听
// late final AppLifecycleListener _lifecycleListener;
bool isShowing = true;
// StreamSubscription<Duration>? _bufferedListener;
bool get isFullScreen => plPlayerController?.isFullScreen.value ?? false;
bool get _shouldShowSeasonPanel =>
@@ -161,8 +169,13 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
setting.get(SettingBoxKey.enableVerticalExpand, defaultValue: false);
removeSafeArea = setting.get(SettingBoxKey.videoPlayerRemoveSafeArea,
defaultValue: false);
// showStatusBarBackgroundColor = setting.get(
// SettingBoxKey.videoPlayerShowStatusBarBackgroundColor,
// defaultValue: false);
if (removeSafeArea) hideStatusBar();
videoSourceInit();
appbarStreamListen();
// lifecycleListener();
autoScreen();
if (Platform.isAndroid) {
Utils.channel.setMethodCallHandler((call) async {
@@ -223,6 +236,11 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
plPlayerController?.play();
}
// 流
appbarStreamListen() {
appbarStream = StreamController<double>();
}
// 播放器状态监听
void playerListener(PlayerStatus? status) async {
try {
@@ -330,6 +348,27 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
await plPlayerController!.autoEnterFullscreen();
}
// // 生命周期监听
// void lifecycleListener() {
// _lifecycleListener = AppLifecycleListener(
// onResume: () => _handleTransition('resume'),
// // 后台
// onInactive: () => _handleTransition('inactive'),
// // 在Android和iOS端不生效
// onHide: () => _handleTransition('hide'),
// onShow: () => _handleTransition('show'),
// onPause: () => _handleTransition('pause'),
// onRestart: () => _handleTransition('restart'),
// onDetach: () => _handleTransition('detach'),
// // 只作用于桌面端
// onExitRequested: () {
// ScaffoldMessenger.maybeOf(context)
// ?.showSnackBar(const SnackBar(content: Text("拦截应用退出")));
// return Future.value(AppExitResponse.cancel);
// },
// );
// }
@override
void dispose() {
_listenerDetail?.cancel();
@@ -357,12 +396,16 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
}
videoDetailController.positionSubscription?.cancel();
videoIntroController.canelTimer();
appbarStream.close();
// floating.dispose();
// videoDetailController.floating?.dispose();
videoIntroController.videoDetail.close();
videoDetailController.cid.close();
if (!horizontalScreen) {
AutoOrientation.portraitUpMode();
}
shutdownTimerService.handleWaitingFinished();
// _bufferedListener?.cancel();
if (videoDetailController.plPlayerController.backToHome != true) {
videoPlayerServiceHandler.onVideoDetailDispose(heroTag);
}
@@ -375,13 +418,16 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
PlPlayerController.updatePlayCount();
}
VideoDetailPageV.routeObserver.unsubscribe(this);
// _lifecycleListener.dispose();
showStatusBar();
// _animationController.dispose();
super.dispose();
}
@override
// 离开当前页面时
void didPushNext() async {
// _bufferedListener?.cancel();
if (videoDetailController.imageStatus) {
return;
}
@@ -396,7 +442,11 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
videoDetailController.playerStatus =
plPlayerController?.playerStatus.status.value;
/// 开启
// if (setting.get(SettingBoxKey.enableAutoBrightness, defaultValue: true)
// as bool) {
videoDetailController.brightness = plPlayerController?.brightness.value;
// }
if (plPlayerController != null) {
videoDetailController.makeHeartBeat();
videoDetailController.showVP = plPlayerController!.showVP.value;
@@ -441,6 +491,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
}
}
super.didPopNext();
// final bool autoplay = autoPlayEnable;
videoDetailController.autoPlay.value =
!videoDetailController.isShowCover.value;
if (videoDetailController.isShowCover.value.not) {
@@ -453,6 +504,29 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
await videoDetailController.playerInit();
}
// if (videoDetailController.playerStatus == PlayerStatus.playing) {
// plPlayerController?.play();
// }
/// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回
// if (autoplay) {
// // await Future.delayed(const Duration(milliseconds: 300));
// debugPrint(plPlayerController);
// if (plPlayerController?.buffered.value == Duration.zero) {
// _bufferedListener = plPlayerController?.buffered.listen((p0) {
// debugPrint("p0");
// debugPrint(p0);
// if (p0 > Duration.zero) {
// _bufferedListener!.cancel();
// plPlayerController?.seekTo(videoDetailController.defaultST);
// plPlayerController?.play();
// }
// });
// } else {
// plPlayerController?.seekTo(videoDetailController.defaultST);
// plPlayerController?.play();
// }
// }
Future.delayed(const Duration(milliseconds: 600), () {
AutoOrientation.fullAutoMode();
});
@@ -470,6 +544,17 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
: Theme.of(context);
}
// void _handleTransition(String name) {
// switch (name) {
// case 'inactive':
// if (plPlayerController != null &&
// playerStatus == PlayerStatus.playing) {
// autoEnterPip();
// }
// break;
// }
// }
void enterPip() {
if (Get.currentRoute.startsWith('/video') &&
videoDetailController.floating != null) {
@@ -539,8 +624,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
bottom: !removeSafeArea &&
MediaQuery.of(context).orientation == Orientation.portrait &&
isFullScreen,
left: false,
right: false,
left: false, //!isFullScreen,
right: false, //!isFullScreen,
child: Scaffold(
resizeToAvoidBottomInset: false,
key: videoDetailController.scaffoldKey,
@@ -1096,6 +1181,41 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
),
),
),
// Expanded(
// child: TabBarView(
// physics: const ClampingScrollPhysics(),
// controller: videoDetailController.tabCtr,
// children: [
// CustomScrollView(
// key: const PageStorageKey<String>('简介'),
// slivers: [
// if (videoDetailController.videoType ==
// SearchType.video) ...[
// const VideoIntroPanel(),
// ] else if (videoDetailController.videoType ==
// SearchType.media_bangumi) ...[
// Obx(() => BangumiIntroPanel(
// cid: videoDetailController.cid.value)),
// ],
// SliverToBoxAdapter(
// child: Divider(
// indent: 12,
// endIndent: 12,
// color: themeData.dividerColor.withOpacity(0.06),
// ),
// ),
// const RelatedVideoPanel(),
// ],
// ),
// Obx(
// () => VideoReplyPanel(
// bvid: videoDetailController.bvid,
// oid: videoDetailController.oid.value,
// ),
// )
// ],
// ),
// ),
],
);
}
@@ -1199,7 +1319,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
left: !removeSafeArea && !isFullScreen,
right: !removeSafeArea && !isFullScreen,
top: !removeSafeArea,
bottom: false,
bottom: false, //!removeSafeArea,
child: childWhenDisabledLandscapeInner,
),
),
@@ -1219,7 +1339,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
left: !removeSafeArea && !isFullScreen,
right: !removeSafeArea && !isFullScreen,
top: !removeSafeArea,
bottom: false,
bottom: false, //!removeSafeArea,
child: childWhenDisabledAlmostSquareInner,
),
);
@@ -1460,14 +1580,53 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
// if (!isShowing) {
// return ColoredBox(color: themeData.colorScheme.surface);
// }
if (constraints.maxWidth > constraints.maxHeight * 1.25) {
// hideStatusBar();
// videoDetailController.hiddenReplyReplyPanel();
return autoChoose(childWhenDisabledLandscape);
} else if (constraints.maxWidth * (9 / 16) <
(2 / 5) * constraints.maxHeight) {
// if (!isFullScreen) {
// if (!removeSafeArea) showStatusBar();
// }
return autoChoose(childWhenDisabled);
} else {
// if (!isFullScreen) {
// if (!removeSafeArea) showStatusBar();
// }
return autoChoose(childWhenDisabledAlmostSquare);
}
//
// final Orientation orientation =
// constraints.maxWidth > constraints.maxHeight * 1.25
// ? Orientation.landscape
// : Orientation.portrait;
// if (orientation == Orientation.landscape) {
// if (!horizontalScreen) {
// hideStatusBar();
// videoDetailController.hiddenReplyReplyPanel();
// }
// } else {
// if (!isFullScreen) {
// showStatusBar();
// }
// }
// if (Platform.isAndroid) {
// return PiPSwitcher(
// childWhenDisabled:
// !horizontalScreen || orientation == Orientation.portrait
// ? childWhenDisabled
// : childWhenDisabledLandscape,
// childWhenEnabled: childWhenEnabled,
// floating: floating,
// );
// }
// return !horizontalScreen || orientation == Orientation.portrait
// ? childWhenDisabled
// : childWhenDisabledLandscape;
},
);
}
@@ -1512,7 +1671,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
labelStyle:
TabBarTheme.of(context).labelStyle?.copyWith(fontSize: 13) ??
const TextStyle(fontSize: 13),
labelPadding: const EdgeInsets.symmetric(horizontal: 10.0),
labelPadding:
const EdgeInsets.symmetric(horizontal: 10.0), // 设置每个标签的宽度
dividerColor: Colors.transparent,
dividerHeight: 0,
onTap: (value) {
@@ -1934,6 +2094,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
bvid: videoDetailController.bvid,
aid: IdUtils.bv2av(videoDetailController.bvid),
cid: videoDetailController.cid.value,
// count: videoIntroController.videoDetail.value.pages!.length,
// name: videoIntroController.videoDetail.value.pages!,
isReversed:
videoIntroController.videoDetail.value.isPageReversed,
changeFucCall: videoDetailController.videoType ==
@@ -1984,6 +2146,10 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
videoIntroController.videoDetail.value.ugcSeason!.id,
list: videoIntroController
.videoDetail.value.ugcSeason!.sections!,
// count: videoIntroController
// .videoDetail.value.ugcSeason!.epCount!,
// name:
// videoIntroController.videoDetail.value.ugcSeason!.title!,
bvid: videoDetailController.bvid,
aid: IdUtils.bv2av(videoDetailController.bvid),
cid: videoDetailController.seasonCid ?? 0,

View File

@@ -45,9 +45,11 @@ class _AiDetailState extends CommonCollapseSlidePageState<AiDetail> {
spanChildren.add(
TextSpan(
text: match.group(0),
style: TextStyle(color: Theme.of(context).colorScheme.primary),
style: TextStyle(
color: Theme.of(context).colorScheme.primary), // 设置颜色为蓝色
recognizer: TapGestureRecognizer()
..onTap = () {
// 处理点击事件
try {
PageUtils.handleWebview(match.group(0)!);
} catch (err) {

View File

@@ -50,6 +50,15 @@ class ScrollAppBar extends StatelessWidget {
],
),
),
// actions: [
// IconButton(
// onPressed: () {},
// icon: const Icon(
// Icons.share,
// size: 20,
// )),
// const SizedBox(width: 12)
// ],
),
),
),

View File

@@ -0,0 +1,75 @@
import 'package:flutter/material.dart';
class ExpandedSection extends StatefulWidget {
final Widget? child;
final bool expand;
final double begin;
final double end;
const ExpandedSection({
super.key,
this.expand = false,
this.child,
this.begin = 0.0,
this.end = 1.0,
});
@override
State<ExpandedSection> createState() => _ExpandedSectionState();
}
class _ExpandedSectionState extends State<ExpandedSection>
with SingleTickerProviderStateMixin {
late AnimationController expandController;
late Animation<double> animation;
@override
void initState() {
super.initState();
prepareAnimations();
_runExpandCheck();
}
void prepareAnimations() {
expandController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 400));
Animation<double> curve = CurvedAnimation(
parent: expandController,
curve: Curves.fastOutSlowIn,
);
animation = Tween(begin: widget.begin, end: widget.end).animate(curve);
// animation = CurvedAnimation(
// parent: expandController,
// curve: Curves.fastOutSlowIn,
// );
}
void _runExpandCheck() {
if (widget.expand) {
expandController.forward();
} else {
expandController.reverse();
}
}
@override
void didUpdateWidget(ExpandedSection oldWidget) {
super.didUpdateWidget(oldWidget);
_runExpandCheck();
}
@override
void dispose() {
expandController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SizeTransition(
axisAlignment: -1.0,
sizeFactor: animation,
child: widget.child,
);
}
}

View File

@@ -131,6 +131,31 @@ class HeaderControlState extends State<HeaderControl> {
padding: EdgeInsets.zero,
children: [
const SizedBox(height: 14),
// ListTile(
// onTap: () {},
// dense: true,
// enabled: false,
// leading:
// const Icon(Icons.network_cell_outlined, size: 20),
// title: Text('省流模式', style: titleStyle),
// subtitle: Text('低画质 减少视频缓存', style: subTitleStyle),
// trailing: Transform.scale(
// scale: 0.75,
// child: Switch(
// thumbIcon: WidgetStateProperty.resolveWith<Icon?>(
// (Set<WidgetState> states) {
// if (states.isNotEmpty &&
// states.first == WidgetState.selected) {
// return const Icon(Icons.done);
// }
// return null; // All other states will use the default thumbIcon.
// }),
// value: false,
// onChanged: (value) => {},
// ),
// ),
// ),
// if (videoDetailCtr.userInfo != null)
ListTile(
dense: true,
onTap: () {
@@ -339,6 +364,7 @@ class HeaderControlState extends State<HeaderControl> {
};
},
),
ListTile(
dense: true,
onTap: () => {Get.back(), showSetVideoQa()},
@@ -1227,6 +1253,7 @@ class HeaderControlState extends State<HeaderControl> {
min: 0,
max: 1,
value: subtitleBgOpaticy,
// label: '${(subtitleBgOpaticy * 100).toInt()}%',
onChanged: (double val) {
updateOpacity(val.toPrecision(2));
},
@@ -1264,6 +1291,13 @@ class HeaderControlState extends State<HeaderControl> {
{'value': 6, 'label': '彩色'},
];
final List blockTypes = widget.controller.blockTypes;
// 显示区域
// final List<Map<String, dynamic>> showAreas = [
// {'value': 0.25, 'label': '1/4屏'},
// {'value': 0.5, 'label': '半屏'},
// {'value': 0.75, 'label': '3/4屏'},
// {'value': 1.0, 'label': '满屏'},
// ];
// 智能云屏蔽
int danmakuWeight = widget.controller.danmakuWeight;
// 显示区域
@@ -1780,6 +1814,7 @@ class HeaderControlState extends State<HeaderControl> {
min: 1.0,
max: 3.0,
value: danmakuLineHeight,
// label: '$danmakuLineHeight',
onChanged: (double val) {
updateLineHeight(val.toPrecision(1));
},
@@ -2017,6 +2052,14 @@ class HeaderControlState extends State<HeaderControl> {
return SizedBox.shrink();
},
),
// ComBtn(
// icon: const Icon(
// FontAwesomeIcons.cropSimple,
// size: 15,
// color: Colors.white,
// ),
// fuc: () => _.screenshot(),
// ),
if (videoDetailCtr.enableSponsorBlock == true)
SizedBox(
width: 42,
@@ -2096,6 +2139,9 @@ class HeaderControlState extends State<HeaderControl> {
!plPlayerController.isOpenDanmu.value;
setting.put(SettingBoxKey.enableShowDanmaku,
plPlayerController.isOpenDanmu.value);
// SmartDialog.showToast(
// "已${plPlayerController.isOpenDanmu.value ? '开启' : '关闭'}弹幕",
// displayTime: const Duration(seconds: 1));
},
icon: Icon(
plPlayerController.isOpenDanmu.value
@@ -2125,6 +2171,9 @@ class HeaderControlState extends State<HeaderControl> {
SettingBoxKey.enableBackgroundPlay,
defaultValue: true);
if (!enableBackgroundPlay && mounted) {
// SmartDialog.showToast('建议开启【后台播放】功能\n避免画中画没有暂停按钮');
// await Future.delayed(const Duration(seconds: 2), () {
// });
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Column(
@@ -2162,6 +2211,7 @@ class HeaderControlState extends State<HeaderControl> {
plPlayerController
.setBackgroundPlay(true);
SmartDialog.showToast("请重新载入本页面刷新");
// Get.back();
},
child: const Text('启用后台音频服务')),
const SizedBox(width: 10),
@@ -2460,6 +2510,9 @@ class HeaderControlState extends State<HeaderControl> {
@override
Widget build(BuildContext context) {
// final bool isLandscape =
// MediaQuery.of(context).orientation == Orientation.landscape;
return plPlayerController.showFSActionItem
? Obx(() => _buildHeader(true))
: _buildHeader(false);