annotation_for_all 0.1.0
annotation_for_all: ^0.1.0 copied to clipboard
A standalone Flutter toolkit for annotating PDFs and images: freehand drawing, highlight, underline, grading marks (tick, cross, circle, question mark, number stamps), image insertion, and undo/redo. [...]
annotation_for_all #
A standalone Flutter toolkit for annotating PDFs: freehand drawing, highlight, underline, grading marks (tick / cross / circle / question mark / number stamps), and image insertion.
You hand it a PDF, the user annotates it, you get the annotated PDF back as bytes. That's it β no backend, no storage assumptions, no accounts. What you do with the bytes (save locally, upload, email, whatever) is entirely up to your app.

Features #
- βοΈ Freehand drawing with a color picker
- ποΈ Highlight and underline selected text
- β Grading marks: tick, cross, circle, question mark, number stamps (handy for marking student submissions)
- πΌοΈ Image insertion (from gallery, auto-compressed if large)
- πΌοΈβ‘οΈπ Photos/scans work too β
FileAnnotator/ImageToPdfwrap an image as a one-page PDF automatically, so it goes through the same annotator - π
FileAnnotator.openhandles "whatever the user tapped" generically β PDF or image, it annotates; anything else, it offers View/Download instead - π Add/remove blank pages (mobile/desktop only β see Platform notes)
- β©οΈ Undo/redo and a one-tap reset
- Works on Android, iOS, and Web
Install #
dependencies:
annotation_for_all: ^0.1.0
Quick start #
The simplest way β push the editor and await the result:
import 'package:annotation_for_all/annotation_for_all.dart';
final bytes = await PdfAnnotator.open(context, pdfFile: myPdfFile);
if (bytes != null) {
// user saved β bytes is the fully annotated PDF
await File('checked.pdf').writeAsBytes(bytes);
} else {
// user cancelled without saving
}
On web, pass pdfBytes instead of pdfFile (there's no writable file system
in the browser):
final bytes = await PdfAnnotator.open(context, pdfBytes: myPdfBytes);
Grading workflow #
enableMarksDialog is off by default β apps that aren't grading
submissions (a banking app annotating a statement, say) never see this
dialog unless they opt in. If you are grading, turn it on and optionally
set totalMarks for the "out of N" display; Save will then ask for a mark
before handing you both:
await Navigator.push(context, MaterialPageRoute(
builder: (context) => PdfAnnotatorScreen(
pdfFile: submissionFile,
enableMarksDialog: true,
totalMarks: 100, // optional β omit for an open-ended mark with no total shown
onSaved: (bytes, {enteredMarks}) {
// enteredMarks is e.g. "87" β upload bytes + save the mark however you like
Navigator.pop(context);
},
),
));
Even with the dialog on, entering a mark is never mandatory β it always offers "Skip & Save" alongside "Save & Submit", so the person actually using the app decides whether they want to bother with it.
Turning off tools you don't need #
Every capability is an independent flag:
PdfAnnotatorScreen(
pdfFile: file,
enableDraw: true,
enableHighlight: true,
enableUnderline: true,
enableImage: false, // no image insertion
enablePageManagement: false,
onSaved: (bytes, {enteredMarks}) { /* ... */ },
)
Choosing which grading marks to show #
enabledMarks controls the marks toolbar individually β pass just the
ones you want (e.g. only Tick and Cross, no number/circle/question mark):
PdfAnnotatorScreen(
pdfFile: file,
enabledMarks: {MarkType.rightTick, MarkType.incorrectCross},
onSaved: (bytes, {enteredMarks}) { /* ... */ },
)
Leave it unset for all five (tick, cross, number, circle, question mark), or
pass an empty set ({}) to hide the marks toolbar entirely.
One entry point for "whatever the user tapped" #
If you're handling arbitrary attachments and don't know ahead of time whether
it's a PDF, an image, or something else entirely, use FileAnnotator.open
instead of PdfAnnotator.open:
final result = await FileAnnotator.open(
context,
bytes: fileBytes, // or `file: File(...)` off web
fileName: 'homework.docx',
);
switch (result.handledAs) {
case FileHandledAs.annotated:
// it was a PDF, or an image that got wrapped into one β result.annotatedBytes
break;
case FileHandledAs.viewed:
case FileHandledAs.downloaded:
// wasn't a PDF/image β the user viewed or downloaded it instead
break;
case FileHandledAs.cancelled:
break;
}
What it does, based on the file's extension:
.pdfβ opens directly in the annotator.- Images (jpg/jpeg/png/gif/bmp/webp) β wrapped as a one-page PDF first
(via
ImageToPdf.convert, also usable on its own), then opens the same annotator β so a photographed/scanned submission gets the exact same drawing/marks/highlight tools as a real PDF. - Anything else (docx, xlsx, ...) β there's no in-app renderer for arbitrary file types, so instead of failing silently, the user is asked to View (opens with whatever the OS/browser has registered for that file type) or Download (saves it) β nothing is annotated in this case.
Platform notes #
-
Web requires a one-time
index.htmlsetup step. The PDF viewer renders pages on web using the PDF.js library, which isn't bundled automatically β you must add this to your app'sweb/index.html, inside<body>, before the Flutter bootstrap script:<script type="module" async> import * as pdfjsLib from 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.9.155/pdf.min.mjs'; pdfjsLib.GlobalWorkerOptions.workerSrc = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.9.155/pdf.worker.min.mjs"; </script>Without this, every PDF fails to open on web with a generic "There was an error opening this document" message β the bytes are fine, the viewer just has no renderer to hand them to. See syncfusion_flutter_pdfviewer's web docs if you need a different PDF.js version.
-
Page add/remove is mobile/desktop only. There's no writable file system on web, and page management works against a
Fileβ the flag is silently ignored on web rather than crashing. -
iOS requires a photo library usage description. The Image tool uses
image_picker, and iOS crashes on first use if your app'sInfo.plistdoesn't declare why it needs photo library access. Add this to your ownios/Runner/Info.plist(not something this package can add for you):<key>NSPhotoLibraryUsageDescription</key> <string>Used to insert a photo into the document you're annotating.</string> -
Syncfusion license: this package renders and edits PDFs using
syncfusion_flutter_pdf/syncfusion_flutter_pdfviewer, which are free to use under Syncfusion's Community License for qualifying individuals and small businesses (revenue/team-size thresholds apply) β check syncfusion.com/license if your usage might fall outside that. This package can't work without them, so that license's terms carry through to anything that depends on it.
Attribution #
The annotation controllers (drawing, highlight, underline, image, save) are
adapted from the nextgen_pdf_editor
package (BSD-3-Clause, Β© Asint.net). See NOTICE for details. The grading
marks feature and the top-level API are original to this package.
License #
BSD-3-Clause β see LICENSE.