常用组件:文本、图片与按钮

日常写页面 80% 的时间都在用三样东西:TextImage、按钮。它们看着简单,细节却最多——文本缩放会不会撑破布局、网络图失败怎么兜底、按钮什么时候算禁用。本章逐个讲清常用参数与踩坑点,代码可直接复制。

Text:样式与富文本

Text(
  'Flutter 文本示例',
  style: Theme.of(context).textTheme.titleMedium?.copyWith(color: Colors.teal),
  maxLines: 2,
  overflow: TextOverflow.ellipsis, // 超出两行显示省略号,避免溢出
  textAlign: TextAlign.start,
)
需求写法说明
局部加粗或变色Text.rich(TextSpan(children: [...]))自动继承默认样式,推荐
跟随系统字号缩放默认行为,读 MediaQuery.textScalerOf(context)textScaleFactorOf 已废弃

富文本用 Text.rich:价格大字号、说明灰色小字,互不影响。

Text.rich(
  const TextSpan(children: [
    TextSpan(text: '¥', style: TextStyle(fontSize: 14, color: Colors.red)),
    TextSpan(text: '199', style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: Colors.red)),
    TextSpan(text: ' 限时优惠', style: TextStyle(fontSize: 12, color: Colors.grey)),
  ]),
  textScaler: MediaQuery.textScalerOf(context), // 读取系统字号缩放
)

Image:三种来源与加载状态

构造方式数据来源适用场景
Image.networkURL服务端图片(需联网权限)
Image.assetpubspec.yaml 声明的资源本地图标、占位图
Image.file文件路径相机/相册结果、缓存文件
Image.network(
  'https://picsum.photos/600/400',
  width: double.infinity,
  height: 200,
  fit: BoxFit.cover, // cover 裁切填满,contain 完整显示,fill 会变形
  loadingBuilder: (context, child, progress) =>
      progress == null ? child : const Center(child: CircularProgressIndicator()),
  errorBuilder: (context, error, stack) =>
      const ColoredBox(color: Color(0xFFEEEEEE), child: Center(child: Icon(Icons.broken_image))),
)
Image.asset('assets/images/logo.png', width: 96, height: 96) // 先在 pubspec.yaml 声明资源

fit 速查:cover 填满可能裁切、contain 完整可能留白、fitWidth 按宽铺满、fill 一定变形——列表封面图基本都用 cover。列表里的网络图建议改用 cached_network_image,省流量也避免滚动时反复请求:

flutter pub add cached_network_image
CachedNetworkImage(
  imageUrl: 'https://picsum.photos/600/400',
  fit: BoxFit.cover,
  placeholder: (context, url) => const ColoredBox(color: Color(0xFFEEEEEE)),
  errorWidget: (context, url, error) => const Icon(Icons.error_outline),
)

Icon 与按钮家族

Icon(Icons.favorite, size: 20, color: Colors.red);
Icon(Icons.star, semanticLabel: '评分'); // 读屏会念出来
组件视觉层级典型用途
FilledButton最高,实心强调页面主操作(提交、下单)
ElevatedButton高,带阴影通用主要按钮
OutlinedButton中,描边次要操作
TextButton低,纯文字取消、跳转、行内操作
IconButton图标按钮工具栏、列表行操作

onPressed: null 就是禁用,视觉变灰且不响应点击,不需要额外的 enabled 参数:

Row(
  children: [
    Expanded(
      child: FilledButton(onPressed: canSubmit ? _submit : null, child: const Text('提交')),
    ),
    const SizedBox(width: 12),
    OutlinedButton.icon(onPressed: _cancel, icon: const Icon(Icons.close), label: const Text('取消')),
    IconButton(onPressed: _toggle, icon: const Icon(Icons.bookmark_border), tooltip: '收藏'),
  ],
)

容器类组件

组件用途关键参数
Card卡片容器,自带圆角阴影elevationshapemargin
Divider分隔线heightthicknessindent
Chip标签、筛选项avataronDeletedselected
Badge角标(消息数)labelisLabelVisible
Card(
  margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
  child: Column(children: [
    const ListTile(
      leading: CircleAvatar(child: Text('瑞')),
      title: Text('瑞林'),
      subtitle: Text('Flutter 学习者'),
      trailing: Badge(label: Text('3'), child: Icon(Icons.notifications_none)),
    ),
    const Divider(height: 1),
    Padding(
      padding: const EdgeInsets.all(12),
      child: Wrap(spacing: 8, children: const [Chip(label: Text('Dart')), Chip(label: Text('Flutter'))]),
    ),
  ]),
)

常见坑与调试方法

现象原因排查方式
文本溢出或截断写死高度、系统字号被放大去掉固定 height,设 maxLines + overflow
图片一片空白忘了声明 assets 或地址 404检查 pubspec.yaml 并补 errorBuilder
列表滚动掉帧大图未限制解码尺寸cacheWidth/cacheHeight 或改用缩略图
按钮点了没反应onPressed 传了 null 或抛异常断点确认是否真的进入回调

小结:Text 优先用主题样式加 copyWith,并留意系统字号缩放导致的溢出;网络图必须配 loadingBuilder/errorBuilder 或改用 cached_network_image;按钮用 onPressed: null 表达禁用,主次操作按 FilledButton/OutlinedButton/TextButton 分层;CardListTileChipBadge 组合起来能覆盖大部分列表与详情页。

笔记加载中…