You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:40:05