面试题答案
一键面试1. 图片分辨率适配
- 根据设备像素密度(DPI)加载合适图片:Flutter 支持根据不同的设备像素密度加载不同分辨率的图片。在项目的
assets/images
目录下,可以创建如images/1.0x
、images/2.0x
、images/3.0x
这样的文件夹,分别存放对应分辨率的图片。例如,对于普通屏幕(1 倍像素密度)设备,会从1.0x
文件夹加载图片;对于高清屏幕(2 倍像素密度)设备,会从2.0x
文件夹加载图片。在pubspec.yaml
文件中配置资源路径时,可以这样写:
flutter:
assets:
- assets/images/1.0x/
- assets/images/2.0x/
- assets/images/3.0x/
然后在代码中通过 Image.asset
加载图片,Flutter 会自动根据设备像素密度选择合适的图片,避免加载过高分辨率图片造成内存浪费。例如:
Image.asset('assets/images/my_image.png');
- 手动调整图片分辨率:如果没有按不同像素密度准备图片,可以在加载图片时手动调整图片分辨率。使用
ImageProvider
的resolve
方法,结合ImageConfiguration
来指定图片的目标分辨率。例如:
Future<Image> loadResizedImage(String assetName, double targetWidth, double targetHeight) async {
final imageProvider = AssetImage(assetName);
final ImageConfiguration config = createLocalImageConfiguration(context, size: Size(targetWidth, targetHeight));
final ImageStream stream = imageProvider.resolve(config);
final Completer<Image> completer = Completer();
stream.addListener(ImageStreamListener((ImageInfo info, bool _) {
final ui.Image uiImage = info.image;
final double widthRatio = targetWidth / uiImage.width;
final double heightRatio = targetHeight / uiImage.height;
final double scale = widthRatio < heightRatio? widthRatio : heightRatio;
final ui.Image resizedImage = uiImage.copyResized(
width: (uiImage.width * scale).toInt(),
height: (uiImage.height * scale).toInt(),
);
completer.complete(Image(image: resizedImage));
}));
return completer.future;
}
2. 图片格式优化
- 使用 WebP 格式:WebP 是一种现代的图像格式,它在提供与 JPEG 相当的视觉质量的同时,文件大小通常更小。在 Flutter 中,可以通过添加
flutter_webp
插件来支持 WebP 图片加载。首先在pubspec.yaml
中添加依赖:
dependencies:
flutter_webp: ^1.0.0
然后在代码中加载 WebP 图片:
import 'package:flutter_webp/flutter_webp.dart';
WebPImage.asset('assets/images/my_image.webp');
- 选择合适的压缩质量:对于 JPEG 图片,降低压缩质量可以减小文件大小,但会影响图片质量。在图片处理工具中,将 JPEG 图片的压缩质量设置在 70 - 80 之间,通常可以在视觉质量和文件大小之间取得较好的平衡。对于 PNG 图片,如果图片颜色较少,可以转换为 8 位或 4 位的 PNG 格式,以减小文件大小。
3. 图片缓存策略
- 使用 Flutter 内置缓存:Flutter 提供了
ImageCache
来缓存图片。默认情况下,Image.asset
和Image.network
加载的图片会自动被缓存。可以通过PaintingBinding.instance.imageCache
来访问和管理图片缓存。例如,可以设置缓存的最大数量和最大内存占用:
PaintingBinding.instance.imageCache.maximumSize = 100; // 缓存图片最大数量
PaintingBinding.instance.imageCache.maximumSizeBytes = 1024 * 1024 * 10; // 缓存图片最大内存占用 10MB
- 自定义缓存策略:如果内置缓存不能满足需求,可以实现自定义缓存策略。例如,使用
lru_cache
插件来实现基于最近最少使用(LRU)算法的缓存。首先在pubspec.yaml
中添加依赖:
dependencies:
lru_cache: ^1.0.0
然后在代码中创建自定义缓存并管理图片加载:
import 'package:lru_cache/lru_cache.dart';
final LruCache<String, ImageProvider> imageCache = LruCache(100);
ImageProvider getImageProvider(String imageUrl) {
if (imageCache.containsKey(imageUrl)) {
return imageCache[imageUrl]!;
}
final ImageProvider provider = NetworkImage(imageUrl);
imageCache.put(imageUrl, provider);
return provider;
}
4. 按需加载图片
- 懒加载:在列表或长页面中,使用
ListView.builder
或GridView.builder
等构造函数来按需创建和加载图片。这些构造函数只会创建当前可见区域内的图片,当图片滚动出可见区域时,Flutter 会自动释放相关资源。例如:
ListView.builder(
itemCount: imageList.length,
itemBuilder: (context, index) {
return Image.asset(imageList[index]);
},
),
- 渐进式加载:对于网络图片,可以使用
Image.network
的loadingBuilder
属性来实现渐进式加载。例如,在图片加载过程中显示一个占位符,当图片加载完成后再显示完整图片:
Image.network(
'https://example.com/image.jpg',
loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes!
: null,
),
);
},
),