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

Flutter中如何创建可展开卡片列表的下拉Widget组件

Flutter实现点击展开卡片下拉列表

你遇到的expansionCallback逻辑不清晰的问题,核心是没有理解这个组件的状态更新机制:ExpansionPanelList本身是无状态的,展开/收起的状态需要你自己在StatefulWidget中维护,expansionCallback就是点击头部触发的状态更新入口。

核心参数说明

  • expansionCallback(panelIndex, isExpanded):点击面板头部时触发的回调
    • panelIndex:当前被点击的面板在children数组中的索引值
    • isExpanded:点击动作触发前,对应面板的当前展开状态
  • 每个ExpansionPanel组件的isExpanded属性,必须和你自己维护的状态变量绑定,状态更新后组件会自动执行展开/收起的过渡动画,不需要手动写动画控制逻辑。

注意:ExpansionPanelList是无状态组件,自身不会维护展开/收起状态,必须放在StatefulWidget中使用,通过setState更新状态才能触发UI变化。

可直接运行的实现代码

这个代码实现的效果和参考截图交互一致:点击彩色头部展开白色卡片列表,再次点击收起。

import 'package:flutter/material.dart';

class ExpandableListPage extends StatefulWidget {
  const ExpandableListPage({super.key});

  @override
  State<ExpandableListPage> createState() => _ExpandableListPageState();
}

class _ExpandableListPageState extends State<ExpandableListPage> {
  // 存储面板的展开状态,初始为收起状态
  bool _panelExpanded = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[100],
      appBar: AppBar(title: const Text('展开列表示例')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: ExpansionPanelList(
          elevation: 3,
          // 展开回调核心逻辑
          expansionCallback: (panelIndex, isExpanded) {
            setState(() {
              // 将当前面板状态取反,触发UI重建
              _panelExpanded = !isExpanded;
            });
          },
          children: [
            ExpansionPanel(
              // 绑定展开状态
              isExpanded: _panelExpanded,
              backgroundColor: Colors.green,
              headerBuilder: (context, isExpanded) {
                return const ListTile(
                  title: Text(
                    'Campionati Appuntati',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 16,
                      fontWeight: FontWeight.w500
                    ),
                  ),
                );
              },
              // 展开后的列表内容区
              body: Column(
                children: [
                  ListTile(
                    tileColor: Colors.white,
                    title: const Text('Serie A'),
                    trailing: const Icon(Icons.chevron_right, color: Colors.grey),
                    onTap: () {
                      // 子项点击逻辑写在这里
                    },
                  ),
                  const Divider(height: 1, color: Color(0xFFF0F0F0)),
                  ListTile(
                    tileColor: Colors.white,
                    title: const Text('Premier League'),
                    trailing: const Icon(Icons.chevron_right, color: Colors.grey),
                    onTap: () {},
                  ),
                  const Divider(height: 1, color: Color(0xFFF0F0F0)),
                  ListTile(
                    tileColor: Colors.white,
                    title: const Text('La Liga'),
                    trailing: const Icon(Icons.chevron_right, color: Colors.grey),
                    onTap: () {},
                  ),
                ],
              ),
            )
          ],
        ),
      ),
    );
  }
}

多面板场景适配

如果你需要做多个可展开的下拉按钮,把单个bool状态改成对应数量的状态列表即可:

  • 定义状态列表:List<bool> _panelStateList = List.generate(面板总数, (index) => false);
  • 每个ExpansionPanel的isExpanded绑定对应索引的状态:isExpanded: _panelStateList[index]
  • 回调中更新对应索引的状态:
expansionCallback: (panelIndex, isExpanded) {
  setState(() {
    _panelStateList[panelIndex] = !isExpanded;
  });
}

样式调整技巧

  • 去掉面板默认分割线:给ExpansionPanelList加dividerColor: Colors.transparent
  • 调整展开/收起动画速度:组件默认动画时长为200ms,符合常规移动端交互体验,无需额外修改
  • 调整头部内边距:修改expandedHeaderPadding参数即可适配设计稿尺寸要求

内容的提问来源于stack exchange,提问作者faccio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 19:24:22