Flutter給圖片添加水印

Flutter給圖片添加水印,有兩種方法
第一種:直接操作圖片怎囚,將水印文字與圖片一起編碼生成新的圖片;

第二種:用Stack組件桥胞,childs中放兩個元素恳守,一個是Image,一個是Text贩虾,最后用RepaintBoundary將Stack包起來催烘,從Widget中截取獲得合成后的水印圖(本文介紹的是這種,也是博主推薦的)整胃。
image.png

圖中的日期就是水印颗圣。

首先需要引用到一個第三方庫,放于pubspec.yaml

path_provider: ^2.0.1       #路徑管理
image_picker: ^0.8.4+11     #拍照屁使,相冊

接下來看是如何實現(xiàn)的在岂,先看下業(yè)務(wù)代碼調(diào)用

//拍照
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';

///圖片生產(chǎn)水印
class WaterMarkPage extends BasePage {
  //任務(wù)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);

    ///異步將這張圖片保存在手機(jī)內(nèi)部存儲目錄下
    String? localImagePath =  await ImageLoaderUtils.imageLoader.saveImageByUIImage(_image, isEncode: false);
    ///保存完畢后關(guān)閉當(dāng)前頁面并將保存的圖片路徑返回到上一個頁面
    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 {

  //私有化構(gòu)造
  ImageLoaderUtils._();

  //單例模式創(chuàng)建
  static final ImageLoaderUtils imageLoader = ImageLoaderUtils._();

  // 將一個Widget轉(zhuǎn)為image.Image對象
  Future<ui.Image> getImageFromWidget(GlobalKey globalKey) async {
    // globalKey為需要圖像化的widget的key
    RenderRepaintBoundary? boundary =
        globalKey.currentContext?.findRenderObject() as RenderRepaintBoundary?;
    // 轉(zhuǎn)換為圖像
    ui.Image img = await boundary!.toImage();
    return img;
  }

  ///將指定的文件保存到目錄空間中蔽午。
  ///[image] 這里是使用的ui包下的Image
  ///[picName] 保存到本地的文件(圖片)文件名,如test_image
  ///[endFormat]保存到本地的文件(圖片)文件格式酬蹋,如png及老,
  ///[isReplace]當(dāng)本地存在同名的文件(圖片)時,true就是替換
  ///[isEncode]對保存的文件(圖片)進(jìn)行編碼
  ///  最終保存到本地的文件 (圖片)的名稱為 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) {
      ///當(dāng)用戶沒有指定picName時范抓,取當(dāng)前的時間命名
      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) {
        ///如果圖片存在就進(jìn)行刪除替換
        ///如果新的圖片加載失敗骄恶,那么舊的圖片也被刪除了
        await file.delete();
      } else {
        ///如果圖片存在就不進(jìn)行下載
        return "";
      }
    }
    ByteData? byteData = await image.toByteData(format: ImageByteFormat.png);
    Uint8List pngBytes = byteData!.buffer.asUint8List();
    print("保存的圖片路徑 $appDocPath");

    ///將Uint8List的數(shù)據(jù)格式保存
    await File(appDocPath).writeAsBytes(pngBytes);

    return appDocPath;
  }
}

參考文章 https://biglead.blog.csdn.net/article/details/106874804

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市匕垫,隨后出現(xiàn)的幾起案子僧鲁,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寞秃,死亡現(xiàn)場離奇詭異斟叼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)春寿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門朗涩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绑改,你說我怎么就攤上這事谢床。” “怎么了绢淀?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵萤悴,是天一觀的道長瘾腰。 經(jīng)常有香客問我皆的,道長,這世上最難降的妖魔是什么蹋盆? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任费薄,我火速辦了婚禮,結(jié)果婚禮上栖雾,老公的妹妹穿的比我還像新娘楞抡。我一直安慰自己,他們只是感情好析藕,可當(dāng)我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布召廷。 她就那樣靜靜地躺著,像睡著了一般账胧。 火紅的嫁衣襯著肌膚如雪竞慢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天治泥,我揣著相機(jī)與錄音筹煮,去河邊找鬼。 笑死居夹,一個胖子當(dāng)著我的面吹牛败潦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播准脂,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼劫扒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了狸膏?” 一聲冷哼從身側(cè)響起沟饥,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后闷板,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體澎灸,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年遮晚,在試婚紗的時候發(fā)現(xiàn)自己被綠了性昭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡县遣,死狀恐怖糜颠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情萧求,我是刑警寧澤其兴,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站夸政,受9級特大地震影響元旬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜守问,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一匀归、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧耗帕,春花似錦穆端、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至嗽仪,卻和暖如春荒勇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钦幔。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工枕屉, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鲤氢。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓搀擂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親卷玉。 傳聞我的和親對象是個殘疾皇子哨颂,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容