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

Flutter中如何制作Expansion Tile?需求示例及问题求助

实现自定义样式的Flutter Expansion Tile

方法一:基于原生ExpansionTile自定义

Flutter自带的ExpansionTile本身支持自定义头部的leading、title、trailing区域,以及展开后的children内容,足够实现你图中的样式,直接配置属性就能快速搭建:

class CustomExpansionTile extends StatelessWidget {
  final String title;
  final Widget? leading;
  final Widget? trailing;
  final List<Widget> children;

  const CustomExpansionTile({
    super.key,
    required this.title,
    this.leading,
    this.trailing,
    required this.children,
  });

  @override
  Widget build(BuildContext context) {
    return ExpansionTile(
      // 左侧图标/头像
      leading: leading ?? const Icon(Icons.category_outlined),
      // 标题文本
      title: Text(
        title,
        style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
      ),
      // 右侧尾部元素(比如数量、按钮)
      trailing: trailing ?? const Icon(Icons.keyboard_arrow_down),
      // 展开后的子内容
      children: children,
      // 自定义背景色
      backgroundColor: Colors.grey[50],
      collapsedBackgroundColor: Colors.white,
      // 调整内边距适配布局
      tilePadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      childrenPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
    );
  }
}

// 使用示例
CustomExpansionTile(
  title: "我的收藏",
  leading: const CircleAvatar(
    backgroundColor: Colors.orange,
    child: Icon(Icons.favorite, color: Colors.white),
  ),
  trailing: const Text("12项", style: TextStyle(color: Colors.grey, fontSize: 14)),
  children: [
    ListTile(title: const Text("商品收藏"), onTap: () {}),
    ListTile(title: const Text("店铺收藏"), onTap: () {}),
  ],
)

方法二:完全自定义展开面板(更灵活)

如果原生ExpansionTile的样式或交互满足不了需求,可以用GestureDetector结合动画组件自己封装,完全控制展开收起的逻辑和动画:

class CustomExpandPanel extends StatefulWidget {
  final Widget header;
  final Widget content;
  final Duration animationDuration;

  const CustomExpandPanel({
    super.key,
    required this.header,
    required this.content,
    this.animationDuration = const Duration(milliseconds: 300),
  });

  @override
  State<CustomExpandPanel> createState() => _CustomExpandPanelState();
}

class _CustomExpandPanelState extends State<CustomExpandPanel> with SingleTickerProviderStateMixin {
  bool _isExpanded = false;
  late AnimationController _controller;
  late Animation<double> _rotationAnim;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: widget.animationDuration,
    );
    _rotationAnim = Tween<double>(begin: 0, end: 0.5).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _toggle() {
    setState(() {
      _isExpanded = !_isExpanded;
      _isExpanded ? _controller.forward() : _controller.reverse();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 点击头部触发展开收起
        GestureDetector(
          onTap: _toggle,
          child: widget.header,
        ),
        // 展开内容,带动画过渡
        AnimatedSize(
          duration: widget.animationDuration,
          curve: Curves.easeInOut,
          child: _isExpanded ? widget.content : const SizedBox.shrink(),
        ),
      ],
    );
  }
}

// 使用示例
CustomExpandPanel(
  header: Container(
    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
    decoration: const BoxDecoration(
      border: Border(bottom: BorderSide(color: Colors.grey.shade200)),
    ),
    child: Row(
      children: [
        const CircleAvatar(
          backgroundColor: Colors.blue,
          child: Icon(Icons.shopping_cart, color: Colors.white),
        ),
        const SizedBox(width: 12),
        const Expanded(child: Text("购物车", style: TextStyle(fontSize: 16))),
        Row(
          children: [
            const Text("编辑", style: TextStyle(color: Colors.blue, fontSize: 14)),
            const SizedBox(width: 8),
            RotationTransition(
              turns: _rotationAnim,
              child: const Icon(Icons.keyboard_arrow_down),
            ),
          ],
        ),
      ],
    ),
  ),
  content: Container(
    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
    child: Column(
      children: const [
        ListTile(title: Text("商品A")),
        ListTile(title: Text("商品B")),
      ],
    ),
  ),
)

参考思路

  1. 优先查看Flutter官方文档中ExpansionTile和ExpansionPanelList的API,熟悉自带的可配置属性,大部分常规需求都能通过原生组件满足
  2. 若需要特殊动画或布局,结合AnimationController、AnimatedSize、RotationTransition等动画组件实现自定义交互
  3. 可以参考Flutter官方的Material组件示例,里面有多种Expansion组件的用法案例

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 14:50:15