Flutter复用卡片组件:如何实现类似Laravel Blade的内容插槽?
嘿,这个需求太常见了!从Laravel Blade的模板插槽转到Flutter,确实需要找对应的组件复用方式,刚好Flutter的组合式Widget模型完美适配这种场景,我给你分享几种最实用的实现方式:
1. 基础单插槽卡片(对应Blade的单个
@yield) 这是最直接的方式,就像你在Blade里定义带@yield('card_content')的模板一样,我们可以给自定义卡片Widget加一个child参数,用来接收外部传入的可变内容:
class ReusableCard extends StatelessWidget { // 这个child就相当于Blade里的@yield('card_content') final Widget child; // 可选:如果卡片需要固定的标题/其他元素,也可以加参数 final String? cardTitle; // 构造函数,child是必填项 const ReusableCard({ super.key, required this.child, this.cardTitle, }); @override Widget build(BuildContext context) { // 这里是卡片的固定结构,和你Blade里的<div class="card">对应 return Card( elevation: 3, margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 固定的标题部分(可选) if (cardTitle != null) Text( cardTitle!, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), if (cardTitle != null) const SizedBox(height: 12), // 这里就是插槽位置,渲染外部传入的child内容 child, ], ), ), ); } }
使用的时候,就像Blade里填充@section('card_content')一样,直接把你要展示的内容作为child传进去就行:
// 在某个页面的build方法里 @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('用户列表')), floatingActionButton: FloatingActionButton( onPressed: () {}, child: const Icon(Icons.add), ), body: ReusableCard( cardTitle: '用户数据', // 这里就是你原来Blade里@section的内容,比如表格、列表啥的 child: DataTable( columns: const [ DataColumn(label: Text('ID')), DataColumn(label: Text('用户名')), ], rows: [ DataRow(cells: [ const DataCell(Text('1')), DataCell(Text('张三')), ]), DataRow(cells: [ const DataCell(Text('2')), DataCell(Text('李四')), ]), ], ), ), ); }
2. 多插槽卡片(对应多个
@yield) 如果你的卡片需要多个可变区域(比如头部、主体、底部),可以给自定义Widget添加多个Widget参数,就像Blade里的多个@yield:
class MultiSlotCard extends StatelessWidget { final Widget header; final Widget body; final Widget? footer; // 可选插槽 const MultiSlotCard({ super.key, required this.header, required this.body, this.footer, }); @override Widget build(BuildContext context) { return Card( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ header, const Divider(height: 1), Expanded(child: body), // 如果需要主体滚动或占满空间,可以用Expanded if (footer != null) const Divider(height: 1), if (footer != null) footer!, ], ), ); } }
使用时分别传入各个插槽的内容:
MultiSlotCard( header: const Padding( padding: EdgeInsets.all(16), child: Text('订单详情'), ), body: const Padding( padding: EdgeInsets.all(16), child: Text('订单编号:202405201001\n下单时间:2024-05-20'), ), footer: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton(onPressed: () {}, child: const Text('取消')), ElevatedButton(onPressed: () {}, child: const Text('确认')), ], ), )
核心思路总结
Flutter的组件复用和Blade的模板复用逻辑本质是相通的:
- Blade是模板继承+插槽,把可变内容注入固定模板;
- Flutter是组合式Widget,通过把可变的Widget作为参数传入固定结构的Widget中,实现复用。
这种方式不仅能复用卡片,你的AppBar、悬浮按钮这些重复组件也可以用同样的思路封装,比如写一个CustomAppBar、CustomFAB,然后在各个页面里直接调用,完全避免重复代码。
内容的提问来源于stack exchange,提问作者renandame
相关产品推荐
相关产品推荐

