pdf_annotations 1.0.0-beta.1 copy "pdf_annotations: ^1.0.0-beta.1" to clipboard
pdf_annotations: ^1.0.0-beta.1 copied to clipboard

A plugin that enables adding text and freehand annotations to existing pdfs

example/lib/main.dart

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter_pdfview/flutter_pdfview.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pdf_annotations/pdf_annotations.dart';

void main() {
  runApp(const MaterialApp(home: MyApp()));
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _pdfPath = '';
  String _originalPdfPath = '';
  bool _isLoading = true;
  double _pdfScale = 1.0;
  Offset _pdfOffset = Offset.zero;

  @override
  void initState() {
    super.initState();
    _initPdf();
  }

  Future<void> _initPdf() async {
    try {
      final dir = await getApplicationDocumentsDirectory();
      final file = File('${dir.path}/sample.pdf');
      final modifiedFile = File('${dir.path}/sample_annotated.pdf');

      _originalPdfPath = _pdfPath = file.path;
      if (await file.exists()) {
        if (await modifiedFile.exists()) {
          _pdfPath = modifiedFile.path;
        }
      } else {
        // Copy from assets to local file system so it can be accessed by path
        final data = await rootBundle.load('assets/sample.pdf');
        final bytes = data.buffer.asUint8List();
        await file.writeAsBytes(bytes, flush: true);
      }
    } catch (e) {
      debugPrint('Error initializing PDF: $e');
    } finally {
      if (mounted) {
        setState(() {
          _isLoading = false;
        });
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: _isLoading
          ? const Scaffold(body: Center(child: CircularProgressIndicator()))
          : Scaffold(
              appBar: AppBar(
                title: const Text('PDF Annotations Example'),
                actions: [
                  IconButton(
                    icon: const Icon(Icons.edit),
                    onPressed: () =>
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) => EditPage(
                              pdfPath: _originalPdfPath,
                              pdfZoom: _pdfScale,
                              pdfOffset: _pdfOffset,
                            ),
                          ),
                        ).then((result) {
                          if (result is String) {
                            setState(() {
                              if (result.isNotEmpty) {
                                _pdfPath = result;
                              } else {
                                _pdfPath = _originalPdfPath;
                              }
                            });
                          }
                        }),
                  ),
                ],
              ),
              body: PDFView(
                key: UniqueKey(),
                filePath: _pdfPath,
                pageFling: false,
                pageSnap: false,
                autoSpacing: false,
                // showScrollIndicators: true,
                fitPolicy: FitPolicy.WIDTH,
                onDraw: (double pdfXOffset, double pdfYOffset, double pdfScale) {
                  _pdfOffset = Offset(pdfXOffset, pdfYOffset);
                  _pdfScale = pdfScale;
                },
                onPageChanged: (page, total) => debugPrint('Page: $page of $total'),
                onError: (error) => debugPrint('Error: $error'),
              ),
            ),
    );
  }
}

class EditPage extends StatefulWidget {
  const EditPage({
    super.key,
    required this.pdfPath,
    required this.pdfOffset,
    required this.pdfZoom,
  });

  final String pdfPath;
  final Offset pdfOffset;
  final double pdfZoom;

  @override
  State<EditPage> createState() => _EditPageState();
}

class _EditPageState extends State<EditPage> {
  final PdfAnnotationsViewController _controller = PdfAnnotationsViewController();

  String get _pdfPath => widget.pdfPath;

  EditMode _editMode = .pan;
  LineMode _lineMode = .pen;
  String _fontFamily = 'Work Sans';
  double _fontSize = 18.0;
  QualityValue _annotationQuality = .high;
  bool _toolsOpen = true;

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        resizeToAvoidBottomInset: false,
        appBar: AppBar(
          title: const Text('PDF Annotations Example'),
          actions: [
            IconButton(
              icon: const Icon(Icons.undo),
              onPressed: () async => await _controller.undo(),
            ),
            IconButton(
              icon: const Icon(Icons.redo),
              onPressed: () async => await _controller.redo(),
            ),
            IconButton(
              icon: const Icon(Icons.save),
              onPressed: () async {
                final pdfFonts = [
                  PdfFont(family: 'Work Sans', fileName: 'WorkSans-Regular.ttf'),
                  PdfFont(family: 'Courier Prime', fileName: 'CourierPrime-Regular.ttf'),
                ];
                await _controller.registerFonts(pdfFonts);
                await _controller.saveAnnotations();
                if (context.mounted) {
                  ScaffoldMessenger.of(
                    context,
                  ).showSnackBar(const SnackBar(content: Text('Annotations saved!')));
                  Navigator.pop(context, _controller.getBakedPath());
                }
              },
            ),
          ],
        ),
        body: _pdfPath.isEmpty
            ? const Center(child: Text('No PDF file found. Please add sample.pdf'))
            : Stack(
                children: [
                  PdfAnnotationsView(
                    pdfPath: _pdfPath,
                    startPage: 0,
                    initialOffset: widget.pdfOffset,
                    initialAnnotationColour: Colors.red,
                    initialFontSize: _fontSize,
                    initialFontFamily: _fontFamily,
                    pdfZoom: widget.pdfZoom,
                    pdfAnnotationsViewController: _controller,
                    onAnnotationQualityChanged: _onAnnotationQualityChanged,
                    onPageChanged: (page) => debugPrint('Page: $page'),
                    onError: (error) => debugPrint('Error: $error'),
                  ),
                  Positioned(top: 10, left: 10, child: _buildToolSection()),
                ],
              ),
      ),
    );
  }

  Container _buildToolSection() {
    return Container(
      color: Colors.orange.withAlpha(210),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          IconButton(
            onPressed: () => setState(() {
              _toolsOpen = !_toolsOpen;
            }),
            icon: Icon(_toolsOpen ? Icons.close_fullscreen : Icons.fullscreen),
          ),
          _toolsOpen
              ? Row(
                  children: [
                    Column(
                      mainAxisAlignment: .center,
                      children: [
                        SegmentedButton<QualityValue>(
                          segments: <ButtonSegment<QualityValue>>[
                            ButtonSegment<QualityValue>(value: .low, label: const Text('Low')),
                            ButtonSegment<QualityValue>(value: .high, label: const Text('High')),
                          ],
                          selected: <QualityValue>{_annotationQuality},
                          onSelectionChanged: (Set<QualityValue> newSelection) {
                            setState(() {
                              _annotationQuality = newSelection.first;
                            });
                            _controller.setAnnotationQuality(_annotationQuality);
                          },
                        ),
                        SegmentedButton<EditMode>(
                          segments: const <ButtonSegment<EditMode>>[
                            ButtonSegment<EditMode>(
                              value: .draw,
                              label: Text('Draw'),
                              icon: Icon(Icons.edit),
                            ),
                            ButtonSegment<EditMode>(
                              value: .text,
                              label: Text('Text'),
                              icon: Icon(Icons.text_fields),
                            ),
                            ButtonSegment<EditMode>(
                              value: .pan,
                              label: Text('Pan'),
                              icon: Icon(Icons.pan_tool),
                            ),
                          ],
                          selected: <EditMode>{_editMode},
                          onSelectionChanged: (Set<EditMode> newSelection) {
                            setState(() {
                              _editMode = newSelection.first;
                            });
                            _controller.setEditMode(_editMode);
                            if (_editMode == .text) {
                              _controller.setFontFamily(_fontFamily);
                            }
                          },
                        ),
                        _editMode == .draw
                            ? SegmentedButton<LineMode>(
                                segments: <ButtonSegment<LineMode>>[
                                  ButtonSegment<LineMode>(value: .pen, label: const Text('Pen')),
                                  ButtonSegment<LineMode>(
                                    value: .highlighter,
                                    label: const Text('Highlighter'),
                                  ),
                                ],
                                selected: <LineMode>{_lineMode},
                                onSelectionChanged: (Set<LineMode> newSelection) {
                                  setState(() {
                                    _lineMode = newSelection.first;
                                  });
                                  _controller.setLineMode(_lineMode);
                                },
                              )
                            : _editMode == .text
                            ? Column(
                                mainAxisSize: .min,
                                children: [
                                  SegmentedButton<String>(
                                    segments: <ButtonSegment<String>>[
                                      ButtonSegment<String>(
                                        value: 'Work Sans',
                                        label: const Text('Work Sans'),
                                      ),
                                      ButtonSegment<String>(
                                        value: 'Courier Prime',
                                        label: const Text('Courier'),
                                      ),
                                    ],
                                    selected: <String>{_fontFamily},
                                    onSelectionChanged: (Set<String> newSelection) {
                                      setState(() {
                                        _fontFamily = newSelection.first;
                                      });
                                      _controller.setFontFamily(_fontFamily);
                                    },
                                  ),
                                  SizedBox(
                                    width: 300,
                                    child: Slider(
                                      min: 10.0,
                                      max: 40.0,
                                      divisions: 30,
                                      value: _fontSize,
                                      onChanged: (newSize) {
                                        setState(() {
                                          _fontSize = newSize;
                                        });
                                        _controller.setFontSize(_fontSize);
                                      },
                                    ),
                                  ),
                                ],
                              )
                            : SizedBox.shrink(),
                      ],
                    ),
                    const SizedBox(width: 40),
                  ],
                )
              : SizedBox.shrink(),
        ],
      ),
    );
  }

  void _onAnnotationQualityChanged(QualityValue quality) {
    setState(() {
      _annotationQuality = quality;
    });
  }
}
0
likes
0
points
103
downloads

Publisher

unverified uploader

Weekly Downloads

A plugin that enables adding text and freehand annotations to existing pdfs

Homepage

License

unknown (license)

Dependencies

collection, equatable, flutter, flutter_pdfview, keyboard_height_plugin, logger, meta, nanoid, native_device_orientation, path, path_provider, transparent_pointer

More

Packages that depend on pdf_annotations

Packages that implement pdf_annotations