Flutter截图保存并加水印

一、屏幕截图

利用RepaintBoundary组件截屏,RepaintBoundary 能够截图是因为它创建了一个独立的绘制层,Flutter 可以捕获该层的内容并转换为图像。使用时,将屏幕上需要截取的内容用RepaintBoundary组件包裹,并传入全局变量GlobalKey

dart 复制代码
GlobalKey pictureKey = GlobalKey();

RepaintBoundary(
    key: pictureKey,
    child: Container(child:...)
)

实现截图的步骤

  • 通过 GlobalKey 获取 RepaintBoundary 的引用。
  • 调用 RepaintBoundarytoImage 方法,Flutter 会将该边界内的内容渲染为 ui.Image 对象。
  • ui.Image 转换为常见的图像格式。
  • 可以借助path_provider插件获取app目录并存储图片。
dart 复制代码
import 'dart:ui' as ui;
import 'dart:typed_data';
import 'package:flutter/rendering.dart';
import 'package:path_provider/path_provider.dart';

Future<void> _captureImage() async {
    RenderObject? boundary = pictureKey.currentContext?.findRenderObject();
    ui.Image image = await boundary.toImage();
    ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
    Uint8List pngBytes = byteData.buffer.asUint8List();
    Directory directory = await getTemporaryDirectory();
    String imagePath =
        '${directory.path}/${DateTime.now().microsecondsSinceEpoch}.png';
    await File(imagePath).writeAsBytes(pngBytes);
}
  • 如果是webview,使用RepaintBoundary是无法截图的,可以使用webview自带的截图方法,如FLutter中经常使用的flutter_inappwebview插件,可以使用webControllertakeScreenshot方法获取截图。
dart 复制代码
Uint8List? uint8List = await webController?.takeScreenshot();

二、图片加水印

上面已经通过RepaintBoundary拿到了ui.Image,可以利用ui.Image来创建Canvas画布,利用画布添加水印,可以添加图片、文字,最后,通过ui.PicturetoImage方法获取带水印的图片。

dart 复制代码
Future<void> _captureImage() async {
    RenderObject? boundary = pictureKey.currentContext?.findRenderObject();
    ui.Image image = await boundary.toImage();
    // 创建一个 Canvas 来绘制水印
    ui.PictureRecorder recorder = ui.PictureRecorder();
    Canvas canvas = Canvas(recorder);
    // 将原始图像绘制在canvas上
    canvas.drawImage(image, Offset.zero, Paint());
    // 添加图片水印
    // 从assets中读取水印图片
    ByteData waterData =
        await rootBundle.load('assets/images/water_mark.png');
    ui.Image waterImage =
        await decodeImageFromList(waterData.buffer.asUint8List());
    int width = image.width;
    int height = image.height;
    canvas.drawImageNine(
        waterImage,
        // 要绘制的水印图片区域
        ui.Rect.fromLTWH(
            0, 0, waterImage.width.toDouble(), waterImage.height.toDouble()),
        // 绘制水印图片的目标区域,如:以原始图片中心点为中心,绘制宽100高100的水印图片
        ui.Rect.fromCenter(
            center: Offset(width / 2, height / 2),
            width: 100,
            height: 100),
        Paint());
    ui.Picture picture = recorder.endRecording();
    // 获取带水印的图片
    ui.Image newImg = await picture.toImage(width, height);
    ...
}

如果是整张水印铺满原始图片的情况,直接指定水印图片宽高容易造成水印图片的拉伸变形,所以最好是保持水印图片的宽高比。可以通过计算原始图片的宽高比和水印图片的宽高比来进行判断:

如下图,把整张水印图片铺在原始图片上,为了避免水印图片拉伸变形,需要保持水印图片的宽高比。

  • 如果原始图片更宽,以原始图片的高为水印图片高,按水印图片宽高比计算出对应的水印图片宽度
  • 如果水印图片更宽,以原始图片的宽度为水印图片宽,按水印图片宽高比计算出对应的水印图片高度
dart 复制代码
// 原始图片宽高比
double imgRatio = width / height;
// 水印图片宽高比
double waterImgRatio = waterImage.width / waterImage.height;
double dstWidth = width.toDouble();
double dstHeight = height.toDouble();
if (imgRatio > waterImgRatio) {
  // 如果原始图片更宽,以原始图片的高为水印图片高,按水印图片宽高比计算出对应的水印图片宽度
  dstWidth = waterImgRatio * dstHeight;
} else {
  // 反之,水印图片更宽,以原始图片的宽度为水印图片宽,按水印图片宽高比计算出对应的水印图片高度
  dstHeight = dstWidth / waterImgRatio;
}
canvas.drawImageNine(
    waterImage,
    ui.Rect.fromLTWH(
        0, 0, waterImage.width.toDouble(), waterImage.height.toDouble()),
    ui.Rect.fromCenter(
        center: Offset(width / 2, height / 2),
        width: dstWidth,
        height: dstHeight),
    Paint());

除了添加图片,还可以添加文字水印:

dart 复制代码
final textPainter = TextPainter(
    text: TextSpan(
      text: '水印',
      style: TextStyle(
        color: Colors.white.withOpacity(0.5),
        fontSize: 40,
        fontWeight: FontWeight.bold,
      ),
    ),
    textDirection: TextDirection.ltr,
  );
textPainter.layout();
// 在原始图片中心绘制水印文字
textPainter.paint(canvas, Offset(
    (width - textPainter.width) / 2,
    (height - textPainter.height) / 2)
);

三、保存图片到相册

上文已经获取到了带水印的图片,一般用于保存或分享,可以借助image_gallery_saver插件来保存图片到相册。

dart 复制代码
ByteData? byteData = await newImg.toByteData(format: ui.ImageByteFormat.png);
Uint8List? pngUint8List = byteData?.buffer.asUint8List();
saveToAlbum(pngUint8List);

Future<String> saveToAlbum(
  Uint8List imageBytes, {
  int quality = 100,
  String? name,
}) async {
  dynamic result = await ImageGallerySaver.saveImage(
    imageBytes,
    quality: quality,
    name: name,
    isReturnImagePathOfIOS: true,
  );
  if (result is! Map) {
    return Future.value('');
  }
  return Future.value(result.['filePath']??''));
}
相关推荐
一人前行1 小时前
Flutter_学习记录_connectivity_plus 检测网络
flutter
顾林海3 小时前
Flutter Dart 异常处理全面解析
android·前端·flutter
程序员老刘5 小时前
从gitee上的鸿蒙开源Flutter停止更新说起
flutter·harmonyos
sunly_5 小时前
Flutter:竖向步骤条,类似查看物流组件
flutter
云空5 小时前
《解锁Flutter:跨平台开发的未来之光》
flutter
西辰Knight7 小时前
【Flutter+鸿蒙】从Flutter开发者视角来看鸿蒙是怎么样的?
flutter·harmonyos
一人前行8 小时前
Flutter_学习记录_ ImagePicker拍照、录制视频、相册选择照片和视频、上传文件
学习·flutter
SoaringHeart9 小时前
Flutter进阶:Cusor + claude-3.7 AI编程实战效果研究
前端·flutter
sunly_19 小时前
Flutter:跑马灯公告栏
flutter