mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-04-28 06:15:51 +08:00
@@ -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();
|
||||
|
||||
@@ -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: "",
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user