我们在ExpansionTile
中有一堆ListView
(到目前为止还不是太疯狂了,对吧?)。我们的用户希望我们在打开ExpansionTile
时“自动滚动”列表,因此新的展开图块现在位于屏幕的顶部(或尽可能顶部),因此他们没有手动滚动以查看我们刚刚打开的内容。
我认为我们在ScrollController
上需要一个ListView
之类的东西-并注册一个onExpansionChanged ..来做.. 。这对我来说还不算什么。 :)
任何有关如何执行此操作的指针或帮助将不胜感激!
TIA!
答案 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);
}
}
滚动控制器在展开时向上滚动,在折叠时通过将其高度乘以其索引向下滚动回到其初始位置。您不必回滚,这只是我个人对本示例的偏爱。这将导致如下结果:
这里有ListView
StatefulWidget
答案 2 :(得分:0)
如果您仍然遇到此问题,我发现了另一个使用Scrollable Widget的解决方案。我们必须将ExpansionTile的当前上下文传递给Scrollable类的sureVisible方法,然后它才能完成工作。您可以查看我的文章How to scroll an ExpansionTile when it is expanded?,以了解更多信息。