如果我理解得正确的话,您想要的基本上是一种自动滚动ExpansionTile
的方式,这样用户就不必手动滚动来查看其内容,对吗?
那么,您实际上可以在ListView
中使用ScrollController
,并利用ExpansionTile
的onExpansionChanged
回调根据瓷砖与当前位置的大小相应地向上/向下滚动它。
但现在您正在想:我如何知道要滚动多少?
为此,您可以事先了解构建小部件时给它一些约束条件(例如构建一个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
,它将在其生成器中使用具有GlobalKey
的_buildExpansionTile
。 我们还将在扩展瓷砖时保存ListView
的滚动偏移量。 但是,我们如何实际上向上滚动以显示其内容? 让我们看看_scrollToSelectedContent
方法。
void _scrollToSelectedContent(bool isExpanded, double previousOffset, int index, GlobalKey myKey) {
final keyContext = myKey.currentContext;
if (keyContext != null) {
final box = keyContext.findRenderObject() as RenderBox;
_scrollController.animateTo(isExpanded ? (box.size.height * index) : previousOffset,
duration: Duration(milliseconds: 500), curve: Curves.linear);
}
}
因此,我们通过其键访问渲染对象,然后使用 ListView
滚动控制器向上滚动以展开,通过将其高度乘以其索引向下滚动回其初始位置时折叠。你不必滚动回来,这只是我对此示例的个人偏好。这将导致类似以下的结果:
![example](https://istack.dev59.com/qyYoN.gif)
您可以在 StatefulWidget
中找到完整的示例:
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: MyApp()));
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final ScrollController _scrollController = ScrollController();
void _scrollToSelectedContent(bool isExpanded, double previousOffset, int index, GlobalKey myKey) {
final keyContext = myKey.currentContext;
if (keyContext != null) {
final box = keyContext.findRenderObject() as RenderBox;
_scrollController.animateTo(isExpanded ? (box.size.height * index) : previousOffset,
duration: Duration(milliseconds: 500), curve: Curves.linear);
}
}
List<Widget> _buildExpansionTileChildren() => [
FlutterLogo(
size: 50.0,
),
Text(
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse vulputate arcu interdum lacus pulvinar aliquam. Donec ut nunc eleifend, volutpat tellus vel, volutpat libero. Vestibulum et eros lorem. Nam ut lacus sagittis, varius risus faucibus, lobortis arcu. Nullam tempor vehicula nibh et ornare. Etiam interdum tellus ut metus faucibus semper. Aliquam quis ullamcorper urna, non semper purus. Mauris luctus quam enim, ut ornare magna vestibulum vel. Donec consectetur, quam a mattis tincidunt, augue nisi bibendum est, quis viverra risus odio ac ligula. Nullam vitae urna malesuada magna imperdiet faucibus non et nunc. Integer magna nisi, dictum a tempus in, bibendum quis nisi. Aliquam imperdiet metus id metus rutrum scelerisque. Morbi at nisi nec risus accumsan tempus. Curabitur non sem sit amet tellus eleifend tincidunt. Pellentesque sed lacus orci.',
textAlign: TextAlign.justify,
),
];
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(),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('MyScrollView'),
),
body: ListView.builder(
controller: _scrollController,
itemCount: 100,
itemBuilder: (BuildContext context, int index) => _buildExpansionTile(index),
),
);
}
}
ListView
上的最后几个项进行微调,因为在展开最后一个ExpansionTile
之前,没有更多内容可供滚动。对此,您需要确定何时完成展开,然后滚动其子项高度的数量。 - Miguel Ruivo