Skip to content

Material 3 卡片组件样式规范

最后更新:2026-01-18 版本:v1.0 状态:✅ 强制执行

💡 提示:本文档是卡片组件的详细实战指南。如需查看完整的 Material 3 设计系统规范(包括文本、图标、颜色等基础规范),请参考 Material 3 设计系统


📋 目录

  1. 组件类型选择
  2. Card 样式规范
  3. ListTile 样式规范
  4. 布局规范
  5. 代码示例
  6. 常见错误

组件类型选择

决策树

展示内容类型?
├── 简单列表项(标题+副标题+图标)
│   └── ✅ ListTile

├── 卡片内容(图片+文字+操作)
│   └── ✅ Card + Column/Row

├── 可点击的卡片
│   └── ✅ Card + InkWell

├── 可展开的内容
│   └── ✅ ExpansionTile / ExpansionPanelList

├── 网格展示
│   └── ✅ GridView + Card

└── 信息面板
    └── ✅ Container + decoration

组件类型对照表

组件Material 3 组件使用场景示例
列表项ListTile设置列表、联系人列表、菜单项设置项、书籍列表
卡片Card书籍卡片、单词卡片、信息面板书籍详情、统计面板
可点击卡片Card + InkWell可交互的卡片项书籍选择、词汇卡片
展开列表ExpansionTile可展开的菜单、FAQ、分组列表设置分组、帮助文档
网格卡片GridView + Card图片网格、应用图标网格书籍封面网格

Card 样式规范

1. 基础样式

Material 3 默认样式(推荐使用):

dart
// ✅ 正确:使用默认样式
Card(
  child: Padding(
    padding: EdgeInsets.all(16),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text('标题', style: Theme.of(context).textTheme.titleMedium),
        SizedBox(height: 8),
        Text('内容'),
      ],
    ),
  ),
)

自动样式

  • 背景色colorScheme.surface
  • 阴影:elevation = 1(轻微阴影)
  • 圆角:12dp(Material 3 默认)
  • 边框:无(使用阴影区分)

2. 圆角规范

统一使用 12px 圆角

dart
// ✅ 正确:默认圆角(12px)
Card(
  child: ...,
)

// ⚠️ 自定义圆角(需要时使用)
Card(
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(12),  // 明确12px
  ),
  child: ...,
)

// ❌ 错误:圆角不一致
Card(
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(8),  // 不统一
  ),
  child: ...,
)

3. 阴影规范

Material 3 Elevation 系统

场景Elevation视觉效果
默认卡片1轻微阴影(推荐)
悬浮卡片2中等阴影
强调卡片4明显阴影
对话框6高层级阴影
dart
// ✅ 默认阴影(elevation: 1)
Card(
  child: ...,
)

// ⚠️ 悬浮效果(elevation: 2)
Card(
  elevation: 2,
  child: ...,
)

// ❌ 避免过高的阴影(> 6)
Card(
  elevation: 10,  // 过于突出
  child: ...,
)

4. 内边距规范

标准内边距:16px

dart
// ✅ 正确:统一使用 16px 内边距
Card(
  child: Padding(
    padding: EdgeInsets.all(16),
    child: ...,
  ),
)

// ⚠️ 紧凑布局:12px
Card(
  child: Padding(
    padding: EdgeInsets.all(12),
    child: ...,
  ),
)

// ⚠️ 宽松布局:24px
Card(
  child: Padding(
    padding: EdgeInsets.all(24),
    child: ...,
  ),
)

5. 颜色规范

背景色(自动)

dart
// ✅ 正确:使用默认背景色
Card(
  // 自动使用 colorScheme.surface
  child: ...,
)

// ⚠️ 自定义背景色(特殊场景)
Card(
  color: Theme.of(context).colorScheme.surfaceContainerHighest,
  child: ...,
)

// ❌ 错误:硬编码颜色
Card(
  color: Colors.grey[100],  // 不适配深色模式
  child: ...,
)

边框颜色(可选)

dart
// ⚠️ 使用边框替代阴影(扁平设计)
Card(
  elevation: 0,  // 无阴影
  shape: RoundedRectangleBorder(
    side: BorderSide(
      color: Theme.of(context).colorScheme.outline,
      width: 1,
    ),
    borderRadius: BorderRadius.circular(12),
  ),
  child: ...,
)

6. 可点击卡片

使用 InkWell 包裹内容

dart
// ✅ 正确:可点击卡片
Card(
  clipBehavior: Clip.antiAlias,  // 裁剪波纹效果
  child: InkWell(
    onTap: () {
      // 点击逻辑
    },
    child: Padding(
      padding: EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('可点击卡片'),
          SizedBox(height: 8),
          Text('点击查看详情'),
        ],
      ),
    ),
  ),
)

ListTile 样式规范

1. 基础用法

dart
// ✅ 标准 ListTile
ListTile(
  leading: Icon(Icons.book),              // 前置图标
  title: Text('书名'),                    // 标题
  subtitle: Text('作者:John Doe'),       // 副标题
  trailing: Icon(Icons.chevron_right),    // 后置图标
  onTap: () {
    // 点击逻辑
  },
)

2. 图标规范

位置尺寸颜色
leading24x24 dpcolorScheme.onSurfaceVariant
trailing24x24 dpcolorScheme.onSurfaceVariant
dart
// ✅ 正确:使用默认图标颜色
ListTile(
  leading: Icon(Icons.book),  // 自动使用 onSurfaceVariant
  title: Text('书名'),
  trailing: Icon(Icons.chevron_right),
)

// ❌ 错误:硬编码图标颜色
ListTile(
  leading: Icon(Icons.book, color: Colors.grey),  // 不适配主题
  title: Text('书名'),
)

3. 文本样式

元素样式颜色
titletitleMedium (16sp, w500)onSurface
subtitlebodyMedium (14sp, w400)onSurfaceVariant
dart
// ✅ 正确:使用默认文本样式
ListTile(
  title: Text('标题'),
  subtitle: Text('副标题'),
)

// ⚠️ 自定义样式(需要时使用)
ListTile(
  title: Text(
    '标题',
    style: Theme.of(context).textTheme.titleMedium?.copyWith(
      fontWeight: FontWeight.w600,
    ),
  ),
  subtitle: Text('副标题'),
)

4. 带图片的 ListTile

dart
// ✅ 圆形头像
ListTile(
  leading: CircleAvatar(
    radius: 24,
    backgroundImage: AssetImage('assets/book-cover.jpg'),
  ),
  title: Text('书名'),
  subtitle: Text('作者'),
)

// ✅ 方形缩略图
ListTile(
  leading: ClipRRect(
    borderRadius: BorderRadius.circular(6),  // 轻微圆角
    child: Image.asset(
      'assets/book-cover.jpg',
      width: 50,
      height: 64,
      fit: BoxFit.cover,
    ),
  ),
  title: Text('书名'),
  subtitle: Text('作者'),
)

5. 三行 ListTile

dart
// ✅ 三行布局
ListTile(
  isThreeLine: true,
  leading: Icon(Icons.book),
  title: Text('书名'),
  subtitle: Text(
    '作者:John Doe\n分类:Fiction',
    maxLines: 2,
    overflow: TextOverflow.ellipsis,
  ),
  trailing: Icon(Icons.chevron_right),
)

布局规范

1. 卡片间距

垂直间距:12px(Card 之间)

dart
ListView(
  padding: EdgeInsets.all(16),
  children: [
    Card(child: ...),
    SizedBox(height: 12),  // 卡片间距
    Card(child: ...),
    SizedBox(height: 12),
    Card(child: ...),
  ],
)

2. 卡片布局模式

垂直滚动列表

dart
ListView.builder(
  padding: EdgeInsets.all(16),
  itemCount: items.length,
  itemBuilder: (context, index) {
    return Padding(
      padding: EdgeInsets.only(bottom: 12),
      child: Card(
        child: ListTile(
          title: Text(items[index]),
        ),
      ),
    );
  },
)

网格布局

dart
GridView.builder(
  padding: EdgeInsets.all(16),
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 2,          // 2列
    crossAxisSpacing: 12,        // 水平间距
    mainAxisSpacing: 12,         // 垂直间距
    childAspectRatio: 0.7,       // 宽高比
  ),
  itemCount: items.length,
  itemBuilder: (context, index) {
    return Card(
      child: Column(
        children: [
          Image.asset(items[index].cover),
          Padding(
            padding: EdgeInsets.all(8),
            child: Text(items[index].title),
          ),
        ],
      ),
    );
  },
)

3. 卡片内容布局

图片 + 文字(垂直)

dart
Card(
  clipBehavior: Clip.antiAlias,
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      // 图片
      Image.asset(
        'assets/book-cover.jpg',
        height: 180,
        width: double.infinity,
        fit: BoxFit.cover,
      ),
      // 文字内容
      Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              '书名',
              style: Theme.of(context).textTheme.titleMedium,
            ),
            SizedBox(height: 4),
            Text(
              '作者:John Doe',
              style: Theme.of(context).textTheme.bodySmall?.copyWith(
                color: Theme.of(context).colorScheme.onSurfaceVariant,
              ),
            ),
          ],
        ),
      ),
    ],
  ),
)

图片 + 文字(水平)

dart
Card(
  child: Padding(
    padding: EdgeInsets.all(12),
    child: Row(
      children: [
        // 封面
        ClipRRect(
          borderRadius: BorderRadius.circular(6),
          child: Image.asset(
            'assets/book-cover.jpg',
            width: 50,
            height: 70,
            fit: BoxFit.cover,
          ),
        ),
        SizedBox(width: 12),
        // 文字
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                '书名',
                style: Theme.of(context).textTheme.titleMedium?.copyWith(
                  fontWeight: FontWeight.w600,
                ),
              ),
              SizedBox(height: 4),
              Text(
                '作者:John Doe',
                style: Theme.of(context).textTheme.bodySmall,
              ),
            ],
          ),
        ),
        // 操作按钮
        IconButton(
          icon: Icon(Icons.more_vert),
          onPressed: () {},
        ),
      ],
    ),
  ),
)

代码示例

示例1:书籍列表卡片

dart
class BookListCard extends StatelessWidget {
  final String title;
  final String author;
  final String coverPath;
  final VoidCallback onTap;

  const BookListCard({
    required this.title,
    required this.author,
    required this.coverPath,
    required this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      clipBehavior: Clip.antiAlias,
      child: InkWell(
        onTap: onTap,
        child: Padding(
          padding: const EdgeInsets.all(12.0),
          child: Row(
            children: [
              // 封面
              ClipRRect(
                borderRadius: BorderRadius.circular(6),
                child: Image.asset(
                  coverPath,
                  width: 50,
                  height: 70,
                  fit: BoxFit.cover,
                ),
              ),
              const SizedBox(width: 12),

              // 书籍信息
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      title,
                      style: Theme.of(context).textTheme.titleMedium?.copyWith(
                        fontWeight: FontWeight.w600,
                      ),
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                    ),
                    const SizedBox(height: 4),
                    Text(
                      '作者:$author',
                      style: Theme.of(context).textTheme.bodySmall?.copyWith(
                        color: Theme.of(context).colorScheme.onSurfaceVariant,
                      ),
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                    ),
                  ],
                ),
              ),

              // 箭头图标
              Icon(
                Icons.chevron_right,
                color: Theme.of(context).colorScheme.onSurfaceVariant,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

// 使用
BookListCard(
  title: 'The Great Gatsby',
  author: 'F. Scott Fitzgerald',
  coverPath: 'assets/gatsby.jpg',
  onTap: () {
    // 导航到书籍详情
  },
)

示例2:统计卡片

dart
class StatisticsCard extends StatelessWidget {
  final String title;
  final String value;
  final IconData icon;
  final Color? color;

  const StatisticsCard({
    required this.title,
    required this.value,
    required this.icon,
    this.color,
  });

  @override
  Widget build(BuildContext context) {
    final cardColor = color ?? Theme.of(context).colorScheme.primary;

    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 图标
            Container(
              padding: const EdgeInsets.all(8),
              decoration: BoxDecoration(
                color: cardColor.withOpacity(0.1),
                borderRadius: BorderRadius.circular(8),
              ),
              child: Icon(
                icon,
                color: cardColor,
                size: 32,
              ),
            ),
            const SizedBox(height: 12),

            // 标题
            Text(
              title,
              style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                color: Theme.of(context).colorScheme.onSurfaceVariant,
              ),
            ),
            const SizedBox(height: 4),

            // 数值
            Text(
              value,
              style: Theme.of(context).textTheme.headlineMedium?.copyWith(
                fontWeight: FontWeight.bold,
                color: cardColor,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// 使用
Row(
  children: [
    Expanded(
      child: StatisticsCard(
        title: '学习中',
        value: '128',
        icon: Icons.school,
        color: Theme.of(context).colorScheme.primary,
      ),
    ),
    const SizedBox(width: 12),
    Expanded(
      child: StatisticsCard(
        title: '已掌握',
        value: '342',
        icon: Icons.check_circle,
        color: Colors.green,
      ),
    ),
  ],
)

示例3:可展开卡片

dart
class ExpandableCard extends StatefulWidget {
  final String title;
  final String summary;
  final Widget content;

  const ExpandableCard({
    required this.title,
    required this.summary,
    required this.content,
  });

  @override
  _ExpandableCardState createState() => _ExpandableCardState();
}

class _ExpandableCardState extends State<ExpandableCard> {
  bool _isExpanded = false;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Column(
        children: [
          ListTile(
            title: Text(widget.title),
            subtitle: Text(widget.summary),
            trailing: IconButton(
              icon: Icon(
                _isExpanded ? Icons.expand_less : Icons.expand_more,
              ),
              onPressed: () {
                setState(() => _isExpanded = !_isExpanded);
              },
            ),
          ),
          if (_isExpanded)
            Padding(
              padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
              child: widget.content,
            ),
        ],
      ),
    );
  }
}

// 使用
ExpandableCard(
  title: 'CEFR A1 级别',
  summary: '初学者,掌握基础词汇',
  content: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Text('• 词汇量:500-1000'),
      Text('• 能够理解简单句子'),
      Text('• 日常对话基础'),
    ],
  ),
)

常见错误

❌ 错误1:圆角不统一

dart
// ❌ 错误:自定义圆角不一致
Card(
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(8),  // 不统一
  ),
  child: ...,
)

// ✅ 正确:使用默认圆角(12px)
Card(
  child: ...,
)

❌ 错误2:内边距不一致

dart
// ❌ 错误:内边距混乱
Card(
  child: Padding(
    padding: EdgeInsets.symmetric(horizontal: 10, vertical: 15),
    child: ...,
  ),
)

// ✅ 正确:统一16px内边距
Card(
  child: Padding(
    padding: EdgeInsets.all(16),
    child: ...,
  ),
)

❌ 错误3:硬编码颜色

dart
// ❌ 错误:硬编码背景色
Card(
  color: Colors.grey[100],
  child: ...,
)

// ✅ 正确:使用主题颜色
Card(
  // 自动使用 colorScheme.surface
  child: ...,
)

// ⚠️ 特殊场景:使用主题颜色
Card(
  color: Theme.of(context).colorScheme.surfaceContainerHighest,
  child: ...,
)

❌ 错误4:ListTile 图标硬编码

dart
// ❌ 错误:硬编码图标颜色
ListTile(
  leading: Icon(Icons.book, color: Colors.grey),
  title: Text('书名'),
)

// ✅ 正确:使用默认颜色
ListTile(
  leading: Icon(Icons.book),  // 自动使用 onSurfaceVariant
  title: Text('书名'),
)

❌ 错误5:过高的阴影

dart
// ❌ 错误:阴影过高
Card(
  elevation: 10,  // 过于突出
  child: ...,
)

// ✅ 正确:使用默认或低阴影
Card(
  elevation: 1,  // 默认(可省略)
  child: ...,
)

Card(
  elevation: 2,  // 悬浮效果
  child: ...,
)

快速参考

常用卡片模板

dart
// 基础卡片
Card(
  child: Padding(
    padding: EdgeInsets.all(16),
    child: Text('内容'),
  ),
)

// 可点击卡片
Card(
  clipBehavior: Clip.antiAlias,
  child: InkWell(
    onTap: () {},
    child: Padding(
      padding: EdgeInsets.all(16),
      child: Text('可点击内容'),
    ),
  ),
)

// ListTile 模板
ListTile(
  leading: Icon(Icons.book),
  title: Text('标题'),
  subtitle: Text('副标题'),
  trailing: Icon(Icons.chevron_right),
  onTap: () {},
)

// 带边框的卡片(扁平设计)
Card(
  elevation: 0,
  shape: RoundedRectangleBorder(
    side: BorderSide(
      color: Theme.of(context).colorScheme.outline,
      width: 1,
    ),
    borderRadius: BorderRadius.circular(12),
  ),
  child: Padding(
    padding: EdgeInsets.all(16),
    child: Text('内容'),
  ),
)

相关文档


维护者:Reading Vocab Helper Team 反馈:发现问题请提Issue