feat: audio page (#1518)

* feat: audio page

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

* opt ui

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

* impl intro, share, fav

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

* tweaks

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

* load prev/next

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

---------

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
dom
2025-10-11 22:16:16 +08:00
committed by GitHub
parent a65edab7d1
commit 16f577f3fd
62 changed files with 49877 additions and 774 deletions

View File

@@ -56,131 +56,114 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
@override
void didChangeDependencies() {
super.didChangeDependencies();
_videoReplyController.showFab();
if (widget.isNested) {
_videoReplyController.scrollController.removeListener(listener);
} else {
_videoReplyController.scrollController.addListener(listener);
}
bottom = MediaQuery.viewPaddingOf(context).bottom;
}
@override
void dispose() {
if (!widget.isNested) {
_videoReplyController.scrollController.removeListener(listener);
}
super.dispose();
}
void listener() {
final ScrollDirection direction =
_videoReplyController.scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
if (mounted) {
_videoReplyController.showFab();
}
} else if (direction == ScrollDirection.reverse) {
if (mounted) {
_videoReplyController.hideFab();
}
}
}
late double bottom;
@override
Widget build(BuildContext context) {
super.build(context);
final theme = Theme.of(context);
final child = refreshIndicator(
onRefresh: _videoReplyController.onRefresh,
child: Stack(
clipBehavior: Clip.none,
children: [
CustomScrollView(
controller: widget.isNested
? null
: _videoReplyController.scrollController,
physics: widget.isNested
? const AlwaysScrollableScrollPhysics(
parent: ClampingScrollPhysics(),
)
: const AlwaysScrollableScrollPhysics(),
key: const PageStorageKey<String>('评论'),
slivers: <Widget>[
SliverPersistentHeader(
pinned: false,
floating: true,
delegate: CustomSliverPersistentHeaderDelegate(
extent: 40,
bgColor: theme.colorScheme.surface,
child: Container(
height: 40,
padding: const EdgeInsets.fromLTRB(12, 0, 6, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Obx(
() => Text(
_videoReplyController.sortType.value.title,
style: const TextStyle(fontSize: 13),
),
),
SizedBox(
height: 35,
child: TextButton.icon(
onPressed: _videoReplyController.queryBySort,
icon: Icon(
Icons.sort,
size: 16,
color: theme.colorScheme.secondary,
final child = NotificationListener<UserScrollNotification>(
onNotification: (notification) {
final direction = notification.direction;
if (direction == ScrollDirection.forward) {
_videoReplyController.showFab();
} else if (direction == ScrollDirection.reverse) {
_videoReplyController.hideFab();
}
return false;
},
child: refreshIndicator(
onRefresh: _videoReplyController.onRefresh,
child: Stack(
clipBehavior: Clip.none,
children: [
CustomScrollView(
controller: widget.isNested
? null
: _videoReplyController.scrollController,
physics: widget.isNested
? const AlwaysScrollableScrollPhysics(
parent: ClampingScrollPhysics(),
)
: const AlwaysScrollableScrollPhysics(),
key: const PageStorageKey<String>('评论'),
slivers: <Widget>[
SliverPersistentHeader(
pinned: false,
floating: true,
delegate: CustomSliverPersistentHeaderDelegate(
extent: 40,
bgColor: theme.colorScheme.surface,
child: Container(
height: 40,
padding: const EdgeInsets.fromLTRB(12, 0, 6, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Obx(
() => Text(
_videoReplyController.sortType.value.title,
style: const TextStyle(fontSize: 13),
),
label: Obx(
() => Text(
_videoReplyController.sortType.value.label,
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.secondary,
),
SizedBox(
height: 35,
child: TextButton.icon(
onPressed: _videoReplyController.queryBySort,
icon: Icon(
Icons.sort,
size: 16,
color: theme.colorScheme.secondary,
),
label: Obx(
() => Text(
_videoReplyController.sortType.value.label,
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.secondary,
),
),
),
),
),
),
],
],
),
),
),
),
),
Obx(
() => _buildBody(
theme,
_videoReplyController.loadingState.value,
Obx(
() => _buildBody(
theme,
_videoReplyController.loadingState.value,
),
),
],
),
Positioned(
right: 14,
bottom: 14 + bottom,
child: SlideTransition(
position: _videoReplyController.anim,
child: FloatingActionButton(
heroTag: null,
onPressed: () {
feedBack();
_videoReplyController.onReply(
context,
oid: _videoReplyController.aid,
replyType: _videoReplyController.videoType.replyType,
);
},
tooltip: '发表评论',
child: const Icon(Icons.reply),
),
),
],
),
Positioned(
right: 14,
bottom: 14 + bottom,
child: SlideTransition(
position: _videoReplyController.anim,
child: FloatingActionButton(
heroTag: null,
onPressed: () {
feedBack();
_videoReplyController.onReply(
context,
oid: _videoReplyController.aid,
replyType: _videoReplyController.videoType.replyType,
);
},
tooltip: '发表评论',
child: const Icon(Icons.reply),
),
),
),
],
],
),
),
);
if (widget.isNested) {

View File

@@ -884,9 +884,8 @@ class ReplyItemGrpc extends StatelessWidget {
InkWell(
onTap: Get.back,
borderRadius: StyleString.bottomSheetRadius,
child: Container(
child: SizedBox(
height: 35,
padding: const EdgeInsets.only(bottom: 2),
child: Center(
child: Container(
width: 32,