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")), ], ), ), )
参考思路
- 优先查看Flutter官方文档中
ExpansionTile和ExpansionPanelList的API,熟悉自带的可配置属性,大部分常规需求都能通过原生组件满足 - 若需要特殊动画或布局,结合
AnimationController、AnimatedSize、RotationTransition等动画组件实现自定义交互 - 可以参考Flutter官方的Material组件示例,里面有多种Expansion组件的用法案例
内容的提问来源于stack exchange,提问作者Muhammed Ajmal
相关产品推荐
相关产品推荐

