# photo_markup: vector photo annotation for Flutter

> Draw boxes, ellipses, arrows, freehand strokes and text over a photo, read it back as resolution-independent vector data, and flatten it to JPEG or PNG.

Source: https://ahmedprime.dev/projects/photo-markup

# photo_markup

- Status

  Released

- Platforms

  All six Flutter platforms

- Version

  1.0.2 on pub.dev

- Pub points

  160/160

- Since

  August 2026

- Publisher

  Verified publisher velzosoft.com

- Stack

  Dart · Flutter

- Last shipped

  2026-08-13

- Links

  - [pub.dev](https://pub.dev/packages/photo_markup)
  - [GitHub](https://github.com/velzosoft/photo_markup)

Why does the arrow on your inspection photo point at the wrong thing on another screen?

Because it was saved in pixels.

photo_markup stores every mark as a fraction of the image. The same markup renders correctly in a 48-pixel thumbnail, a full-screen tablet editor and a 4,000-pixel export.

![A photo_markup export: a red box around a house, a blue arrow at a crack in its wall, an orange ellipse around the sun and a green freehand line, flattened onto the example app's sample picture.](/_astro/photo-markup-flattened.DKjrWMUw_2qDzk0.png)

Box, ellipse, arrow and freehand marks, flattened by MarkupFlattener into a 1,000-pixel PNG. No text mark: it's rendered under flutter test, where every font is a placeholder.

## Decisions

- **Vector data is the source of truth.** The flattened image is a derived artifact.
- **One painter for the editor, the viewer and the export,** so all three produce identical geometry.
- **Forward-compatible both ways.** An older app can open and re-save a newer document without destroying what it doesn't understand.
- **No `fit` option on the viewer, on purpose.** Letterboxing would misalign the markup. Wrap it in a `FittedBox` instead.
- **A "not supported yet" list** in the README.

## Under the hood

- Box, ellipse, arrow, freehand and text tools. Pinch-zoom, pan, and one-finger drawing while zoomed. Undo and redo.
- Exports draw at output resolution, never upscale, and cap size by default, because a 12-megapixel image in memory is about 48 MB.
- JPEG encoding runs off the main thread. No platform channels.
- 37 tests.

* [pub.dev/packages/photo_markup](https://pub.dev/packages/photo_markup)
* [github.com/velzosoft/photo_markup](https://github.com/velzosoft/photo_markup)

**Still have questions?** [Ask the agent about this page](/ask?about=photo-markup)

Or [write to Ahmed](/contact)
