Flutter实现粘性头和项目捕捉效果

22
在过去的几天里,我一直在阅读Flutter框架文档,特别是Sliver部分,但我不太确定从哪里开始。 我正在尝试实现粘性标题和快照效果。RenderSliverList可能是一个很好的开始吗?我需要重新布局吗?我需要进行额外的绘制吗?如果需要,在哪里进行呢?
任何关于从哪里开始的帮助都将是巨大的帮助,先感谢您!
编辑:我现在认为我理解了布局部分,但我只是找不到绘画应该发生的位置。
编辑2:为了澄清,这是期望的“粘性标头效果”:如何在RecyclerView中制作粘性标头?(无需外部库)这是“快照”效果:https://rubensousa.github.io/2016/08/recyclerviewsnap
6个回答

27

对于"粘性标题效果",我自己也遇到了这个问题,所以我创建了这个包来管理带有slivers的粘性标题: https://github.com/letsar/flutter_sticky_header

Flutter Sticky Headers

要使用它,您需要在CustomScrollView中为每个部分创建一个SliverStickyHeader
一个部分可以像这样编写:
new SliverStickyHeader(
  header: new Container(
    height: 60.0,
    color: Colors.lightBlue,
    padding: EdgeInsets.symmetric(horizontal: 16.0),
    alignment: Alignment.centerLeft,
    child: new Text(
      'Header #0',
      style: const TextStyle(color: Colors.white),
    ),
  ),
  sliver: new SliverList(
    delegate: new SliverChildBuilderDelegate(
      (context, i) => new ListTile(
            leading: new CircleAvatar(
              child: new Text('0'),
            ),
            title: new Text('List tile #$i'),
          ),
      childCount: 4,
    ),
  ),
);

如果您需要,以上演示的完整源代码可以在这里获得:https://github.com/letsar/flutter_sticky_header/blob/master/example/lib/main.dart

希望这对您有所帮助。


#Romain Rastel 如果我只想将一个标题固定在顶部,例如 #header2,我该怎么做呢?使用这个包怎样实现? - satish

18

非常简单:

使用 CustomScrollView,并将 SliverListSliverAppBar 作为其子项。如果需要,您可以用 SliverGrid 替换 SliverList

然后,根据您想要实现的效果,在SliverAppBar 上设置几个属性:

  • snap
  • expandedHeight(+ flexibleSpace)
  • floating
  • pinned

最终,您可能会得到类似于:

new CustomScrollView(
    slivers: <Widget>[
        new SliverAppBar(
            title: new Text("Title"),
            snap: true,
            floating: true,
        ),
        new SliverFixedExtentList(
            itemExtent: 50.0,
            delegate: new SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                    return new Container(
                        alignment: Alignment.center,
                        color: Colors.lightBlue[100 * (index % 9)],
                        child: new Text('list item $index'),
                    );
                },
            ),
        ),
    ],
)
更好的是,您可以在单个CustomScrollView中连接不同的滚动行为。这意味着您可以通过将SliverGrid作为scrollView的子项添加,潜在地拥有一个网格后面跟着一个列表。
我知道,我知道,Flutter真是太棒了。

谢谢你的回答,确实Flutter很棒!不幸的是,我正在寻找像这样的效果:https://dev59.com/kVwY5IYBdhLWcg3wJk8q。但我喜欢连接不同的sliver的概念,你认为只使用标准的sliver是否可能实现那种效果? - Norbert
哦,我明白了。在这种情况下,您应该将您的问题分成两个部分:一个是关于固定标题的,另一个是关于捕捉效果的。 - Rémi Rousselet
简而言之,您的“粘性效果”可能没有内置解决方案。但是您可以查看SliverPersistentHeader代码以实现自定义行为。 - Rémi Rousselet
关于快照效果,可以通过滚动控制器在滚动元素上实现。添加监听器,在滚动结束时使用 animateTo - Rémi Rousselet
谢谢,那应该可以作为快照。关于“粘性效果”,是的,我也这么想,我已经阅读了最近几天的文档,但我不太确定我该如何处理它。 - Norbert
你好,Rémi Rousselet,你知道我如何在不提供collapsedHeight和expandedHeight的情况下使用_SliverAppBarDelegate吗?我只想让stickyheader像在Android中那样包裹内容。如果你知道,请看一下我的问题答案并评论。 - Simon

11

我使用以下代码成功在Flutter中实现iOS应用程序的stickyheader效果 - 感谢这里编写的代码提供了灵感(https://github.com/flutter/flutter/blob/master/examples/flutter_gallery/lib/demo/animation/home.dart#L112):

class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SliverAppBarDelegate({
    @required this.collapsedHeight,
    @required this.expandedHeight,}
      );

  final double expandedHeight;
  final double collapsedHeight;

  @override double get minExtent => collapsedHeight;
  @override double get maxExtent => math.max(expandedHeight, minExtent);

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    return new Container(color: Colors.red,
        child: new Padding(
          padding: const EdgeInsets.only(
              left: 8.0, top: 8.0, bottom: 8.0, right: 8.0),
          child: new Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              new Text("Time"), new Text("Price"), new Text("Hotness")
            ],
          ),
        )
    );
  }

  @override
  bool shouldRebuild(@checked _SliverAppBarDelegate oldDelegate) {
    return expandedHeight != oldDelegate.expandedHeight
        || collapsedHeight != oldDelegate.collapsedHeight;
  }
}

为了使其固定,将_SliverAppBarDelegate添加到silvers小部件列表中:

 new SliverPersistentHeader(delegate: new _SliverAppBarDelegate(collapsedHeight: 36.0, expandedHeight: 36.0), pinned: true, ),

我不太确定如何使_SliverAppBarDelegate自动包裹内容,我必须为它提供36个逻辑像素的大小才能使其工作。如果有人知道如何让它自动包裹内容,请在下面的答案中留言。

输入图片描述


页面底部是否可以有类似于红色页眉的内容,作为页脚? - Wout
我认为这是可能的。您需要在构建方法中将SliverPersistentHeader放置在最后,并使用Expanded小部件包装SliverPersistentHeader之前的项目,以便将其推到底部。只是一个想法,也许可以尝试一下? - Simon
不,那对我没用。我现在用一个堆栈来解决它,只需将页脚作为第二层的MainAxisAlignment.end列放置即可。为了确保列表末尾显示,我必须将SliverList放在具有底部插图的SliverPadding中。它可以工作,但感觉有点过度设计。一个小缺点是滚动结束动画没有出现在正确的位置(它在页脚层下面)。 - Wout

4

我解决了这个问题,建议使用sticky_and_expandable_list.

enter image description here

特点

  • 支持构建可展开/折叠的ListView, 可以创建粘性的Section头部。
  • 可与CustomScrollView、SliverAppBar一起使用。
  • 可以监听当前粘性header的滚动偏移、当前header索引和切换header索引。
  • 只使用一个列表控件,因此支持大数据和小内存使用。同时支持更多Section自定义,可以通过sectionBuilder返回新的section widget,来定制背景、展开/折叠动画、section布局等等。
  • 支持添加分割线。

0

0
根据文档,您可以将StickyHeaderStickyHeaderBuilder放置在任何可滚动内容中。
文档page仅提供了如何在ListView中应用StickyHeader的示例,那么其他小部件如SingleChildScrollViewCustomScrollView呢?
在此示例中,我将提供一个非常简单的SingleChildScrollViewStickyHeader,当然您可以将其放置在任何想要的SingleChildScrollView中。
return Container(
    child: SingleChildScrollView(
      scrollDirection: Axis.vertical,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text("My title"),
          StickyHeader(
            header: Container(
                  child: // Put here whatever widget you like as the sticky widget
                ),
            content: Column(
              children: [
                Container(
                  child: // Put here whatever widget you like as scrolling content (Column, Text, ListView, etc...)
                ),
              ],
            ),
          ),
        ],
      ),
    ),
  );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接