smart_signature
A Flutter signature field that cleans up what you draw. Shaky lines come out smooth, the little hooks where the pen lands and lifts are trimmed, stray taps disappear and a tilted signature is levelled on export. Ink gets a real pen feel: thinner when you move fast, thicker when you slow down, tapered ends.
Everything runs on the device in plain Dart. It's instant, works offline and the signature never leaves the phone.
| As drawn | Enhanced |
|---|---|
![]() |
![]() |
Features
SignatureField: drop-in widget with a formatting toolbar on top, like a rich text box- Smart enhance: removes jitter, pen hooks and stray marks, refits strokes as smooth curves, straightens on export
- Ballpoint, fountain (calligraphy nib) and marker inks, each previewed in the picker
- Word style colour palette: ink colours, theme grid with tints and shades, standard colours, recent colours and a full custom picker with hex input
- Stroke width presets plus a fine slider with a live preview
- Eraser that rubs out just what you touch, or whole strokes
- Undo and redo, including clear and every erase
- Follows light and dark mode, and swaps the ink so the signature stays visible
- Export to PNG (transparent or with a background), SVG or JSON, cropped to the ink
- Stylus pressure when the device reports it
- Claims the gesture right away, so signing inside a scroll view doesn't scroll it
- Every label can be replaced for localisation
- No dependencies besides Flutter
Getting started
dependencies:
smart_signature: ^0.1.0
import 'package:smart_signature/smart_signature.dart';
Usage
The field
final signature = SignatureController();
SignatureField(controller: signature, height: 220)
Read the result whenever you need it, for example from your form's submit button:
final Uint8List? png = await signature.toPngBytes();
final String? svg = signature.toSvg();
final Map<String, Object> json = signature.toData().toJson();
Exports are cropped to the ink, enhanced and straightened. PNGs are
transparent unless you pass a background. If people sign in dark mode but
the signature goes on a white document, force the ink colour:
await signature.toPngBytes(
background: Colors.white,
inkColor: const Color(0xFF1C2A5A),
);
Load a saved signature back with
signature.load(SignatureData.fromJson(json)).
Choosing the tools
SignatureField(
controller: signature,
tools: {SignatureTool.color, SignatureTool.undo, SignatureTool.clear},
)
Pass an empty set to hide the toolbar. You can also set your own ink colours
(colors), width presets (widths) and the pad colour (padColor).
Text and localisation
The "Sign here" hint and every tooltip and panel label live in
SignatureFieldLabels:
SignatureField(
labels: const SignatureFieldLabels(
hint: 'Firme aquí',
undo: 'Deshacer',
clear: 'Borrar',
),
)
An empty hint hides it.
Just the pad
Building your own UI? SignaturePad is the bare signing surface, and the
controller holds everything else:
SignaturePad(controller: signature)
signature.style = const SignatureStyle(
color: Color(0xFF1C2A5A),
width: 2.6,
ink: InkStyle.fountain,
);
signature.enhance = false; // show exactly what was drawn
signature.erasing = true;
signature.undo();
Tuning or replacing the enhancement
signature.enhancer = const SmartEnhancer(
options: EnhanceOptions(
smoothing: 0.7, // 0 keeps the lines as drawn, 1 irons out every wobble
removeHooks: true,
removeStrayMarks: true,
straighten: true, // on export only
maxTilt: 20, // steeper slants are treated as intentional
),
);
SmartEnhancer is one implementation of SignatureEnhancer. Implement
enhance (and optionally finalize) to plug in your own, for example an ML
model.
How the enhancement works
Each stroke is resampled to an even spacing, then:
- Hooks: the first and last 14px are checked for a sharp turn, the tick a pen leaves when it lands or lifts, and trimmed there.
- Jitter: a Gaussian blur along the stroke removes the pixel level shake, with the window shrinking at the ends so strokes keep their reach.
- Refit: the line is simplified (Ramer-Douglas-Peucker) and redrawn as a centripetal Catmull-Rom spline, which flattens the slower wobble a blur leaves behind without adding loops.
- Stray marks: tiny strokes well outside the signature are dropped. Dots inside it, like on an i, stay.
- Straighten: on export, the main axis of all the ink is measured and a
tilt between 0.5° and
maxTiltis rotated level.
Steps 1 to 4 run live after every stroke. Straightening only runs on export, so ink never moves under your finger while you're still signing.
Ink width is computed per point from speed (or stylus pressure), the direction for the fountain nib, and a taper at both ends. Each stroke is then filled as a smooth outline with round caps, the same shape for the screen, PNG and SVG.
Example
The example app is a delivery receipt screen with the field and a save button. "Try sample" loads a deliberately shaky, tilted signature so you can see what Enhance does.
Contributing
Issues and pull requests are welcome on GitHub.
Author
Made by Umar Ghaffar. If this package saves you some time, a like on pub.dev or a star on GitHub is much appreciated.
Also by me: liquid_glass_navigation, iOS 26 style Liquid Glass navigation for Flutter, and scroll_edge_blur, iOS style progressive edge blur for scroll views, toolbars and headers.
Libraries
- smart_signature
- A signature pad that cleans up what you draw: smoother lines, no pen hooks or stray marks, ink-like width and a level baseline.

