Flutter/Dart中如何读取本地图片、获取尺寸并内存缩放?
嘿,这事儿我熟!在Flutter/Dart里完全能在内存里完成「读图→取尺寸→缩放」的全流程,不用把处理后的图片存回磁盘,直接就能用Image组件显示。下面给你一步步拆解实现:
步骤1:从磁盘读取图片字节
首先我们用dart:io里的File类读取图片文件,把它转换成内存中的字节数组(Uint8List)——这是后续所有操作的基础:
import 'dart:io'; // 替换成你的图片实际路径 final imageFile = File('/storage/emulated/0/Download/test.jpg'); if (!await imageFile.exists()) { throw Exception('图片文件不存在'); } final originalBytes = await imageFile.readAsBytes();
步骤2:获取图片原始宽高
要拿到原始尺寸,我们需要用dart:ui里的图片解码能力(不能直接用Flutter的Image组件,因为它是UI层的,没法直接拿尺寸数据):
import 'dart:ui' as ui; // 解码字节得到原始图片对象 final codec = await ui.instantiateImageCodec(originalBytes); final frame = await codec.getNextFrame(); final originalImage = frame.image; // 现在就能拿到原始宽高了 final originalWidth = originalImage.width; final originalHeight = originalImage.height; print('原始尺寸:$originalWidth x $originalHeight');
步骤3:内存中缩放图片
接下来我们用Canvas绘制缩放后的图片,再把绘制结果转换成字节数组——全程都在内存里完成,不用碰磁盘:
// 定义目标缩放尺寸(这里按比例缩放到宽度300,你可以按需调整) final targetWidth = 300; final targetHeight = (originalHeight * targetWidth / originalWidth).round(); // 创建绘图记录器和画布 final recorder = ui.PictureRecorder(); final canvas = Canvas(recorder); // 用高质量滤镜提升缩放后的图片清晰度 final paint = Paint()..filterQuality = FilterQuality.high; // 把原始图片绘制到目标尺寸的画布上 canvas.drawImageRect( originalImage, Rect.fromLTWH(0, 0, originalWidth.toDouble(), originalHeight.toDouble()), Rect.fromLTWH(0, 0, targetWidth.toDouble(), targetHeight.toDouble()), paint, ); // 结束绘图,生成缩放后的图片对象 final picture = recorder.endRecording(); final scaledImage = await picture.toImage(targetWidth, targetHeight); // 把图片转换成PNG字节(也可以选JPEG,不过PNG无损) final byteData = await scaledImage.toByteData(format: ui.ImageByteFormat.png); final scaledBytes = byteData!.buffer.asUint8List(); // 重要:记得释放图片资源,避免内存泄漏 originalImage.dispose(); scaledImage.dispose();
最终显示缩放后的图片
现在scaledBytes就是内存里的缩放后图片字节,直接用Image.memory就能显示:
Image.memory(scaledBytes)
完整封装函数
为了方便复用,你可以把这些逻辑封装成一个异步函数:
import 'dart:io'; import 'dart:ui' as ui; import 'dart:typed_data'; Future<Uint8List> scaleImageFromDisk(String imagePath, int targetWidth) async { final imageFile = File(imagePath); if (!await imageFile.exists()) { throw Exception('图片文件不存在'); } // 读取原始字节 final originalBytes = await imageFile.readAsBytes(); // 获取原始尺寸 final codec = await ui.instantiateImageCodec(originalBytes); final frame = await codec.getNextFrame(); final originalImage = frame.image; final originalWidth = originalImage.width; final originalHeight = originalImage.height; // 计算目标高度(按比例缩放) final targetHeight = (originalHeight * targetWidth / originalWidth).round(); // 绘制缩放后的图片 final recorder = ui.PictureRecorder(); final canvas = Canvas(recorder); final paint = Paint()..filterQuality = FilterQuality.high; canvas.drawImageRect( originalImage, Rect.fromLTWH(0, 0, originalWidth.toDouble(), originalHeight.toDouble()), Rect.fromLTWH(0, 0, targetWidth.toDouble(), targetHeight.toDouble()), paint, ); final picture = recorder.endRecording(); final scaledImage = await picture.toImage(targetWidth, targetHeight); final byteData = await scaledImage.toByteData(format: ui.ImageByteFormat.png); final scaledBytes = byteData!.buffer.asUint8List(); // 释放资源 originalImage.dispose(); scaledImage.dispose(); return scaledBytes; }
调用的时候就这么用:
final scaledBytes = await scaleImageFromDisk('/path/to/your/image.jpg', 300); // 在UI里显示 Image.memory(scaledBytes)
注意事项
- 别忘了处理异常:比如文件不存在、解码失败、内存不足等情况,建议用
try-catch包裹调用逻辑 - 如果需要按高度缩放,或者固定尺寸(不按比例),只需要调整
targetWidth和targetHeight的计算逻辑就行 - 缩放后的图片格式可以选
ui.ImageByteFormat.jpeg,这样字节数组会更小,但会损失一点画质
内容的提问来源于stack exchange,提问作者Marcelo Glasberg
相关产品推荐
相关产品推荐

