← Frontend / Flutter

13_图片与资源

Flutter 图片加载和资源管理

静态资源管理、网络图片缓存、SVG 图片使用。


注册 assets 资源

所有静态资源必须在 pubspec.yaml 中声明才能使用:

flutter:
  assets:
    - assets/images/         # 整个目录(包含子目录需单独列出)
    - assets/images/logo.png # 单个文件
    - assets/data/cities.json
  fonts:
    - family: NotoSansSC
      fonts:
        - asset: assets/fonts/NotoSansSC-Regular.ttf
        - asset: assets/fonts/NotoSansSC-Bold.ttf
          weight: 700

使用本地 assets

// 本地图片
Image.asset(
  'assets/images/banner.png',
  width: double.infinity,
  fit: BoxFit.cover,
)

// 读取 JSON 数据文件
import 'package:flutter/services.dart';

Future<List<dynamic>> loadCities() async {
  final jsonString = await rootBundle.loadString('assets/data/cities.json');
  return jsonDecode(jsonString) as List;
}

网络图片

Image.network(
  'https://example.com/photo.jpg',
  width: 200,
  height: 150,
  fit: BoxFit.cover,
  loadingBuilder: (context, child, progress) {
    if (progress == null) return child;  // 已加载完成
    return Center(
      child: CircularProgressIndicator(
        value: progress.cumulativeBytesLoaded / (progress.expectedTotalBytes ?? 1),
      ),
    );
  },
  errorBuilder: (context, error, stackTrace) {
    return const Icon(Icons.broken_image, size: 48, color: Colors.grey);
  },
)

cached_network_image(带缓存的网络图片)

类似 next/image,自动缓存到磁盘,避免重复下载:

dependencies:
  cached_network_image: ^3.3.0
import 'package:cached_network_image/cached_network_image.dart';

CachedNetworkImage(
  imageUrl: 'https://example.com/photo.jpg',
  width: 200,
  height: 150,
  fit: BoxFit.cover,
  // 加载中占位
  placeholder: (context, url) => Container(
    color: Colors.grey.shade200,
    child: const Center(child: CircularProgressIndicator()),
  ),
  // 加载失败占位
  errorWidget: (context, url, error) => Container(
    color: Colors.grey.shade100,
    child: const Icon(Icons.broken_image, color: Colors.grey),
  ),
  // 内存缓存尺寸(按显示尺寸缓存,节省内存)
  memCacheWidth: 200,
  memCacheHeight: 150,
)

// 图片预加载(提前缓存下一页图片)
await precacheImage(
  CachedNetworkImageProvider('https://example.com/next-image.jpg'),
  context,
);

// 主动清除缓存
await CachedNetworkImage.evictFromCache('https://example.com/photo.jpg');

SVG 图片

dependencies:
  flutter_svg: ^2.0.0
import 'package:flutter_svg/flutter_svg.dart';

// 本地 SVG
SvgPicture.asset(
  'assets/icons/logo.svg',
  width: 120,
  height: 120,
  colorFilter: const ColorFilter.mode(
    Colors.blue,
    BlendMode.srcIn, // 替换颜色(类似 CSS currentColor)
  ),
)

// 网络 SVG
SvgPicture.network(
  'https://example.com/icon.svg',
  width: 32,
  height: 32,
  placeholderBuilder: (_) => const SizedBox(
    width: 32,
    height: 32,
    child: CircularProgressIndicator(strokeWidth: 2),
  ),
)

图片显示技巧

// 圆形图片(头像)
ClipOval(
  child: CachedNetworkImage(
    imageUrl: avatarUrl,
    width: 80,
    height: 80,
    fit: BoxFit.cover,
  ),
)

// 圆角图片
ClipRRect(
  borderRadius: BorderRadius.circular(12),
  child: Image.network(imageUrl, fit: BoxFit.cover),
)

// 响应式图片(等比缩放)
AspectRatio(
  aspectRatio: 16 / 9,          // 类似 CSS aspect-ratio: 16/9
  child: Image.network(imageUrl, fit: BoxFit.cover),
)

// 骨架屏(加载占位)
Container(
  width: 200,
  height: 150,
  decoration: BoxDecoration(
    color: Colors.grey.shade200,
    borderRadius: BorderRadius.circular(8),
  ),
  // 可配合 shimmer 包实现闪光效果
)

// BoxFit 速查
// BoxFit.cover    → 裁剪填充(最常用,类似 object-fit: cover)
// BoxFit.contain  → 保持比例,留白(类似 object-fit: contain)
// BoxFit.fill     → 拉伸填充
// BoxFit.fitWidth → 宽度适配
// BoxFit.fitHeight→ 高度适配
// BoxFit.none     → 原始尺寸,居中显示