展开图块时如何滚动ExpansionTile / Listview?

时间:2019-02-22 14:37:46

标签: dart flutter flutter-layout

我们在ExpansionTile中有一堆ListView(到目前为止还不是太疯狂了,对吧?)。我们的用户希望我们在打开ExpansionTile时“自动滚动”列表,因此新的展开图块现在位于屏幕的顶部(或尽可能顶部),因此他们没有手动滚动以查看我们刚刚打开的内容。

我认为我们在ScrollController上需要一个ListView之类的东西-并注册一个onExpansionChanged ..来做.. 。这对我来说还不算什么。 :)

任何有关如何执行此操作的指针或帮助将不胜感激!

TIA!

3 个答案:

答案 0 :(得分:3)

基于this的答案,您可以通过从其控制器获取ListView的当前滚动偏移量来计算滚动所需的空间:

_scrollController.offset

并将其添加到通过其键指定的当前ExpansionTile Y位置:

RenderBox _box = _ExpansiontileKey.currentContext.findRenderObject();
yPosition = _box.localToGlobal(Offset.zero).dy;

此位置考虑状态栏和AppBar的高度,因此可以通过以下方式计算滚动点:

scrollPoint = _scrollController.offset + yPosition - MediaQueryData.fromWindow(window).padding.top - 56;

MediaQueryData.fromWindow(window).padding.top将为您提供状态栏的高度,并从constats.dart中获取AppBar的高度:

/// The height of the toolbar component of the [AppBar].
const double kToolbarHeight = 56.0;

通过检查scrollPoint是否低于滚动的最大范围,可以避免对最后一项的错误计算:

if(_scrollPoint <= _scrollController.position.maxScrollExtent)
  _scrollController.animateTo(_scrollPoint, duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
else
  _scrollController.animateTo(_scrollController.position.maxScrollExtent,
    duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn
  );

通知,ExpansionTiles自己的扩展动画需要200毫秒,如其定义中所示: const Duration _kExpand = Duration(milliseconds: 200);因此,最好等扩展动画完成后再开始滚动,以便获取更新后的值。

一种方法是使用Future.delayed(duration)延迟通话:

Future.delayed(Duration(milliseconds: 250)).then((value){
  RenderBox _box = _ExpansiontileKey.currentContext.findRenderObject();
  yPosition = _box.localToGlobal(Offset.zero).dy;
  scrollPoint = _scrollController.offset + yPosition - 
    MediaQueryData.fromWindow(window).padding.top - 56;
    if(_scrollPoint <= _scrollController.position.maxScrollExtent)
      _scrollController.animateTo(_scrollPoint, duration: Duration(milliseconds: 
        500), curve: Curves.fastOutSlowIn);
    else
      _scrollController.animateTo(_scrollController.position.maxScrollExtent, 
        duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
  });

除此之外,您可以制作自己的ExpansionTile,并为其提供所需的扩展时间。

答案 1 :(得分:2)

如果我对您的要求是正确的,基本上是一种自动向上滚动ExpansionTile的方法,因此用户不必手动操作即可查看其内容,对吗?

然后,您实际上可以在ScrollController中使用ListView并利用onExpansionChanged的{​​{1}}回调来相应地向上/向下滚动到大小瓷砖的位置与其当前位置的关系。

但是现在您想知道: 我怎么知道要滚动多少?

为此,您要么通过赋予一些约束(例如,构建ExpansionTile来预先知道构建窗口小部件时高度是多少,或者在这种特定情况下,您不知道确切有多少? Container(heigh: 100.0))高度将占用的像素。因此,我们可以使用ExpansionTile,然后通过在运行时检查其GlobalKey来检查其渲染高度,以确切地知道多少折叠时需要的像素,然后乘以它的索引。

RenderBox

好吧,现在我们有一个 ExpansionTile _buildExpansionTile(int index) { final GlobalKey expansionTileKey = GlobalKey(); double previousOffset; return ExpansionTile( key: expansionTileKey, onExpansionChanged: (isExpanded) { if (isExpanded) previousOffset = _scrollController.offset; _scrollToSelectedContent(isExpanded, previousOffset, index, expansionTileKey); }, title: Text('My expansion tile $index'), children: _buildExpansionTileChildren(), ); } ,它将在其生成器中将此ListView_buildExpansionTile一起使用。展开图块时,我们还将保存GlobalKey的滚动偏移量。但是,实际上我们如何向上滚动以显示其内容?让我们看看ListView方法。

_scrollToSelectedContent

因此,我们通过其键访问渲染对象,然后使用 void _scrollToSelectedContent(bool isExpanded, double previousOffset, int index, GlobalKey myKey) { final keyContext = myKey.currentContext; if (keyContext != null) { // make sure that your widget is visible final box = keyContext.findRenderObject() as RenderBox; _scrollController.animateTo(isExpanded ? (box.size.height * index) : previousOffset, duration: Duration(milliseconds: 500), curve: Curves.linear); } } 滚动控制器在展开时向上滚动,在折叠时通过将其高度乘以其索引向下滚动回到其初始位置。您不必回滚,这只是我个人对本示例的偏爱。这将导致如下结果:

example

这里有ListView

中的完整示例
StatefulWidget

答案 2 :(得分:0)

如果您仍然遇到此问题,我发现了另一个使用Scrollable Widget的解决方案。我们必须将ExpansionTile的当前上下文传递给Scrollable类的sureVisible方法,然后它才能完成工作。您可以查看我的文章How to scroll an ExpansionTile when it is expanded?,以了解更多信息。