Flutter中点击新Expansion Tile时如何收起已展开的Tile?
实现单个ExpansionTile展开时自动收起其他Tile的方案
这个需求我在开发Flutter项目时也碰到过,ExpansionTile的样式自定义灵活性确实比ExpansionPanel高太多了。要实现“点击一个展开,其他自动收起”的效果,核心思路就是用一个状态变量统一控制所有Tile的展开状态,下面给你两种实用的实现方式:
方式一:动态列表场景(推荐)
如果你的ExpansionTile是从动态数据(比如接口返回的列表)生成的,用ListView.builder配合索引状态管理最方便:
class SingleExpandTileList extends StatefulWidget { const SingleExpandTileList({super.key}); @override State<SingleExpandTileList> createState() => _SingleExpandTileListState(); } class _SingleExpandTileListState extends State<SingleExpandTileList> { // 记录当前展开的Tile索引,null表示无展开项 int? _activeExpandedIndex; @override Widget build(BuildContext context) { // 模拟动态数据,实际可以替换为接口返回的列表 final tileData = List.generate(5, (index) => "Tile $index"); return ListView.builder( itemCount: tileData.length, itemBuilder: (context, index) { return ExpansionTile( // 绑定展开状态:当前索引等于激活索引时展开 initiallyExpanded: index == _activeExpandedIndex, title: Text(tileData[index]), subtitle: const Text("点击展开/收起"), // 展开状态变化时更新激活索引 onExpansionChanged: (isExpanded) { setState(() { _activeExpandedIndex = isExpanded ? index : null; }); }, children: [ Padding( padding: const EdgeInsets.all(16.0), child: Text("这是${tileData[index]}的详情内容"), ), ], ); }, ); } }
代码说明:
_activeExpandedIndex作为全局状态,控制所有Tile的展开/收起- 每个ExpansionTile的
initiallyExpanded属性直接绑定当前索引与激活索引的对比结果 - 点击Tile时,通过
onExpansionChanged回调更新激活索引:展开时设为当前索引,收起时重置为null,这样其他Tile会自动同步状态
方式二:静态固定Tile场景
如果你的Tile数量是固定的,不需要动态生成,可以直接给每个Tile手动指定索引:
class StaticSingleExpandTiles extends StatefulWidget { const StaticSingleExpandTiles({super.key}); @override State<StaticSingleExpandTiles> createState() => _StaticSingleExpandTilesState(); } class _StaticSingleExpandTilesState extends State<StaticSingleExpandTiles> { int? _activeExpandedIndex; @override Widget build(BuildContext context) { return Column( children: [ ExpansionTile( initiallyExpanded: 0 == _activeExpandedIndex, title: const Text("个人信息"), onExpansionChanged: (isExpanded) { setState(() => _activeExpandedIndex = isExpanded ? 0 : null); }, children: const [Text("姓名、手机号、邮箱等信息")], ), ExpansionTile( initiallyExpanded: 1 == _activeExpandedIndex, title: const Text("收货地址"), onExpansionChanged: (isExpanded) { setState(() => _activeExpandedIndex = isExpanded ? 1 : null); }, children: const [Text("添加/编辑收货地址")], ), ExpansionTile( initiallyExpanded: 2 == _activeExpandedIndex, title: const Text("订单记录"), onExpansionChanged: (isExpanded) { setState(() => _activeExpandedIndex = isExpanded ? 2 : null); }, children: const [Text("查看全部订单状态")], ), ], ); } }
额外提示:
- 如果需要实现“点击当前展开的Tile不允许收起,始终保持一个展开状态”,可以修改
onExpansionChanged的逻辑:onExpansionChanged: (isExpanded) { setState(() { // 只有展开新Tile时才更新索引,点击当前展开的Tile不收起 if (isExpanded) { _activeExpandedIndex = index; } }); } - 这种方式完全基于Flutter的状态管理,不需要额外引入第三方包,自定义样式时直接修改ExpansionTile的
title、leading、trailing等属性即可,非常灵活。
内容的提问来源于stack exchange,提问作者praveen Dp
相关产品推荐
相关产品推荐

