watermark_kit 2.0.0
watermark_kit: ^2.0.0 copied to clipboard
Fast watermarking for Flutter (iOS + Android). Image/text overlays for images and videos without FFmpeg (Core Image/AVFoundation on iOS; MediaCodec + GLES on Android).
watermark_kit #
Lightweight, fast watermarking plugin for Flutter (iOS + Android). Compose image or text watermarks over images and videos, with a simple cross‑platform API and no FFmpeg dependency. Under the hood: iOS uses Core Image + AVFoundation; Android uses MediaCodec + OpenGL ES.
Platform Support #
- iOS: images, text, video
- Android: images, text, video (API 24+)
Install #
In your app's pubspec.yaml
:
dependencies:
watermark_kit: ^2.0.0
Import and create the API:
import 'package:watermark_kit/watermark_kit.dart';
final wm = WatermarkKit();
Quick Start #
Image watermark (PNG/JPEG bytes → bytes):
final bytes = await wm.composeImage(
inputImage: baseBytes, // Uint8List (PNG/JPEG/HEIF usually supported by iOS)
watermarkImage: wmBytes, // Uint8List with alpha support (e.g., PNG)
anchor: 'bottomRight', // 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'center'
margin: 0.05, // 5% margin when marginUnit == 'percent'
marginUnit: 'percent', // 'px' | 'percent' (default: 'px')
widthPercent: 0.18, // watermark width = 18% of base width
opacity: 0.6, // 0..1 applied to watermark alpha
offsetX: 0.0, // additional shift from anchor (see offsetUnit)
offsetY: 0.0,
offsetUnit: 'px', // 'px' | 'percent' (default: 'px')
format: 'png', // 'png' | 'jpeg'
quality: 0.9, // JPEG only
);
Write to a file if you want:
final file = File('/tmp/composed.png');
await file.writeAsBytes(bytes);
See the runnable example/
app for a simple UI and usage.
Text watermark (text → bytes):
final textBytes = await wm.composeTextImage(
inputImage: baseBytes,
text: '© watermark kit',
anchor: 'bottomRight',
margin: 16.0,
widthPercent: 0.18, // target text width = 18% of base width
opacity: 0.6, // global alpha after raster
format: 'jpeg', // or 'png'
fontFamily: '.SFUI', // optional
fontSizePt: 24.0, // used if widthPercent is not provided
fontWeight: 600, // 100..900
colorArgb: 0xFFFFFFFF, // ARGB32
);
Video #
Add a text or image watermark to every frame of a video and export an MP4 file. The call returns a VideoTask
with a progress
stream and a done
future.
Text watermark example:
final task = await wm.composeVideo(
inputVideoPath: '/path/in.mp4',
text: '© watermark_kit', // use `text` OR `watermarkImage`
anchor: 'bottomRight', // 'topLeft'|'topRight'|'bottomLeft'|'bottomRight'|'center'
margin: 16.0, // px (use 'percent' in marginUnit for relative)
marginUnit: 'px', // 'px' | 'percent'
widthPercent: 0.18, // watermark width = 18% of video display width
opacity: 0.6, // 0..1
codec: 'h264', // or 'hevc' when supported
);
task.progress.listen((p) => print('progress: ${(p * 100).toStringAsFixed(0)}%'));
final res = await task.done;
print('Output: ${res.path} (${res.width}x${res.height})');
Image watermark example:
final wmBytes = await File('/path/wm.png').readAsBytes();
final task = await wm.composeVideo(
inputVideoPath: '/path/in.mp4',
watermarkImage: wmBytes, // PNG with alpha recommended
anchor: 'topLeft',
margin: 24.0,
widthPercent: 0.15,
opacity: 0.8,
);
final res = await task.done;
Cancelling:
final task = await wm.composeVideo(inputVideoPath: '/path/in.mp4', text: '© …');
final sub = task.progress.listen((p) { /* update UI */ });
// ... some condition
await task.cancel();
await sub.cancel();
Options (quick reference):
inputVideoPath
(String, required): input MP4 path (H.264/HEVC).watermarkImage
(Uint8List?) ortext
(String?): choose either image or text.anchor
(String): 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'center'. Default 'bottomRight'.margin
(double) +marginUnit
('px'|'percent'): distance from edges at the anchor. Default 16.0 px.offsetX
/offsetY
(double) +offsetUnit
('px'|'percent'): fine adjustments. Default 0.widthPercent
(double): watermark width relative to video display width. Default 0.18.opacity
(double): 0..1, multiplies watermark alpha. Default 0.6.codec
('h264'|'hevc'): default 'h264'.
Notes:
- Default codec is H.264; set
codec: 'hevc'
when supported by the device. - Audio is passed through on a best‑effort basis.
- Anchors apply to the displayed orientation (rotation is handled internally on both iOS and Android).
- See the example app’s “Video” tab for end‑to‑end usage.
API Reference #
Method: Future<Uint8List> composeImage({...})
Parameters:
inputImage
(Uint8List, required): Base image bytes. PNG/JPEG recommended (HEIF may work depending on iOS codecs).watermarkImage
(Uint8List, required): Watermark image bytes. PNG with transparency recommended.anchor
(String): One oftopLeft
,topRight
,bottomLeft
,bottomRight
,center
. Default:bottomRight
.margin
(double): Margin from the edges around the anchor. Default:16.0
.marginUnit
(String):'px'
or'percent'
. Default:'px'
.- If
'percent'
: horizontal margin =margin * baseWidth
, vertical margin =margin * baseHeight
.
- If
widthPercent
(double): Target watermark width as a fraction of the base width (0..1). Default:0.18
.opacity
(double): 0..1, multiplies watermark alpha. Default:0.6
.offsetX
,offsetY
(double): Additional X/Y offset from the anchored position. Default:0.0
.offsetUnit
(String):'px'
or'percent'
(applies to both X and Y). Default:'px'
.format
(String):'png'
or'jpeg'
. Default:'jpeg'
.quality
(double): JPEG quality (0..1). Default:0.9
.
Returns:
Uint8List
— encoded output image.
Errors:
- Throws
PlatformException
with codes likedecode_failed
,invalid_image
,encode_failed
,compose_failed
on native failures.
Method: Future<Uint8List> composeTextImage({...})
Parameters (in addition to placement/format options shared with composeImage
):
text
(String, required): Watermark text (single line in MVP).widthPercent
(double): Fit rendered text width to a fraction of base width (0..1). Default0.18
.fontFamily
(String): System or custom registered font name. Default.SFUI
.fontSizePt
(double): Point size if you prefer absolute sizing; ignored whenwidthPercent
is provided.fontWeight
(int): 100..900; mapped to iOS font weights. Default 600 (semibold).colorArgb
(int): ARGB32 color; default white.opacity
(double): 0..1 applied to the rendered text alpha.
Returns: Uint8List
— encoded output image.
Notes & Tips #
- Watermark scaling is based on the base image width:
outputWatermarkWidth = widthPercent * baseWidth
. - Alpha is preserved for PNG watermark images.
- The plugin itself does not request platform permissions. Your app is responsible for any file/media permissions needed to read input or write the output file (e.g., READ_MEDIA_VIDEO on Android 13+ if accessing shared storage).
Limitations #
- SVG overlays are not implemented yet.
- Text is single-line in the current MVP (no wrapping).
- Android HEVC and Dolby Vision availability varies by device; the example falls back to software decode when needed.
Example App #
example/
includes a minimal UI that lets you pick a base image and a watermark image, tweak options, and preview the result. It is intended for manual testing and does not represent best-practice UI.
Development #
This plugin uses Pigeon for a type-safe platform bridge. Generated files live under lib/gen/
(Dart), ios/Classes/
(Swift), and android/src/main/kotlin/...
(Kotlin). When changing the schema in pigeons/messages.dart
, re‑generate via:
dart run pigeon --input pigeons/messages.dart
License #
See LICENSE.