show audio playlist parts

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-10-21 11:13:56 +08:00
parent cf86bb7e13
commit 9808f50816
3 changed files with 197 additions and 94 deletions

View File

@@ -583,13 +583,13 @@ class AudioController extends GetxController
return false;
}
void playIndex(int index) {
if (index == this.index) return;
void playIndex(int index, {List<Int64>? subId}) {
if (index == this.index && subId == null) return;
this.index = index;
final audioItem = playlist![index];
final item = audioItem.item;
oid = item.oid;
subId = item.subId;
this.subId = subId ?? item.subId;
itemType = item.itemType;
_queryPlayUrl().then((res) {
if (res) {

View File

@@ -147,7 +147,8 @@ class _AudioPageState extends State<AudioPage> {
maxWidth: min(640, context.mediaQueryShortestSide),
),
builder: (context) {
final colorScheme = ColorScheme.of(context);
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
return FractionallySizedBox(
heightFactor: !context.mediaQuerySize.isPortrait && Utils.isMobile
? 1.0
@@ -177,101 +178,200 @@ class _AudioPageState extends State<AudioPage> {
Expanded(
child: Material(
type: MaterialType.transparency,
child: refreshIndicator(
onRefresh: () => _controller.loadPrev(context),
child: CustomScrollView(
controller: scrollController,
physics: _controller.reachStart
? const ClampingScrollPhysics()
: const AlwaysScrollableScrollPhysics(
parent: ClampingScrollPhysics(),
child: Theme(
data: theme.copyWith(
dividerColor: Colors.transparent,
),
child: refreshIndicator(
onRefresh: () => _controller.loadPrev(context),
child: CustomScrollView(
controller: scrollController,
physics: _controller.reachStart
? const ClampingScrollPhysics()
: const AlwaysScrollableScrollPhysics(
parent: ClampingScrollPhysics(),
),
slivers: [
SliverPadding(
padding: EdgeInsets.only(
bottom:
MediaQuery.paddingOf(context).bottom + 100,
),
slivers: [
SliverPadding(
padding: EdgeInsets.only(
bottom:
MediaQuery.paddingOf(context).bottom + 100,
),
sliver: SliverList.builder(
itemCount: playlist.length,
itemBuilder: (_, index) {
if (index == playlist.length - 1) {
_controller.loadNext(context);
}
final isCurr = index == _controller.index;
final item = playlist[index];
return ListTile(
dense: true,
minTileHeight: 45,
onTap: () {
Get.back();
if (!isCurr) {
_controller.playIndex(index);
}
},
title: Text.rich(
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: isCurr
? TextStyle(
height: 1,
fontSize: 14,
color: colorScheme.primary,
fontWeight: FontWeight.bold,
)
: const TextStyle(
height: 1,
fontSize: 14,
),
strutStyle: const StrutStyle(
height: 1,
leading: 0,
fontSize: 14,
),
TextSpan(
children: [
if (isCurr) ...[
WidgetSpan(
alignment:
PlaceholderAlignment.bottom,
child: Image.asset(
'assets/images/live.gif',
width: 16,
height: 16,
color: colorScheme.primary,
sliver: SliverList.builder(
itemCount: playlist.length,
itemBuilder: (_, index) {
if (index == playlist.length - 1) {
_controller.loadNext(context);
}
final isCurr = index == _controller.index;
final item = playlist[index];
if (item.parts.length > 1) {
final subId = _controller.subId.firstOrNull;
return ExpansionTile(
dense: true,
minTileHeight: 45,
initiallyExpanded: isCurr,
collapsedIconColor: isCurr
? colorScheme.primary
: null,
iconColor: isCurr
? null
: colorScheme.onSurfaceVariant,
controlAffinity:
ListTileControlAffinity.leading,
title: Text(
item.arc.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: isCurr
? TextStyle(
fontSize: 14,
color: colorScheme.primary,
fontWeight: FontWeight.bold,
)
: const TextStyle(fontSize: 14),
),
trailing: isCurr
? null
: iconButton(
icon: const Icon(Icons.clear),
onPressed: () {
if (index <
_controller.index!) {
_controller.index -= 1;
}
_controller.playlist!.removeAt(
index,
);
(context as Element)
.markNeedsBuild();
},
iconColor: colorScheme.outline,
size: 28,
iconSize: 18,
),
children: item.parts.map((e) {
final isCurr = e.subId == subId;
return ListTile(
dense: true,
minTileHeight: 45,
contentPadding:
const EdgeInsetsDirectional.only(
start: 56.0,
end: 24.0,
),
onTap: () {
Get.back();
if (!isCurr) {
_controller.playIndex(
index,
subId: [e.subId],
);
}
},
title: Text.rich(
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: isCurr
? TextStyle(
fontSize: 14,
color: colorScheme.primary,
fontWeight: FontWeight.bold,
)
: TextStyle(
fontSize: 14,
color: colorScheme
.onSurfaceVariant,
),
TextSpan(
children: [
if (isCurr) ...[
WidgetSpan(
alignment:
PlaceholderAlignment
.bottom,
child: Image.asset(
'assets/images/live.gif',
width: 16,
height: 16,
color:
colorScheme.primary,
),
),
const TextSpan(text: ' '),
],
TextSpan(text: e.title),
],
),
),
const TextSpan(text: ' '),
);
}).toList(),
);
}
return ListTile(
dense: true,
minTileHeight: 45,
onTap: () {
Get.back();
if (!isCurr) {
_controller.playIndex(index);
}
},
title: Text.rich(
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: isCurr
? TextStyle(
fontSize: 14,
color: colorScheme.primary,
fontWeight: FontWeight.bold,
)
: const TextStyle(fontSize: 14),
TextSpan(
children: [
if (isCurr) ...[
WidgetSpan(
alignment:
PlaceholderAlignment.bottom,
child: Image.asset(
'assets/images/live.gif',
width: 16,
height: 16,
color: colorScheme.primary,
),
),
const TextSpan(text: ' '),
],
TextSpan(
text: item.arc.title,
),
],
TextSpan(
text: item.arc.title,
),
],
),
),
),
trailing: isCurr
? null
: iconButton(
icon: const Icon(Icons.clear),
onPressed: () {
if (index < _controller.index!) {
_controller.index -= 1;
}
_controller.playlist!.removeAt(
index,
);
(context as Element)
.markNeedsBuild();
},
iconColor: colorScheme.outline,
size: 28,
iconSize: 18,
),
);
},
trailing: isCurr
? null
: iconButton(
icon: const Icon(Icons.clear),
onPressed: () {
if (index < _controller.index!) {
_controller.index -= 1;
}
_controller.playlist!.removeAt(
index,
);
(context as Element)
.markNeedsBuild();
},
iconColor: colorScheme.outline,
size: 28,
iconSize: 18,
),
);
},
),
),
),
],
],
),
),
),
),

View File

@@ -291,7 +291,10 @@ class _ReplyPageState extends CommonRichTextPubPageState<ReplyPage> {
Text(
title,
maxLines: 1,
style: const TextStyle(fontSize: 13),
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.onSurface,
),
),
],
),