pdf_marks 0.0.1 copy "pdf_marks: ^0.0.1" to clipboard
pdf_marks: ^0.0.1 copied to clipboard

A Flutter package for viewing and annotating PDF documents with drawing, notes, shapes, and highlights.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:pdfx/pdfx.dart';
import 'package:pdf_marks/pdf_marks.dart';
import 'package:file_picker/file_picker.dart';
import 'debug_panel.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'PDF Marks Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const PdfMarksExample(),
    );
  }
}

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

  @override
  State<PdfMarksExample> createState() => _PdfMarksExampleState();
}

class _PdfMarksExampleState extends State<PdfMarksExample> {
  PdfDocument? _document;
  bool _isLoading = false;
  bool _showDebugPanel = false;
  Map<int, List<Annotation>> _annotations = {};
  
  // Current annotation state for debugging
  AnnotationMode _currentMode = AnnotationMode.view;
  Color _currentColor = Colors.red;
  double _currentStrokeWidth = 2.0;
  ShapeType _currentShapeType = ShapeType.rectangle;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('PDF Marks Debug App'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        actions: [
          if (_document != null) ...[
            IconButton(
              icon: Icon(_showDebugPanel ? Icons.visibility_off : Icons.bug_report),
              tooltip: _showDebugPanel ? 'Hide Debug Panel' : 'Show Debug Panel',
              onPressed: () {
                setState(() {
                  _showDebugPanel = !_showDebugPanel;
                });
              },
            ),
            IconButton(
              icon: const Icon(Icons.refresh),
              tooltip: 'Reset App',
              onPressed: _resetApp,
            ),
          ],
        ],
      ),
      body: _document == null
          ? _buildLoadingView()
          : Row(
              children: [
                // Main PDF viewer
                Expanded(
                  child: PdfAnnotator(
                    document: _document!,
                    backgroundColor: Colors.grey[100]!, // Light grey background
                    onAnnotationsChanged: (annotations) {
                      setState(() {
                        _annotations = annotations;
                      });
                      debugPrint('📝 Annotations changed: ${annotations.keys.length} pages, ${_getTotalAnnotations(annotations)} total');
                    },
                  ),
                ),
                
                // Debug panel
                if (_showDebugPanel)
                  DebugPanel(
                    annotations: _annotations,
                    currentMode: _currentMode,
                    currentColor: _currentColor,
                    currentStrokeWidth: _currentStrokeWidth,
                    currentShapeType: _currentShapeType,
                    onClearAnnotations: _clearAllAnnotations,
                  ),
              ],
            ),
    );
  }

  Widget _buildLoadingView() {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          if (_isLoading) ...[
            const CircularProgressIndicator(),
            const SizedBox(height: 16),
            const Text('Loading PDF...'),
          ] else ...[
            const Icon(
              Icons.picture_as_pdf,
              size: 64,
              color: Colors.grey,
            ),
            const SizedBox(height: 16),
            const Text(
              'Load a PDF to start annotating',
              style: TextStyle(fontSize: 18),
            ),
            const SizedBox(height: 8),
            const Text(
              'đŸŽ¯ Try the Multi-Page PDF (4 pages) for better testing!',
              style: TextStyle(fontSize: 14, color: Colors.green),
            ),
            const SizedBox(height: 8),
            const Text(
              '📱 New: Auto-switch to move mode after adding annotations!',
              style: TextStyle(fontSize: 12, color: Colors.orange),
            ),
            const SizedBox(height: 24),
            ElevatedButton.icon(
              onPressed: _loadSamplePdf,
              icon: const Icon(Icons.file_open),
              label: const Text('Load Simple PDF (1 page)'),
            ),
            const SizedBox(height: 12),
            ElevatedButton.icon(
              onPressed: _loadMultiPagePdf,
              icon: const Icon(Icons.description),
              label: const Text('Load Multi-Page PDF'),
              style: ElevatedButton.styleFrom(
                backgroundColor: Colors.green,
                foregroundColor: Colors.white,
              ),
            ),
            const SizedBox(height: 16),
            OutlinedButton.icon(
              onPressed: _loadFromFile,
              icon: const Icon(Icons.folder_open),
              label: const Text('Choose Your Own PDF File'),
            ),
          ],
        ],
      ),
    );
  }

  Future<void> _loadSamplePdf() async {
    await _loadPdfFromAssets('assets/sample.pdf', 'Simple PDF');
  }

  Future<void> _loadMultiPagePdf() async {
    await _loadPdfFromAssets('assets/multi_page_sample.pdf', 'Multi-Page PDF');
  }

  Future<void> _loadPdfFromAssets(String assetPath, String displayName) async {
    setState(() {
      _isLoading = true;
    });

    try {
      debugPrint('🔄 Loading $displayName from assets...');
      
      final document = await PdfDocument.openAsset(assetPath);
      
      setState(() {
        _document = document;
        _annotations = {}; // Clear any existing annotations
      });
      
      debugPrint('✅ $displayName loaded successfully: ${document.pagesCount} pages');
      
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('📄 $displayName loaded (${document.pagesCount} pages)'),
            backgroundColor: Colors.green,
            action: SnackBarAction(
              label: 'Debug Panel',
              textColor: Colors.white,
              onPressed: () {
                setState(() {
                  _showDebugPanel = true;
                });
              },
            ),
          ),
        );
      }
    } catch (e) {
      debugPrint('❌ Failed to load $displayName: $e');
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('❌ Failed to load $displayName: $e'),
            backgroundColor: Colors.red,
          ),
        );
      }
    } finally {
      setState(() {
        _isLoading = false;
      });
    }
  }

  Future<void> _loadFromFile() async {
    try {
      debugPrint('🔄 Opening file picker...');
      
      final result = await FilePicker.platform.pickFiles(
        type: FileType.custom,
        allowedExtensions: ['pdf'],
        dialogTitle: 'Select a PDF file to annotate',
      );
      
      if (result != null && result.files.single.path != null) {
        setState(() {
          _isLoading = true;
        });
        
        final filePath = result.files.single.path!;
        final fileName = result.files.single.name;
        
        debugPrint('🔄 Loading PDF from file: $fileName');
        
        final document = await PdfDocument.openFile(filePath);
        
        setState(() {
          _document = document;
          _annotations = {}; // Clear any existing annotations
          _isLoading = false;
        });
        
        debugPrint('✅ PDF loaded from file: $fileName (${document.pagesCount} pages)');
        
        if (mounted) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(
              content: Text('📄 $fileName loaded (${document.pagesCount} pages)'),
              backgroundColor: Colors.green,
            ),
          );
        }
      } else {
        debugPrint('â„šī¸ File picker cancelled');
      }
    } catch (e) {
      debugPrint('❌ Failed to load PDF from file: $e');
      
      setState(() {
        _isLoading = false;
      });
      
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('❌ Failed to load PDF: $e'),
            backgroundColor: Colors.red,
          ),
        );
      }
    }
  }

  void _resetApp() {
    debugPrint('🔄 Resetting app state...');
    setState(() {
      _document = null;
      _annotations = {};
      _isLoading = false;
      _showDebugPanel = false;
      _currentMode = AnnotationMode.view;
      _currentColor = Colors.red;
      _currentStrokeWidth = 2.0;
      _currentShapeType = ShapeType.rectangle;
    });
    
    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('🔄 App reset successfully'),
          backgroundColor: Colors.blue,
        ),
      );
    }
  }
  
  void _clearAllAnnotations() {
    debugPrint('đŸ—‘ī¸ Clearing all annotations...');
    setState(() {
      _annotations = {};
    });
    
    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('đŸ—‘ī¸ All annotations cleared'),
          backgroundColor: Colors.orange,
        ),
      );
    }
  }
  
  int _getTotalAnnotations(Map<int, List<Annotation>> annotations) {
    return annotations.values.fold(0, (sum, list) => sum + list.length);
  }

  @override
  void dispose() {
    debugPrint('🔄 Disposing PDF Marks Example...');
    // Note: PdfDocument disposal is handled automatically by pdfx
    super.dispose();
  }
}
2
likes
140
points
11
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter package for viewing and annotating PDF documents with drawing, notes, shapes, and highlights.

License

MIT (license)

Dependencies

flutter, path_provider, pdfx

More

Packages that depend on pdf_marks