annotation_for_all 0.1.0 copy "annotation_for_all: ^0.1.0" to clipboard
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.

A quiz PDF annotated with tick/cross marks and a freehand circle

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/ImageToPdf wrap an image as a one-page PDF automatically, so it goes through the same annotator
  • πŸ“Ž FileAnnotator.open handles "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.html setup 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's web/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's Info.plist doesn't declare why it needs photo library access. Add this to your own ios/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.

0
likes
130
points
102
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

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. Returns the annotated PDF as bytes β€” no backend or storage assumptions.

Repository (GitHub)
View/report issues

Topics

#pdf #annotation #drawing #markup #grading

License

BSD-3-Clause (license)

Dependencies

flutter, flutter_colorpicker, image_picker, open_file, path_provider, syncfusion_flutter_pdf, syncfusion_flutter_pdfviewer, universal_html

More

Packages that depend on annotation_for_all