Flutter给图片添加水印,有两种方法
第一种:直接操作图片,将水印文字与图片一起编码生成新的图片;
图中的日期就是水印。
首先需要引用到一个第三方库,放于pubspec.yaml
中
path_provider: ^2.0.1 #路径管理
image_picker: ^0.8.4+11 #拍照,相册
接下来看是如何实现的,先看下业务代码调用
//拍照
XFile? file = await ImagePicker.platform.getImage(source: ImageSource.camera);
//拍照图传至WaterMarkPage返回水印图
Get.to(WaterMarkPage(file!.path))?.then((newSignImg){
//上传水印图
ApiCommonRequest.uploadFile(HttpUrls.api_img_upload, newSignImg, Constants.FILE_IMAGE, onSuccess: (String fileUrl) {
print("图片上传成功:${fileUrl}");
});
});
然后重点看下WaterMarkPage.dart代码是如何生成水印图的
import 'dart:io';
import 'dart:ui' as ui;
import 'package:app_flutter/base/base_page.dart';
import 'package:app_flutter/constant/font_styles.dart';
import 'package:app_flutter/pages/task/view/icon_text_button.dart';
import 'package:app_flutter/utils/date_utils.dart';
import 'package:app_flutter/utils/image_loader_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
///图片生产水印
class WaterMarkPage extends BasePage {
//任务ID
late String imagePath;
WaterMarkPage(this.imagePath, {Key? key}) : super(key: key);
@override
State<WaterMarkPage> createState() => _WaterMarkPageState();
}
class _WaterMarkPageState extends BasePageState<WaterMarkPage> {
GlobalKey _globalKey = GlobalKey();
@override
AppBar buildAppBar() {
return AppBar(
title: Text(
"签到图片",
style: FontStyles.black(fontSize: 34.sp),
),
elevation: 0,
leading: BackButton(
color: Colors.black,
),
backgroundColor: Colors.transparent,
centerTitle: true,
actions: [
IconTextButton(
padding: EdgeInsets.only(right: 25.w),
text: "保存",
tapCallback: saveSignImg),
],
);
}
/**
* 保存签到图片
*/
Future<void> saveSignImg() async {
///通过globalkey将Widget保存为ui.Image
ui.Image _image = await ImageLoaderUtils.imageLoader.getImageFromWidget(_globalKey);
///异步将这张图片保存在手机内部存储目录下
String? localImagePath = await ImageLoaderUtils.imageLoader.saveImageByUIImage(_image, isEncode: false);
///保存完毕后关闭当前页面并将保存的图片路径返回到上一个页面
Get.back(result: localImagePath);
}
@override
Widget buildBody(BuildContext context) {
return Container(
alignment: Alignment.center,
child: RepaintBoundary(
key: _globalKey,
child: Stack(
children: [
Image.file(
File(widget.imagePath),
width: 1.sw,
fit: BoxFit.fitWidth,
),
Positioned(
top: 2,
right: 2,
child: Text(
MDateUtils.dateNow(),
style: FontStyles.value(fontSize: 30.sp, color: Colors.white),
))
],
),
)
);
}
}
上面部分代码没有参考价值,主要参考saveSignImg()
方法即可;
最后提供ImageLoaderUtils.dart
源码
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'dart:ui';
import 'package:crypto/crypto.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
/// 图片加载工具类
class ImageLoaderUtils {
//私有化构造
ImageLoaderUtils._();
//单例模式创建
static final ImageLoaderUtils imageLoader = ImageLoaderUtils._();
// 将一个Widget转为image.Image对象
Future<ui.Image> getImageFromWidget(GlobalKey globalKey) async {
// globalKey为需要图像化的widget的key
RenderRepaintBoundary? boundary =
globalKey.currentContext?.findRenderObject() as RenderRepaintBoundary?;
// 转换为图像
ui.Image img = await boundary!.toImage();
return img;
}
///将指定的文件保存到目录空间中。
///[image] 这里是使用的ui包下的Image
///[picName] 保存到本地的文件(图片)文件名,如test_image
///[endFormat]保存到本地的文件(图片)文件格式,如png,
///[isReplace]当本地存在同名的文件(图片)时,true就是替换
///[isEncode]对保存的文件(图片)进行编码
/// 最终保存到本地的文件 (图片)的名称为 picName.endFormat
Future<String> saveImageByUIImage(ui.Image image,
{String? picName,
String endFormat = "png",
bool isReplace = true,
bool isEncode = true}) async {
///获取本地磁盘路径
/*
* 在Android平台中获取的是/data/user/0/com.studyyoun.flutterbookcode/app_flutter
* 此方法在在iOS平台获取的是Documents路径
*/
Directory appDocDir = await getApplicationDocumentsDirectory();
String appDocPath = appDocDir.path;
///拼接目录
if (picName == null || picName.trim().length == 0) {
///当用户没有指定picName时,取当前的时间命名
picName = "${DateTime.now().millisecond.toString()}.$endFormat";
} else {
picName = "$picName.$endFormat";
}
if (isEncode) {
///对保存的图片名字加密
picName = md5.convert(utf8.encode(picName)).toString();
}
appDocPath = "$appDocPath/$picName";
///校验图片是否存在
var file = File(appDocPath);
bool exist = await file.exists();
if (exist) {
if (isReplace) {
///如果图片存在就进行删除替换
///如果新的图片加载失败,那么旧的图片也被删除了
await file.delete();
} else {
///如果图片存在就不进行下载
return "";
}
}
ByteData? byteData = await image.toByteData(format: ImageByteFormat.png);
Uint8List pngBytes = byteData!.buffer.asUint8List();
print("保存的图片路径 $appDocPath");
///将Uint8List的数据格式保存
await File(appDocPath).writeAsBytes(pngBytes);
return appDocPath;
}
}
参考文章 https://biglead.blog.csdn.net/article/details/106874804