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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:16:02