refa persistent header & dynamic sliver appbar

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-03-02 16:16:17 +08:00
parent 1dbc54f063
commit 9c7b18710c
30 changed files with 1691 additions and 883 deletions

View File

@@ -1,8 +1,9 @@
import 'dart:math' show pi;
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/skeleton/video_reply.dart';
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_pinned_header.dart';
import 'package:PiliPlus/common/widgets/view_safe_area.dart';
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
show ReplyInfo;
@@ -103,44 +104,33 @@ abstract class CommonDynPageState<T extends StatefulWidget> extends State<T>
Widget buildReplyHeader(ThemeData theme) {
final secondary = theme.colorScheme.secondary;
return SliverPersistentHeader(
pinned: true,
delegate: CustomSliverPersistentHeaderDelegate(
extent: 45,
bgColor: theme.colorScheme.surface,
child: Container(
height: 45,
padding: const EdgeInsets.only(left: 12, right: 6),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Obx(
() {
final count = controller.count.value;
return Text(
'${count == -1 ? 0 : NumUtils.numFormat(count)}条回复',
);
},
),
SizedBox(
height: 35,
child: TextButton.icon(
onPressed: controller.queryBySort,
icon: Icon(
Icons.sort,
size: 16,
color: secondary,
),
label: Obx(
() => Text(
controller.sortType.value.label,
style: TextStyle(fontSize: 13, color: secondary),
),
),
return SliverPinnedHeader(
backgroundColor: theme.colorScheme.surface,
child: Padding(
padding: const .fromLTRB(12, 2.5, 6, 2.5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Obx(
() {
final count = controller.count.value;
return Text(
'${count == -1 ? 0 : NumUtils.numFormat(count)}条回复',
);
},
),
TextButton.icon(
style: StyleString.buttonStyle,
onPressed: controller.queryBySort,
icon: Icon(Icons.sort, size: 16, color: secondary),
label: Obx(
() => Text(
controller.sortType.value.label,
style: TextStyle(fontSize: 13, color: secondary),
),
),
],
),
),
],
),
),
);

View File

@@ -1,11 +1,11 @@
import 'dart:async';
import 'dart:math';
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/flutter/draggable_sheet/draggable_scrollable_sheet_topic.dart'
as topic_sheet;
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_pinned_header.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models_new/dynamic/dyn_mention/group.dart';
import 'package:PiliPlus/pages/dynamics_mention/controller.dart';
@@ -247,18 +247,14 @@ class _DynMentionPanelState
}
return SliverMainAxisGroup(
slivers: [
SliverPersistentHeader(
pinned: true,
delegate: CustomSliverPersistentHeaderDelegate(
extent: 40,
needRebuild: true,
bgColor: theme.colorScheme.surface,
child: Container(
height: 40,
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(group.groupName!),
SliverPinnedHeader(
backgroundColor: theme.colorScheme.surface,
child: Padding(
padding: const .symmetric(
horizontal: 16,
vertical: 10,
),
child: Text(group.groupName!),
),
),
SliverList.builder(

View File

@@ -1,10 +1,10 @@
import 'package:PiliPlus/common/widgets/custom_icon.dart';
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/dynamic_sliver_appbar_medium.dart';
import 'package:PiliPlus/common/widgets/dynamic_sliver_app_bar/dynamic_sliver_app_bar.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/pair.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_pinned_header.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/common/image_type.dart';
import 'package:PiliPlus/models_new/dynamic/dyn_topic_feed/item.dart';
@@ -78,56 +78,49 @@ class _DynTopicPageState extends State<DynTopicPage> with DynMixin {
Obx(() {
final allSortBy = _controller.topicSortByConf.value?.allSortBy;
if (allSortBy != null && allSortBy.isNotEmpty) {
return SliverPersistentHeader(
pinned: true,
delegate: CustomSliverPersistentHeaderDelegate(
extent: 36,
needRebuild: true,
bgColor: theme.colorScheme.surface,
child: Container(
height: 36,
padding: EdgeInsets.only(
left: 12 + padding.left,
top: 6,
bottom: 6,
),
child: Builder(
builder: (context) {
return ToggleButtons(
fillColor: theme.colorScheme.secondaryContainer,
selectedColor:
theme.colorScheme.onSecondaryContainer,
constraints: const BoxConstraints(
minWidth: 54,
minHeight: 24,
),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
borderRadius: const .all(.circular(25)),
onPressed: (index) {
_controller.onSort(allSortBy[index].sortBy!);
(context as Element).markNeedsBuild();
},
isSelected: allSortBy
.map((e) => e.sortBy == _controller.sortBy)
.toList(),
children: allSortBy.map((e) {
return Text(
e.sortName!,
style: const TextStyle(
fontSize: 13,
height: 1,
),
strutStyle: const StrutStyle(
height: 1,
leading: 0,
fontSize: 13,
),
textScaler: TextScaler.noScaling,
);
}).toList(),
);
},
),
return SliverPinnedHeader(
backgroundColor: theme.colorScheme.surface,
child: Padding(
padding: EdgeInsets.only(
left: 12 + padding.left,
top: 6,
bottom: 6,
),
child: Builder(
builder: (context) {
return ToggleButtons(
fillColor: theme.colorScheme.secondaryContainer,
selectedColor: theme.colorScheme.onSecondaryContainer,
constraints: const BoxConstraints(
minWidth: 54,
minHeight: 24,
),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
borderRadius: const .all(.circular(25)),
onPressed: (index) {
_controller.onSort(allSortBy[index].sortBy!);
(context as Element).markNeedsBuild();
},
isSelected: allSortBy
.map((e) => e.sortBy == _controller.sortBy)
.toList(),
children: allSortBy.map((e) {
return Text(
e.sortName!,
style: const TextStyle(
fontSize: 13,
height: 1,
),
strutStyle: const StrutStyle(
height: 1,
leading: 0,
fontSize: 13,
),
textScaler: TextScaler.noScaling,
);
}).toList(),
);
},
),
),
);
@@ -157,10 +150,9 @@ class _DynTopicPageState extends State<DynTopicPage> with DynMixin {
) {
return switch (topState) {
Loading() => const SliverAppBar(),
Success(:final response) when response != null => DynamicSliverAppBarMedium(
pinned: true,
onPerformLayout: (value) =>
_controller.appbarOffset = value - kToolbarHeight - padding.top,
Success(:final response) when response != null => DynamicSliverAppBar.medium(
onPerformLayout: (value) => _controller.appbarOffset =
value.height - kToolbarHeight - padding.top,
title: IgnorePointer(child: Text(response.topicItem!.name)),
flexibleSpace: Container(
decoration: BoxDecoration(

View File

@@ -1,8 +1,8 @@
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
import 'package:PiliPlus/common/widgets/keep_alive_wrapper.dart';
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_pinned_header.dart';
import 'package:PiliPlus/models/common/live/live_dm_silent_type.dart';
import 'package:PiliPlus/models_new/live/live_dm_block/shield_user_list.dart';
import 'package:PiliPlus/pages/live_dm_block/controller.dart';
@@ -102,13 +102,7 @@ class _LiveDmBlockPageState extends State<LiveDmBlockPage> {
ExtendedNestedScrollView.sliverOverlapAbsorberHandleFor(
context,
),
sliver: SliverPersistentHeader(
pinned: true,
delegate: CustomSliverPersistentHeaderDelegate(
extent: 48,
child: tabBar,
),
),
sliver: SliverPinnedHeader(child: tabBar),
),
];
},

View File

@@ -1,7 +1,8 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/skeleton/video_reply.dart';
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_floating_header.dart';
import 'package:PiliPlus/common/widgets/view_safe_area.dart';
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
show ReplyInfo;
@@ -15,7 +16,6 @@ import 'package:PiliPlus/utils/num_utils.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart' show ScrollDirection;
import 'package:get/get.dart';
class MainReplyPage extends StatefulWidget {
@@ -61,9 +61,9 @@ class _MainReplyPageState extends State<MainReplyPage> {
body: NotificationListener<UserScrollNotification>(
onNotification: (notification) {
final direction = notification.direction;
if (direction == ScrollDirection.forward) {
if (direction == .forward) {
_controller.showFab();
} else if (direction == ScrollDirection.reverse) {
} else if (direction == .reverse) {
_controller.hideFab();
}
return false;
@@ -172,44 +172,33 @@ class _MainReplyPageState extends State<MainReplyPage> {
Widget buildReplyHeader(ColorScheme colorScheme) {
final secondary = colorScheme.secondary;
return SliverPersistentHeader(
floating: true,
delegate: CustomSliverPersistentHeaderDelegate(
extent: 45,
bgColor: colorScheme.surface,
child: Container(
height: 45,
padding: const EdgeInsets.only(left: 12, right: 6),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Obx(
() {
final count = _controller.count.value;
return Text(
'${count == -1 ? 0 : NumUtils.numFormat(count)}条回复',
);
},
),
SizedBox(
height: 35,
child: TextButton.icon(
onPressed: _controller.queryBySort,
icon: Icon(
Icons.sort,
size: 16,
color: secondary,
),
label: Obx(
() => Text(
_controller.sortType.value.label,
style: TextStyle(fontSize: 13, color: secondary),
),
),
return SliverFloatingHeaderWidget(
backgroundColor: colorScheme.surface,
child: Padding(
padding: const .fromLTRB(12, 2.5, 6, 2.5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Obx(
() {
final count = _controller.count.value;
return Text(
'${count == -1 ? 0 : NumUtils.numFormat(count)}条回复',
);
},
),
TextButton.icon(
style: StyleString.buttonStyle,
onPressed: _controller.queryBySort,
icon: Icon(Icons.sort, size: 16, color: secondary),
label: Obx(
() => Text(
_controller.sortType.value.label,
style: TextStyle(fontSize: 13, color: secondary),
),
),
],
),
),
],
),
),
);

View File

@@ -1,5 +1,5 @@
import 'package:PiliPlus/common/widgets/dialog/report_member.dart';
import 'package:PiliPlus/common/widgets/dynamic_sliver_appbar_medium.dart';
import 'package:PiliPlus/common/widgets/dynamic_sliver_app_bar/dynamic_sliver_app_bar.dart';
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/http/loading_state.dart';
@@ -328,8 +328,7 @@ class _MemberPageState extends State<MemberPage> {
return const CircularProgressIndicator();
case Success<SpaceData?>(:final response):
if (response != null) {
return DynamicSliverAppBarMedium(
pinned: true,
return DynamicSliverAppBar.medium(
actions: _actions(theme),
title: Text(_userController.username ?? ''),
flexibleSpace: Obx(

View File

@@ -1,8 +1,8 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_floating_header.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models_new/space/space_audio/item.dart';
import 'package:PiliPlus/pages/member_audio/controller.dart';
@@ -80,44 +80,36 @@ class _MemberAudioState extends State<MemberAudio>
response != null && response.isNotEmpty
? SliverMainAxisGroup(
slivers: [
SliverPersistentHeader(
floating: true,
delegate: CustomSliverPersistentHeaderDelegate(
extent: 40,
bgColor: colorScheme.surface,
child: SizedBox(
height: 40,
child: Row(
children: [
const SizedBox(width: 8),
Padding(
padding: const EdgeInsets.only(left: 6),
child: Text(
'${_controller.totalSize ?? 0}',
style: const TextStyle(fontSize: 13),
SliverFloatingHeaderWidget(
backgroundColor: colorScheme.surface,
child: Padding(
padding: const EdgeInsets.fromLTRB(14, 2.5, 8, 2.5),
child: Row(
children: [
Text(
'${_controller.totalSize ?? 0}',
style: const TextStyle(fontSize: 13),
),
Padding(
padding: const EdgeInsets.only(left: 6),
child: TextButton.icon(
style: StyleString.buttonStyle,
onPressed: _controller.toViewPlayAll,
icon: Icon(
Icons.play_circle_outline_rounded,
size: 16,
color: colorScheme.secondary,
),
),
Container(
height: 35,
padding: const EdgeInsets.only(left: 6),
child: TextButton.icon(
onPressed: _controller.toViewPlayAll,
icon: Icon(
Icons.play_circle_outline_rounded,
size: 16,
label: Text(
'播放全部',
style: TextStyle(
fontSize: 13,
color: colorScheme.secondary,
),
label: Text(
'播放全部',
style: TextStyle(
fontSize: 13,
color: colorScheme.secondary,
),
),
),
),
],
),
),
],
),
),
),

View File

@@ -97,13 +97,14 @@ class _MemberContributeState extends State<MemberContribute>
}
Widget _getPageFromType(SpaceTab2Item item) {
final isSingle = _controller.tabs == null;
return switch (item.param) {
'video' => MemberVideo(
type: ContributeType.video,
heroTag: widget.heroTag,
mid: widget.mid,
title: item.title,
isSingle: _controller.tabs == null,
isSingle: isSingle,
),
'charging_video' => MemberVideo(
type: ContributeType.charging,
@@ -116,9 +117,9 @@ class _MemberContributeState extends State<MemberContribute>
mid: widget.mid,
),
'opus' => MemberOpus(
isSingle: _controller.tabs == null,
heroTag: widget.heroTag,
mid: widget.mid,
isSingle: isSingle,
),
'audio' => MemberAudio(
heroTag: widget.heroTag,

View File

@@ -1,7 +1,7 @@
import 'package:PiliPlus/common/skeleton/video_card_h.dart';
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_pinned_header.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models_new/space/space_fav/data.dart';
import 'package:PiliPlus/pages/member_favorite/controller.dart';
@@ -109,56 +109,51 @@ class _MemberFavoriteState extends State<MemberFavorite>
}) {
return SliverMainAxisGroup(
slivers: [
SliverPersistentHeader(
pinned: true,
delegate: CustomSliverPersistentHeaderDelegate(
child: Material(
color: theme.colorScheme.surface,
child: Builder(
builder: (context) {
return InkWell(
onTap: () {
_controller.setExpand(isFav);
(context as Element).markNeedsBuild();
data.refresh();
if (!isEnd.value) {
isEnd.refresh();
}
},
child: Container(
height: 45,
alignment: .centerLeft,
padding: const .only(left: 12),
child: Text.rich(
TextSpan(
children: [
WidgetSpan(
alignment: .middle,
child: Icon(
_controller.isExpand(isFav)
? Icons.expand_less
: Icons.expand_more,
color: theme.colorScheme.outline,
),
SliverPinnedHeader(
child: Material(
color: theme.colorScheme.surface,
child: Builder(
builder: (context) {
return InkWell(
onTap: () {
_controller.setExpand(isFav);
(context as Element).markNeedsBuild();
data.refresh();
if (!isEnd.value) {
isEnd.refresh();
}
},
child: Padding(
padding: const .symmetric(horizontal: 12, vertical: 10),
child: Text.rich(
TextSpan(
children: [
WidgetSpan(
alignment: .middle,
child: Icon(
_controller.isExpand(isFav)
? Icons.expand_less
: Icons.expand_more,
color: theme.colorScheme.outline,
),
TextSpan(
text: ' ${data.value.name}',
style: const TextStyle(fontSize: 14),
),
TextSpan(
text: ' ${data.value.name}',
style: const TextStyle(fontSize: 14),
),
TextSpan(
text: ' ${data.value.mediaListResponse?.count}',
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.outline,
),
TextSpan(
text: ' ${data.value.mediaListResponse?.count}',
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.outline,
),
),
],
),
),
],
),
),
);
},
),
),
);
},
),
),
),

View File

@@ -1,7 +1,8 @@
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_floating_header.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/common/member/contribute_type.dart';
import 'package:PiliPlus/models_new/space/space_archive/item.dart';
@@ -173,91 +174,78 @@ class _MemberVideoState extends State<MemberVideo>
response != null && response.isNotEmpty
? SliverMainAxisGroup(
slivers: [
SliverPersistentHeader(
pinned: false,
floating: true,
delegate: CustomSliverPersistentHeaderDelegate(
extent: 40,
bgColor: theme.colorScheme.surface,
child: SizedBox(
height: 40,
child: Row(
children: [
const SizedBox(width: 8),
Padding(
padding: const EdgeInsets.only(left: 6),
child: Obx(
() {
final count = _controller.count.value;
return Text(
count != -1 ? '$count视频' : '',
style: const TextStyle(fontSize: 13),
);
},
),
),
Obx(
() {
final episodicButton =
_controller.episodicButton.value;
return episodicButton.uri?.isNotEmpty == true
? Container(
height: 35,
padding: EdgeInsets.only(
left: _controller.count.value != -1
? 6
: 0,
SliverFloatingHeaderWidget(
backgroundColor: theme.colorScheme.surface,
child: Padding(
padding: const EdgeInsets.fromLTRB(14, 2.5, 8, 2.5),
child: Row(
children: [
Obx(
() {
final count = _controller.count.value;
return Text(
count != -1 ? '$count视频' : '',
style: const TextStyle(fontSize: 13),
);
},
),
Obx(
() {
final episodicButton =
_controller.episodicButton.value;
return episodicButton.uri?.isNotEmpty == true
? Padding(
padding: EdgeInsets.only(
left: _controller.count.value != -1
? 6
: 0,
),
child: TextButton.icon(
style: StyleString.buttonStyle,
onPressed: _controller.toViewPlayAll,
icon: Icon(
Icons.play_circle_outline_rounded,
size: 16,
color: theme.colorScheme.secondary,
),
child: TextButton.icon(
onPressed: _controller.toViewPlayAll,
icon: Icon(
Icons.play_circle_outline_rounded,
size: 16,
label: Text(
episodicButton.text ?? '播放全部',
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.secondary,
),
label: Text(
episodicButton.text ?? '播放全部',
style: TextStyle(
fontSize: 13,
color:
theme.colorScheme.secondary,
),
),
),
)
: const SizedBox.shrink();
},
),
)
: const SizedBox.shrink();
},
),
const Spacer(),
TextButton.icon(
style: StyleString.buttonStyle,
onPressed: _controller.queryBySort,
icon: Icon(
Icons.sort,
size: 16,
color: theme.colorScheme.secondary,
),
const Spacer(),
SizedBox(
height: 35,
child: TextButton.icon(
onPressed: _controller.queryBySort,
icon: Icon(
Icons.sort,
size: 16,
label: Obx(
() => Text(
widget.type == ContributeType.video
? _controller.order.value == 'pubdate'
? '最新发布'
: '最多播放'
: _controller.sort.value == 'desc'
? '默认'
: '倒序',
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.secondary,
),
label: Obx(
() => Text(
widget.type == ContributeType.video
? _controller.order.value == 'pubdate'
? '最新发布'
: '最多播放'
: _controller.sort.value == 'desc'
? '默认'
: '倒序',
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.secondary,
),
),
),
),
),
const SizedBox(width: 8),
],
),
),
],
),
),
),

View File

@@ -1,11 +1,12 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/skeleton/video_reply.dart';
import 'package:PiliPlus/common/widgets/custom_icon.dart';
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/flutter/selectable_text/selectable_text.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_floating_header.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/common/image_type.dart';
import 'package:PiliPlus/models/common/pgc_review_type.dart';
@@ -383,51 +384,43 @@ class _PgcReviewChildPageState extends State<PgcReviewChildPage>
);
}
Widget _buildHeader(ThemeData theme) => 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(
() {
final count = _controller.count.value;
return count == null
? const SizedBox.shrink()
: Text(
'${NumUtils.numFormat(count)}条点评',
style: const TextStyle(fontSize: 13),
);
},
Widget _buildHeader(ThemeData theme) => SliverFloatingHeaderWidget(
backgroundColor: theme.colorScheme.surface,
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 2.5, 6, 2.5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Obx(
() {
final count = _controller.count.value;
return count == null
? const SizedBox.shrink()
: Text(
'${NumUtils.numFormat(count)}条点评',
style: const TextStyle(fontSize: 13),
);
},
),
TextButton.icon(
style: StyleString.buttonStyle,
onPressed: _controller.queryBySort,
icon: Icon(
Icons.sort,
size: 16,
color: theme.colorScheme.secondary,
),
SizedBox(
height: 35,
child: TextButton.icon(
onPressed: _controller.queryBySort,
icon: Icon(
Icons.sort,
size: 16,
label: Obx(
() => Text(
_controller.sortType.value.label,
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.secondary,
),
label: Obx(
() => Text(
_controller.sortType.value.label,
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.secondary,
),
),
),
),
),
],
),
),
],
),
),
);

View File

@@ -1,9 +1,9 @@
import 'dart:math';
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_floating_header.dart';
import 'package:PiliPlus/common/widgets/video_card/video_card_h.dart';
import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart';
import 'package:PiliPlus/http/loading_state.dart';
@@ -201,17 +201,11 @@ class _PopularSeriesPageState extends State<PopularSeriesPage> with GridMixin {
],
);
}
final height = MediaQuery.textScalerOf(context).scale(27);
return SliverPersistentHeader(
floating: true,
delegate: CustomSliverPersistentHeaderDelegate(
extent: height,
child: Container(
height: height,
padding: const EdgeInsets.only(left: 14, bottom: 7),
child: child,
),
bgColor: colorScheme.surface,
return SliverFloatingHeaderWidget(
backgroundColor: colorScheme.surface,
child: Padding(
padding: const .only(left: 14, bottom: 7),
child: child,
),
);
}

View File

@@ -1,4 +1,4 @@
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_floating_header.dart';
import 'package:PiliPlus/models/search/result.dart';
import 'package:PiliPlus/pages/search_panel/article/controller.dart';
import 'package:PiliPlus/pages/search_panel/article/widgets/item.dart';
@@ -45,51 +45,45 @@ class _SearchArticlePanelState
@override
Widget buildHeader(ThemeData theme) {
return SliverPersistentHeader(
pinned: false,
floating: true,
delegate: CustomSliverPersistentHeaderDelegate(
extent: 40,
bgColor: theme.colorScheme.surface,
child: Container(
height: 40,
padding: const EdgeInsets.only(left: 25, right: 12),
child: Row(
children: [
Obx(
() => Text(
'排序: ${controller.articleOrderType.value.label}',
maxLines: 1,
style: TextStyle(color: theme.colorScheme.outline),
return SliverFloatingHeaderWidget(
backgroundColor: theme.colorScheme.surface,
child: Padding(
padding: const .fromLTRB(25, 0, 12, 4),
child: Row(
children: [
Obx(
() => Text(
'排序: ${controller.articleOrderType.value.label}',
maxLines: 1,
style: TextStyle(color: theme.colorScheme.outline),
),
),
const Spacer(),
Obx(
() => Text(
'分区: ${controller.articleZoneType!.value.label}',
maxLines: 1,
style: TextStyle(color: theme.colorScheme.outline),
),
),
const Spacer(),
SizedBox(
width: 32,
height: 32,
child: IconButton(
tooltip: '筛选',
style: const ButtonStyle(
padding: WidgetStatePropertyAll(EdgeInsets.zero),
),
onPressed: () => controller.onShowFilterDialog(context),
icon: Icon(
Icons.filter_list_outlined,
size: 18,
color: theme.colorScheme.primary,
),
),
const Spacer(),
Obx(
() => Text(
'分区: ${controller.articleZoneType!.value.label}',
maxLines: 1,
style: TextStyle(color: theme.colorScheme.outline),
),
),
const Spacer(),
SizedBox(
width: 32,
height: 32,
child: IconButton(
tooltip: '筛选',
style: const ButtonStyle(
padding: WidgetStatePropertyAll(EdgeInsets.zero),
),
onPressed: () => controller.onShowFilterDialog(context),
icon: Icon(
Icons.filter_list_outlined,
size: 18,
color: theme.colorScheme.primary,
),
),
),
],
),
),
],
),
),
);

View File

@@ -1,5 +1,5 @@
import 'package:PiliPlus/common/skeleton/msg_feed_top.dart';
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_floating_header.dart';
import 'package:PiliPlus/models/search/result.dart';
import 'package:PiliPlus/pages/search_panel/user/controller.dart';
import 'package:PiliPlus/pages/search_panel/user/widgets/item.dart';
@@ -46,51 +46,45 @@ class _SearchUserPanelState
@override
Widget buildHeader(ThemeData theme) {
return SliverPersistentHeader(
pinned: false,
floating: true,
delegate: CustomSliverPersistentHeaderDelegate(
extent: 40,
bgColor: theme.colorScheme.surface,
child: Container(
height: 40,
padding: const EdgeInsets.only(left: 25, right: 12),
child: Row(
children: [
Obx(
() => Text(
'排序: ${controller.userOrderType!.value.label}',
maxLines: 1,
style: TextStyle(color: theme.colorScheme.outline),
return SliverFloatingHeaderWidget(
backgroundColor: theme.colorScheme.surface,
child: Padding(
padding: const .fromLTRB(25, 0, 12, 4),
child: Row(
children: [
Obx(
() => Text(
'排序: ${controller.userOrderType!.value.label}',
maxLines: 1,
style: TextStyle(color: theme.colorScheme.outline),
),
),
const Spacer(),
Obx(
() => Text(
'用户类型: ${controller.userType!.value.label}',
maxLines: 1,
style: TextStyle(color: theme.colorScheme.outline),
),
),
const Spacer(),
SizedBox(
width: 32,
height: 32,
child: IconButton(
tooltip: '筛选',
style: const ButtonStyle(
padding: WidgetStatePropertyAll(EdgeInsets.zero),
),
onPressed: () => controller.onShowFilterDialog(context),
icon: Icon(
Icons.filter_list_outlined,
size: 18,
color: theme.colorScheme.primary,
),
),
const Spacer(),
Obx(
() => Text(
'用户类型: ${controller.userType!.value.label}',
maxLines: 1,
style: TextStyle(color: theme.colorScheme.outline),
),
),
const Spacer(),
SizedBox(
width: 32,
height: 32,
child: IconButton(
tooltip: '筛选',
style: const ButtonStyle(
padding: WidgetStatePropertyAll(EdgeInsets.zero),
),
onPressed: () => controller.onShowFilterDialog(context),
icon: Icon(
Icons.filter_list_outlined,
size: 18,
color: theme.colorScheme.primary,
),
),
),
],
),
),
],
),
),
);

View File

@@ -1,4 +1,4 @@
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_floating_header.dart';
import 'package:PiliPlus/common/widgets/video_card/video_card_h.dart';
import 'package:PiliPlus/models/common/search/video_search_type.dart';
import 'package:PiliPlus/models/search/result.dart';
@@ -47,61 +47,55 @@ class _SearchVideoPanelState
@override
Widget buildHeader(ThemeData theme) {
return SliverPersistentHeader(
pinned: false,
floating: true,
delegate: CustomSliverPersistentHeaderDelegate(
extent: 34,
bgColor: theme.colorScheme.surface,
child: Container(
height: 34,
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Wrap(
children: [
for (final e in ArchiveFilterType.values)
Obx(
() => SearchText(
fontSize: 13,
text: e.desc,
bgColor: Colors.transparent,
textColor: controller.selectedType.value == e
? theme.colorScheme.primary
: theme.colorScheme.outline,
onTap: (_) => controller
..order = e.name
..selectedType.value = e
..onSortSearch(getBack: false),
),
return SliverFloatingHeaderWidget(
backgroundColor: theme.colorScheme.surface,
child: Padding(
padding: const .fromLTRB(12, 0, 12, 4),
child: Row(
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Wrap(
children: [
for (final e in ArchiveFilterType.values)
Obx(
() => SearchText(
fontSize: 13,
text: e.desc,
bgColor: Colors.transparent,
textColor: controller.selectedType.value == e
? theme.colorScheme.primary
: theme.colorScheme.outline,
onTap: (_) => controller
..order = e.name
..selectedType.value = e
..onSortSearch(getBack: false),
),
],
),
),
],
),
),
const VerticalDivider(indent: 7, endIndent: 8),
const SizedBox(width: 3),
SizedBox(
width: 32,
height: 32,
child: IconButton(
tooltip: '筛选',
style: const ButtonStyle(
padding: WidgetStatePropertyAll(EdgeInsets.zero),
),
onPressed: () => controller.onShowFilterDialog(context),
icon: Icon(
Icons.filter_list_outlined,
size: 18,
color: theme.colorScheme.primary,
),
),
const VerticalDivider(indent: 7, endIndent: 8),
const SizedBox(width: 3),
SizedBox(
width: 32,
height: 32,
child: IconButton(
tooltip: '筛选',
style: const ButtonStyle(
padding: WidgetStatePropertyAll(EdgeInsets.zero),
),
onPressed: () => controller.onShowFilterDialog(context),
icon: Icon(
Icons.filter_list_outlined,
size: 18,
color: theme.colorScheme.primary,
),
),
],
),
),
],
),
),
);

View File

@@ -1,3 +1,4 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/skeleton/video_card_h.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
@@ -136,24 +137,22 @@ class _HorizontalMemberPageState extends State<HorizontalMemberPage> {
);
},
),
SizedBox(
height: 35,
child: TextButton.icon(
onPressed: () => _controller
..lastAid = widget.videoDetailController.aid.toString()
..queryBySort(),
icon: Icon(
Icons.sort,
size: 16,
color: theme.colorScheme.secondary,
),
label: Obx(
() => Text(
_controller.order.value == 'pubdate' ? '最新发布' : '最多播放',
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.secondary,
),
TextButton.icon(
style: StyleString.buttonStyle,
onPressed: () => _controller
..lastAid = widget.videoDetailController.aid.toString()
..queryBySort(),
icon: Icon(
Icons.sort,
size: 16,
color: theme.colorScheme.secondary,
),
label: Obx(
() => Text(
_controller.order.value == 'pubdate' ? '最新发布' : '最多播放',
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.secondary,
),
),
),

View File

@@ -1,7 +1,8 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/skeleton/video_reply.dart';
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_floating_header.dart';
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
show ReplyInfo;
import 'package:PiliPlus/http/loading_state.dart';
@@ -83,47 +84,39 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
: _videoReplyController.scrollController,
physics: const AlwaysScrollableScrollPhysics(),
key: const PageStorageKey(_VideoReplyPanelState),
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),
),
slivers: [
SliverFloatingHeaderWidget(
backgroundColor: theme.colorScheme.surface,
child: Padding(
padding: const .fromLTRB(12, 2.5, 6, 2.5),
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,
),
TextButton.icon(
style: StyleString.buttonStyle,
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,
),
label: Obx(
() => Text(
_videoReplyController.sortType.value.label,
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.secondary,
),
),
),
),
),
],
),
),
],
),
),
),

View File

@@ -1,8 +1,9 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/skeleton/video_reply.dart';
import 'package:PiliPlus/common/widgets/colored_box_transition.dart';
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_pinned_header.dart';
import 'package:PiliPlus/common/widgets/view_safe_area.dart';
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
show ReplyInfo, Mode;
@@ -240,52 +241,43 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel>
}
Widget _sortWidget(ThemeData theme) {
return SliverPersistentHeader(
pinned: 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(
() {
final count = _controller.count.value;
return count != -1
? Text(
'相关回复共${NumUtils.numFormat(count)}',
style: const TextStyle(fontSize: 13),
)
: const SizedBox.shrink();
},
return SliverPinnedHeader(
backgroundColor: theme.colorScheme.surface,
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 2.5, 6, 2.5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Obx(
() {
final count = _controller.count.value;
return count != -1
? Text(
'相关回复共${NumUtils.numFormat(count)}',
style: const TextStyle(fontSize: 13),
)
: const SizedBox.shrink();
},
),
TextButton.icon(
style: StyleString.buttonStyle,
onPressed: _controller.queryBySort,
icon: Icon(
Icons.sort,
size: 16,
color: theme.colorScheme.secondary,
),
SizedBox(
height: 35,
child: TextButton.icon(
onPressed: _controller.queryBySort,
icon: Icon(
Icons.sort,
size: 16,
label: Obx(
() => Text(
_controller.mode.value == Mode.MAIN_LIST_HOT ? '按热度' : '按时间',
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.secondary,
),
label: Obx(
() => Text(
_controller.mode.value == Mode.MAIN_LIST_HOT
? '按热度'
: '按时间',
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.secondary,
),
),
),
),
),
],
),
),
],
),
),
);

View File

@@ -9,6 +9,7 @@ import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/image_viewer/hero_dialog_route.dart';
import 'package:PiliPlus/common/widgets/keep_alive_wrapper.dart';
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_pinned_dynamic_header.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/main.dart';
import 'package:PiliPlus/models/common/episode_panel_type.dart';
@@ -648,14 +649,10 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
? animHeight
: videoDetailController.videoHeight;
return [
SliverAppBar(
elevation: 0,
scrolledUnderElevation: 0,
primary: false,
automaticallyImplyLeading: false,
pinned: true,
expandedHeight: height,
flexibleSpace: Stack(
SliverPinnedDynamicHeader(
minExtent: kToolbarHeight,
maxExtent: height,
child: Stack(
clipBehavior: Clip.none,
children: [
SizedBox(

View File

@@ -1,8 +1,8 @@
import 'dart:async';
import 'dart:io' show File;
import 'package:PiliPlus/common/widgets/chat_list_view.dart';
import 'package:PiliPlus/common/widgets/dialog/report.dart';
import 'package:PiliPlus/common/widgets/flutter/chat_list_view.dart';
import 'package:PiliPlus/common/widgets/flutter/text_field/text_field.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';