Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-06-13 11:49:38 +08:00
parent f824477ddb
commit c05fbde3fa
106 changed files with 2780 additions and 3200 deletions

View File

@@ -13,7 +13,6 @@ import 'package:PiliPlus/pages/search/widgets/search_text.dart';
import 'package:PiliPlus/pages/video/controller.dart';
import 'package:PiliPlus/pages/video/introduction/ugc/controller.dart';
import 'package:PiliPlus/pages/video/introduction/ugc/widgets/action_item.dart';
import 'package:PiliPlus/pages/video/introduction/ugc/widgets/action_row_item.dart';
import 'package:PiliPlus/pages/video/introduction/ugc/widgets/page.dart';
import 'package:PiliPlus/pages/video/introduction/ugc/widgets/season.dart';
import 'package:PiliPlus/utils/app_scheme.dart';
@@ -435,7 +434,9 @@ class _VideoInfoState extends State<VideoInfo> {
'${videoItem['staff'][index].mid}'] ==
null
? Material(
color: Colors.transparent,
type:
MaterialType.transparency,
shape: const CircleBorder(),
child: InkWell(
customBorder:
const CircleBorder(),
@@ -837,11 +838,9 @@ class _VideoInfoState extends State<VideoInfo> {
Widget actionGrid(
BuildContext context, VideoIntroController videoIntroController) {
return Container(
margin: const EdgeInsets.only(top: 1),
return SizedBox(
height: 48,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Obx(
() => ActionItem(
@@ -941,63 +940,6 @@ class _VideoInfoState extends State<VideoInfo> {
);
}
Widget actionRow(
BuildContext context,
VideoIntroController videoIntroController,
VideoDetailController videoDetailCtr,
) {
return Row(children: <Widget>[
Obx(
() => ActionRowItem(
icon: const Icon(FontAwesomeIcons.thumbsUp),
onTap: () => handleState(videoIntroController.actionLikeVideo),
selectStatus: videoIntroController.hasLike.value,
isLoading: widget.isLoading,
text: !widget.isLoading ? videoDetail.stat!.like!.toString() : '-',
),
),
const SizedBox(width: 8),
Obx(
() => ActionRowItem(
icon: const Icon(FontAwesomeIcons.b),
onTap: () => handleState(videoIntroController.actionCoinVideo),
selectStatus: videoIntroController.hasCoin,
isLoading: widget.isLoading,
text: !widget.isLoading ? 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,
isLoading: widget.isLoading,
text:
!widget.isLoading ? videoDetail.stat!.favorite!.toString() : '-',
),
),
const SizedBox(width: 8),
ActionRowItem(
icon: const Icon(FontAwesomeIcons.comment),
onTap: () => videoDetailCtr.tabCtr.animateTo(1),
selectStatus: false,
isLoading: widget.isLoading,
text: !widget.isLoading ? videoDetail.stat!.reply!.toString() : '-',
),
const SizedBox(width: 8),
ActionRowItem(
icon: const Icon(FontAwesomeIcons.share),
onTap: () => videoIntroController.actionShareVideo(context),
selectStatus: false,
isLoading: widget.isLoading,
text: '转发',
),
]);
}
InlineSpan buildContent(ThemeData theme, VideoDetailData content) {
final List descV2 = content.descV2!;
// type
@@ -1073,15 +1015,11 @@ class _VideoInfoState extends State<VideoInfo> {
return TextSpan(children: spanChildren);
case 2:
final Color colorSchemePrimary = theme.colorScheme.primary;
final String heroTag = Utils.makeHeroTag(currentDesc.bizId);
return TextSpan(
text: '@${currentDesc.rawText}',
style: TextStyle(color: colorSchemePrimary),
recognizer: TapGestureRecognizer()
..onTap = () => Get.toNamed(
'/member?mid=${currentDesc.bizId}',
arguments: {'face': '', 'heroTag': heroTag},
),
..onTap = () => Get.toNamed('/member?mid=${currentDesc.bizId}'),
);
default:
return const TextSpan();

View File

@@ -136,70 +136,73 @@ class ActionItemState extends State<ActionItem>
label: (widget.text ?? "") +
(widget.selectStatus ? "" : "") +
widget.semanticsLabel,
child: InkWell(
borderRadius: const BorderRadius.all(Radius.circular(6)),
onTap: _isThumbsUp
? null
: () {
feedBack();
widget.onTap?.call();
},
onLongPress: _isThumbsUp ? null : widget.onLongPress,
onTapDown: _isThumbsUp ? (details) => _startLongPress() : null,
onTapUp: _isThumbsUp ? (details) => _cancelLongPress() : null,
onTapCancel: _isThumbsUp ? () => _cancelLongPress(true) : null,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
if (widget.needAnim && !_hideCircle)
CustomPaint(
size: const Size(28, 28),
painter: _ArcPainter(
color: theme.colorScheme.primary,
sweepAngle: _animation!.value,
child: Material(
type: MaterialType.transparency,
child: InkWell(
borderRadius: const BorderRadius.all(Radius.circular(6)),
onTap: _isThumbsUp
? null
: () {
feedBack();
widget.onTap?.call();
},
onLongPress: _isThumbsUp ? null : widget.onLongPress,
onTapDown: _isThumbsUp ? (details) => _startLongPress() : null,
onTapUp: _isThumbsUp ? (details) => _cancelLongPress() : null,
onTapCancel: _isThumbsUp ? () => _cancelLongPress(true) : null,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
if (widget.needAnim && !_hideCircle)
CustomPaint(
size: const Size(28, 28),
painter: _ArcPainter(
color: theme.colorScheme.primary,
sweepAngle: _animation!.value,
),
)
else
const SizedBox(width: 28, height: 28),
Icon(
widget.selectStatus
? widget.selectIcon!.icon!
: widget.icon.icon,
size: 18,
color: widget.selectStatus
? theme.colorScheme.primary
: widget.icon.color ?? theme.colorScheme.outline,
),
],
),
if (widget.text != null)
AnimatedOpacity(
opacity: widget.isLoading! ? 0 : 1,
duration: const Duration(milliseconds: 200),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder:
(Widget child, Animation<double> animation) {
return ScaleTransition(scale: animation, child: child);
},
child: Text(
widget.text!,
key: ValueKey<String>(widget.text!),
style: TextStyle(
color: widget.selectStatus
? theme.colorScheme.primary
: theme.colorScheme.outline,
fontSize: theme.textTheme.labelSmall!.fontSize,
),
semanticsLabel: "",
),
)
else
const SizedBox(width: 28, height: 28),
Icon(
widget.selectStatus
? widget.selectIcon!.icon!
: widget.icon.icon,
size: 18,
color: widget.selectStatus
? theme.colorScheme.primary
: widget.icon.color ?? theme.colorScheme.outline,
),
],
),
if (widget.text != null)
AnimatedOpacity(
opacity: widget.isLoading! ? 0 : 1,
duration: const Duration(milliseconds: 200),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder:
(Widget child, Animation<double> animation) {
return ScaleTransition(scale: animation, child: child);
},
child: Text(
widget.text!,
key: ValueKey<String>(widget.text!),
style: TextStyle(
color: widget.selectStatus
? theme.colorScheme.primary
: theme.colorScheme.outline,
fontSize: theme.textTheme.labelSmall!.fontSize,
),
semanticsLabel: "",
),
),
),
],
],
),
),
),
);

View File

@@ -1,79 +0,0 @@
import 'package:PiliPlus/utils/feed_back.dart';
import 'package:flutter/material.dart';
class ActionRowItem extends StatelessWidget {
final Icon? icon;
final Icon? selectIcon;
final Function? onTap;
final bool? isLoading;
final String? text;
final bool selectStatus;
final Function? onLongPress;
const ActionRowItem({
super.key,
this.icon,
this.selectIcon,
this.onTap,
this.isLoading,
this.text,
this.selectStatus = false,
this.onLongPress,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Material(
color: selectStatus
? theme.colorScheme.primaryContainer.withValues(alpha: 0.6)
: theme.highlightColor.withValues(alpha: 0.2),
borderRadius: const BorderRadius.all(Radius.circular(30)),
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: () => {
feedBack(),
onTap?.call(),
},
onLongPress: () {
feedBack();
onLongPress?.call();
},
child: Padding(
padding: const EdgeInsets.fromLTRB(15, 7, 15, 7),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (icon != null) ...[
Icon(icon!.icon!,
size: 13,
color: selectStatus
? theme.colorScheme.primary
: theme.colorScheme.onSecondaryContainer),
const SizedBox(width: 6),
],
AnimatedOpacity(
opacity: isLoading! ? 0 : 1,
duration: const Duration(milliseconds: 200),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder:
(Widget child, Animation<double> animation) {
return ScaleTransition(scale: animation, child: child);
},
child: Text(
text ?? '',
key: ValueKey<String>(text ?? ''),
style: TextStyle(
color: selectStatus ? theme.colorScheme.primary : null,
fontSize: theme.textTheme.labelMedium!.fontSize),
),
),
),
],
),
),
),
);
}
}

View File

@@ -1,102 +1,6 @@
import 'package:PiliPlus/utils/feed_back.dart';
import 'package:flutter/material.dart';
class MenuRow extends StatelessWidget {
const MenuRow({
super.key,
this.isLoading,
});
final bool? isLoading;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Container(
width: double.infinity,
color: theme.colorScheme.surface,
padding: const EdgeInsets.only(top: 9, bottom: 9, left: 12),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(children: [
ActionRowLineItem(
onTap: () => {},
isLoading: isLoading,
text: '推荐',
selectStatus: false,
),
const SizedBox(width: 8),
ActionRowLineItem(
onTap: () => {},
isLoading: isLoading,
text: '弹幕',
selectStatus: false,
),
const SizedBox(width: 8),
ActionRowLineItem(
onTap: () => {},
isLoading: isLoading,
text: '评论列表',
selectStatus: false,
),
const SizedBox(width: 8),
ActionRowLineItem(
onTap: () => {},
isLoading: isLoading,
text: '播放列表',
selectStatus: false,
),
]),
),
);
}
Widget actionRowLineItem(
ThemeData theme, Function? onTap, bool? isLoading, String? text,
{bool selectStatus = false}) {
return Material(
color: selectStatus
? theme.highlightColor.withValues(alpha: 0.2)
: Colors.transparent,
borderRadius: const BorderRadius.all(Radius.circular(30)),
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: () => {
feedBack(),
onTap?.call(),
},
child: Container(
padding: const EdgeInsets.fromLTRB(13, 5.5, 13, 4.5),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(30)),
border: Border.all(
color: selectStatus
? Colors.transparent
: theme.highlightColor.withValues(alpha: 0.2),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedOpacity(
opacity: isLoading! ? 0 : 1,
duration: const Duration(milliseconds: 200),
child: Text(
text!,
style: TextStyle(
fontSize: 13,
color: selectStatus
? theme.colorScheme.onSurface
: theme.colorScheme.outline),
),
),
],
),
),
),
);
}
}
class ActionRowLineItem extends StatelessWidget {
const ActionRowLineItem({
super.key,
@@ -118,12 +22,11 @@ class ActionRowLineItem extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Material(
color: selectStatus
? theme.colorScheme.secondaryContainer
: Colors.transparent,
color: selectStatus ? theme.colorScheme.secondaryContainer : null,
type: selectStatus ? MaterialType.canvas : MaterialType.transparency,
borderRadius: const BorderRadius.all(Radius.circular(30)),
clipBehavior: Clip.hardEdge,
child: InkWell(
borderRadius: const BorderRadius.all(Radius.circular(30)),
onTap: () => {
feedBack(),
onTap?.call(),

View File

@@ -147,8 +147,8 @@ class _PagesPanelState extends State<PagesPanel> {
child: Material(
color: theme.colorScheme.onInverseSurface,
borderRadius: const BorderRadius.all(Radius.circular(6)),
clipBehavior: Clip.hardEdge,
child: InkWell(
borderRadius: const BorderRadius.all(Radius.circular(6)),
onTap: () {
if (widget.showEpisodes == null) {
Get.back();

View File

@@ -90,68 +90,66 @@ class _SeasonPanelState extends State<SeasonPanel> {
return const SizedBox.shrink();
}
final theme = Theme.of(context);
return Builder(builder: (BuildContext context) {
return Container(
margin: const EdgeInsets.only(
top: 8,
left: 2,
right: 2,
),
child: Material(
color: theme.colorScheme.onInverseSurface,
return Padding(
padding: const EdgeInsets.only(
top: 8,
left: 2,
right: 2,
),
child: Material(
color: theme.colorScheme.onInverseSurface,
borderRadius: const BorderRadius.all(Radius.circular(6)),
child: InkWell(
borderRadius: const BorderRadius.all(Radius.circular(6)),
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: widget.onTap == false
? null
: () => widget.showEpisodes(
_videoDetailController.seasonIndex.value,
videoDetail.ugcSeason,
null,
_videoDetailController.bvid,
null,
_videoDetailController.seasonCid,
),
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 12, 8, 12),
child: Row(
children: <Widget>[
Expanded(
child: Text(
'合集:${videoDetail.ugcSeason!.title!}',
style: theme.textTheme.labelMedium,
overflow: TextOverflow.ellipsis,
),
onTap: widget.onTap == false
? null
: () => widget.showEpisodes(
_videoDetailController.seasonIndex.value,
videoDetail.ugcSeason,
null,
_videoDetailController.bvid,
null,
_videoDetailController.seasonCid,
),
const SizedBox(width: 15),
Image.asset(
'assets/images/live.png',
color: theme.colorScheme.primary,
height: 12,
semanticLabel: "正在播放:",
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 12, 8, 12),
child: Row(
children: <Widget>[
Expanded(
child: Text(
'合集:${videoDetail.ugcSeason!.title!}',
style: theme.textTheme.labelMedium,
overflow: TextOverflow.ellipsis,
),
const SizedBox(width: 10),
Obx(
() => Text(
'${currentIndex.value + 1}/${episodes.length}',
style: theme.textTheme.labelMedium,
semanticsLabel:
'${currentIndex.value + 1}集,共${episodes.length}',
),
),
const SizedBox(width: 15),
Image.asset(
'assets/images/live.png',
color: theme.colorScheme.primary,
height: 12,
semanticLabel: "正在播放:",
),
const SizedBox(width: 10),
Obx(
() => Text(
'${currentIndex.value + 1}/${episodes.length}',
style: theme.textTheme.labelMedium,
semanticsLabel:
'${currentIndex.value + 1}集,共${episodes.length}',
),
const SizedBox(width: 6),
const Icon(
Icons.arrow_forward_ios_outlined,
size: 13,
semanticLabel: '查看',
)
],
),
),
const SizedBox(width: 6),
const Icon(
Icons.arrow_forward_ios_outlined,
size: 13,
semanticLabel: '查看',
)
],
),
),
),
);
});
),
);
}
void _findEpisode() {