opt persist header

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-02-03 17:25:33 +08:00
parent 2596859778
commit 50561b8dc1
5 changed files with 118 additions and 75 deletions

View File

@@ -1,18 +1,15 @@
import 'dart:io' show Platform;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart' show RenderProxyBox;
class CustomSliverPersistentHeaderDelegate class CustomSliverPersistentHeaderDelegate
extends SliverPersistentHeaderDelegate { extends SliverPersistentHeaderDelegate {
const CustomSliverPersistentHeaderDelegate({ const CustomSliverPersistentHeaderDelegate({
required this.child, required this.child,
required this.bgColor, this.bgColor,
double extent = 45, this.extent = 45,
this.needRebuild = false, this.needRebuild = false,
}) : _minExtent = extent, });
_maxExtent = extent; final double extent;
final double _minExtent;
final double _maxExtent;
final Widget child; final Widget child;
final Color? bgColor; final Color? bgColor;
final bool needRebuild; final bool needRebuild;
@@ -26,31 +23,16 @@ class CustomSliverPersistentHeaderDelegate
//创建child子组件 //创建child子组件
//shrinkOffsetchild偏移值minExtent~maxExtent //shrinkOffsetchild偏移值minExtent~maxExtent
//overlapsContentSliverPersistentHeader覆盖其他子组件返回true否则返回false //overlapsContentSliverPersistentHeader覆盖其他子组件返回true否则返回false
return bgColor != null return _DecoratedBox(color: bgColor, child: child);
? DecoratedBox(
decoration: BoxDecoration(
color: bgColor,
boxShadow: Platform.isIOS
? null
: [
BoxShadow(
color: bgColor!,
offset: const Offset(0, -1),
),
],
),
child: child,
)
: child;
} }
//SliverPersistentHeader最大高度 //SliverPersistentHeader最大高度
@override @override
double get maxExtent => _maxExtent; double get maxExtent => extent;
//SliverPersistentHeader最小高度 //SliverPersistentHeader最小高度
@override @override
double get minExtent => _minExtent; double get minExtent => extent;
@override @override
bool shouldRebuild(CustomSliverPersistentHeaderDelegate oldDelegate) { bool shouldRebuild(CustomSliverPersistentHeaderDelegate oldDelegate) {
@@ -58,3 +40,62 @@ class CustomSliverPersistentHeaderDelegate
(needRebuild && oldDelegate.child != child); (needRebuild && oldDelegate.child != child);
} }
} }
class _DecoratedBox extends SingleChildRenderObjectWidget {
const _DecoratedBox({
this.color,
super.child,
});
final Color? color;
@override
RenderObject createRenderObject(BuildContext context) {
return _RenderDecoratedBox(color: color);
}
@override
void updateRenderObject(
BuildContext context,
_RenderDecoratedBox renderObject,
) {
renderObject.color = color;
}
}
class _RenderDecoratedBox extends RenderProxyBox {
_RenderDecoratedBox({
Color? color,
}) : _color = color;
Color? _color;
Color? get color => _color;
set color(Color? value) {
if (_color == value) return;
_color = value;
markNeedsPaint();
}
@override
void paint(PaintingContext context, Offset offset) {
if (_color case final color?) {
final size = this.size;
context.canvas.drawRect(
Rect.fromLTWH(
offset.dx,
offset.dy - 2,
size.width,
size.height + 2,
),
Paint()..color = color,
);
}
super.paint(context, offset);
}
@override
bool hitTestSelf(Offset position) => true;
@override
bool get isRepaintBoundary => true;
}

View File

@@ -81,13 +81,14 @@ class _DynTopicPageState extends State<DynTopicPage> with DynMixin {
return SliverPersistentHeader( return SliverPersistentHeader(
pinned: true, pinned: true,
delegate: CustomSliverPersistentHeaderDelegate( delegate: CustomSliverPersistentHeaderDelegate(
extent: 30, extent: 36,
needRebuild: true, needRebuild: true,
bgColor: theme.colorScheme.surface, bgColor: theme.colorScheme.surface,
child: Container( child: Container(
height: 30, height: 36,
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: 12 + padding.left, left: 12 + padding.left,
top: 6,
bottom: 6, bottom: 6,
), ),
child: Builder( child: Builder(
@@ -101,16 +102,14 @@ class _DynTopicPageState extends State<DynTopicPage> with DynMixin {
minHeight: 24, minHeight: 24,
), ),
tapTargetSize: MaterialTapTargetSize.shrinkWrap, tapTargetSize: MaterialTapTargetSize.shrinkWrap,
borderRadius: const BorderRadius.all( borderRadius: const .all(.circular(25)),
Radius.circular(25),
),
onPressed: (index) { onPressed: (index) {
_controller.onSort(allSortBy[index].sortBy!); _controller.onSort(allSortBy[index].sortBy!);
(context as Element).markNeedsBuild(); (context as Element).markNeedsBuild();
}, },
isSelected: allSortBy.map((e) { isSelected: allSortBy
return e.sortBy == _controller.sortBy; .map((e) => e.sortBy == _controller.sortBy)
}).toList(), .toList(),
children: allSortBy.map((e) { children: allSortBy.map((e) {
return Text( return Text(
e.sortName!, e.sortName!,

View File

@@ -107,7 +107,6 @@ class _LiveDmBlockPageState extends State<LiveDmBlockPage> {
delegate: CustomSliverPersistentHeaderDelegate( delegate: CustomSliverPersistentHeaderDelegate(
extent: 48, extent: 48,
child: tabBar, child: tabBar,
bgColor: null,
), ),
), ),
), ),

View File

@@ -160,7 +160,6 @@ class _MemberFavoriteState extends State<MemberFavorite>
}, },
), ),
), ),
bgColor: null,
), ),
), ),
Obx(() { Obx(() {

View File

@@ -6,7 +6,6 @@ import 'dart:math';
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/button/icon_button.dart'; import 'package:PiliPlus/common/widgets/button/icon_button.dart';
import 'package:PiliPlus/common/widgets/custom_icon.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/report.dart'; import 'package:PiliPlus/common/widgets/dialog/report.dart';
import 'package:PiliPlus/common/widgets/marquee.dart'; import 'package:PiliPlus/common/widgets/marquee.dart';
import 'package:PiliPlus/http/danmaku.dart'; import 'package:PiliPlus/http/danmaku.dart';
@@ -1580,49 +1579,55 @@ class HeaderControlState extends State<HeaderControl>
if (ctr == null) return; if (ctr == null) return;
showBottomSheet((context, setState) { showBottomSheet((context, setState) {
final theme = Theme.of(context); final theme = Theme.of(context);
return Padding( return Container(
padding: const EdgeInsets.all(12), margin: const EdgeInsets.all(12),
child: Material( decoration: BoxDecoration(
clipBehavior: Clip.hardEdge,
color: theme.colorScheme.surface, color: theme.colorScheme.surface,
borderRadius: const BorderRadius.all(Radius.circular(12)), borderRadius: const BorderRadius.all(Radius.circular(12)),
child: CustomScrollView( ),
slivers: [ child: Column(
SliverPersistentHeader( children: [
pinned: true, Container(
delegate: CustomSliverPersistentHeaderDelegate( height: 45,
child: Container( padding: const EdgeInsets.symmetric(horizontal: 14),
height: 45, decoration: BoxDecoration(
padding: const EdgeInsets.symmetric(horizontal: 14), border: Border(
decoration: BoxDecoration( bottom: BorderSide(
border: Border( color: theme.colorScheme.outline.withValues(alpha: 0.1),
bottom: BorderSide(
color: theme.colorScheme.outline.withValues(
alpha: 0.1,
),
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('弹幕列表'),
iconButton(
onPressed: () => setState(() {}),
icon: const Icon(Icons.refresh),
),
],
),
), ),
bgColor: theme.colorScheme.surface,
), ),
), ),
?_buildDanmakuList(ctr.staticDanmaku.nonNulls.toList()), child: Row(
?_buildDanmakuList(ctr.scrollDanmaku.expand((e) => e).toList()), mainAxisAlignment: MainAxisAlignment.spaceBetween,
?_buildDanmakuList(ctr.specialDanmaku.toList()), children: [
const SliverToBoxAdapter(child: SizedBox(height: 12)), const Text('弹幕列表'),
], iconButton(
), onPressed: () => setState(() {}),
icon: const Icon(Icons.refresh),
),
],
),
),
Expanded(
child: Material(
type: .transparency,
clipBehavior: .hardEdge,
borderRadius: const BorderRadius.vertical(
bottom: Radius.circular(12),
),
child: CustomScrollView(
slivers: [
?_buildDanmakuList(ctr.staticDanmaku.nonNulls.toList()),
?_buildDanmakuList(
ctr.scrollDanmaku.expand((e) => e).toList(),
),
?_buildDanmakuList(ctr.specialDanmaku.toList()),
const SliverToBoxAdapter(child: SizedBox(height: 12)),
],
),
),
),
],
), ),
); );
}); });