我在我的CustomScrollView中使用SliverPersistentHeader来拥有一个持久的标头,该标头在用户滚动时会缩小和增长,但是当达到最大尺寸时,它会变得有点僵硬,因为它不会过度增长"
I'm using a SliverPersistentHeader in my CustomScrollView to have a persistent header that shrinks and grows when the user scrolls, but when it reaches its maximum size it feels a bit stiff since it doesn't "overgrow".
以下是我想要的行为(来自Spotify应用)的视频以及我的行为:
Here is a video of the behaviour I want (from the Spotify app) and the behaviour I have:
.
推荐答案在寻找此问题的解决方案时,我遇到了三种不同的解决方法:
While looking for a solution for this problem, I came across three different ways to solve it:
我在解决方案1和解决方案上遇到了问题2:
I ran into problems with solution 1 & 2:
这就是为什么我选择解决方案3的原因.我确定实现它的方式并不是最好的,但是它完全可以按照我的意愿进行工作:
That's why I opted for solution 3. I'm sure the way I implemented it, is not the best, but it works exactly as I want:
import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'dart:math' as math; /// The delegate that is provided to [ElSliverPersistentHeader]. abstract class ElSliverPersistentHeaderDelegate { double get maxExtent; double get minExtent; /// This acts exactly like `SliverPersistentHeaderDelegate.build()` but with /// the difference that `shrinkOffset` might be negative, in which case, /// this widget exceeds `maxExtent`. Widget build(BuildContext context, double shrinkOffset); } /// Pretty much the same as `SliverPersistentHeader` but when the user /// continues to drag down, the header grows in size, exceeding `maxExtent`. class ElSliverPersistentHeader extends SingleChildRenderObjectWidget { final ElSliverPersistentHeaderDelegate delegate; ElSliverPersistentHeader({ Key key, ElSliverPersistentHeaderDelegate delegate, }) : this.delegate = delegate, super( key: key, child: _ElSliverPersistentHeaderDelegateWrapper(delegate: delegate)); @override _ElPersistentHeaderRenderSliver createRenderObject(BuildContext context) { return _ElPersistentHeaderRenderSliver( delegate.maxExtent, delegate.minExtent); } } class _ElSliverPersistentHeaderDelegateWrapper extends StatelessWidget { final ElSliverPersistentHeaderDelegate delegate; _ElSliverPersistentHeaderDelegateWrapper({Key key, this.delegate}) : super(key: key); @override Widget build(BuildContext context) => LayoutBuilder(builder: (context, constraints) { final height = constraints.maxHeight; return delegate.build(context, delegate.maxExtent - height); }); } class _ElPersistentHeaderRenderSliver extends RenderSliver with RenderObjectWithChildMixin<RenderBox> { final double maxExtent; final double minExtent; _ElPersistentHeaderRenderSliver(this.maxExtent, this.minExtent); @override bool hitTestChildren(HitTestResult result, {@required double mainAxisPosition, @required double crossAxisPosition}) { if (child != null) { return child.hitTest(result, position: Offset(crossAxisPosition, mainAxisPosition)); } return false; } @override void performLayout() { /// The amount of scroll that extends the theoretical limit. /// I.e.: when the user drags down the list, although it already hit the /// top. /// /// This seems to be a bit of a hack, but I haven't found a way to get this /// information in another way. final overScroll = constraints.viewportMainAxisExtent - constraints.remainingPaintExtent; /// The actual Size of the widget is the [maxExtent] minus the amount the /// user scrolled, but capped at the [minExtent] (we don't want the widget /// to become smaller than that). /// Additionally, we add the [overScroll] here, since if there *is* /// "over scroll", we want the widget to grow in size and exceed /// [maxExtent]. final actualSize = math.max(maxExtent - constraints.scrollOffset + overScroll, minExtent); /// Now layout the child with the [actualSize] as `maxExtent`. child.layout(constraints.asBoxConstraints(maxExtent: actualSize)); /// We "clip" the `paintExtent` to the `maxExtent`, otherwise the list /// below stops moving when reaching the border. /// /// Tbh, I'm not entirely sure why that is. final paintExtent = math.min(actualSize, maxExtent); /// For the layout to work properly (i.e.: the following slivers to /// scroll behind this sliver), the `layoutExtent` must not be capped /// at [minExtent], otherwise the next sliver will "stop" scrolling when /// [minExtent] is reached, final layoutExtent = math.max(maxExtent - constraints.scrollOffset, 0.0); geometry = SliverGeometry( scrollExtent: maxExtent, paintExtent: paintExtent, layoutExtent: layoutExtent, maxPaintExtent: maxExtent, ); } @override void paint(PaintingContext context, Offset offset) { if (child != null) { /// This sliver is always displayed at the top. context.paintChild(child, Offset(0.0, 0.0)); } } }更多推荐
如何使SliverPersistentHeader达到“过度增长"状态.
发布评论