如何使SliverPersistentHeader达到“过度增长"状态. [英] How to get the SliverPersistentHeader to "overgrow"

查看:82
本文介绍了如何使SliverPersistentHeader达到“过度增长"状态.的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我的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. 创建一个包含CustomScrollView和标题小部件(覆盖在滚动视图顶部)的Stack,为CustomScrollView提供一个ScrollController并将控制器传递给标题小部件以调整其大小
  2. 使用ScrollController,将其传递到CustomScrollView,然后使用控制器的值来调整SliverPersistentHeadermaxExtent(这是
  1. Create a Stack that contains the CustomScrollView and a header widget (overlaid on top of the scroll view), provide a ScrollController to the CustomScrollView and pass the controller to the header widget to adjust its size
  2. Use a ScrollController, pass it to the CustomScrollView and use the value of the controller to adjust the maxExtent of the SliverPersistentHeader (this is what Eugene recommended).
  3. Write my own Sliver to do exactly what I want.

我在解决方案1和解决方案上遇到了问题2:

I ran into problems with solution 1 & 2:

  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达到“过度增长"状态.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆