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

A Flutter plugin to decrypt password-protected PDF files on Android and iOS.

example/lib/main.dart

import 'dart:async';

import 'package:decrypt_pdf/decrypt_pdf.dart'; // Import your plugin
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:pdfx/pdfx.dart';

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

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

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

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Plugin example app')),
        body: HomePage(),
      ),
    );
  }
}

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  String? _pickedFilePath;
  String? _decryptedPdfPath;
  String _status = 'Please pick a PDF file.';
  bool _isLoading = false;
  final TextEditingController _passwordController = TextEditingController();

  Future<void> _pickFile() async {
    setState(() {
      _isLoading = true;
      _status = 'Picking file...';
      _decryptedPdfPath = null; // Reset on new file pick
      _pickedFilePath = null;
    });
    try {
      FilePickerResult? result = await FilePicker.platform.pickFiles(
        type: FileType.custom,
        allowedExtensions: ['pdf'],
      );

      if (result != null && result.files.single.path != null) {
        _pickedFilePath = result.files.single.path!;
        _status = 'File picked: ${_pickedFilePath!.split('/').last}';
        // Automatically try to check if it's protected
        await _checkAndPromptForPassword();
      } else {
        _status = 'File picking cancelled.';
      }
    } catch (e) {
      _status = 'Error picking file: $e';
    } finally {
      setState(() {
        _isLoading = false;
      });
    }
  }

  Future<void> _checkAndPromptForPassword() async {
    if (_pickedFilePath == null) return;

    setState(() {
      _isLoading = true;
      _status = 'Checking if PDF is protected...';
    });

    try {
      bool isProtected = await DecryptPdf.isPdfProtected(
        filePath: _pickedFilePath!,
      );
      if (isProtected) {
        _status = 'PDF is password protected. Please enter password.';
        // ignore: use_build_context_synchronously
        _showPasswordDialog(); // Show dialog to get password
      } else {
        _status = 'PDF is not protected. Opening directly...';
        // If not protected, we can try to "open" it with an empty password
        // or directly use the original path with PDFView if it supports it.
        // For consistency with the plugin's flow, let's still use 'openPdf'.
        // The native side should handle non-protected PDFs gracefully.
        await _openPdfWithPassword(""); // Treat as opening with empty password
      }
    } catch (e) {
      _status = 'Error checking PDF protection: $e';
      // If checking fails, still allow user to try opening with password
      _showPasswordDialog();
    } finally {
      // isLoading will be set to false inside _showPasswordDialog or _openPdfWithPassword
    }
  }

  Future<void> _showPasswordDialog() async {
    // Reset loading state if it was set by _checkAndPromptForPassword
    // as the dialog itself is an interaction point.
    if (mounted) {
      setState(() {
        _isLoading = false;
      });
    }

    if (_pickedFilePath == null) return;

    _passwordController.clear(); // Clear previous password
    return showDialog<void>(
      context: context,
      barrierDismissible: false, // User must enter password or cancel
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('Enter PDF Password'),
          content: TextField(
            controller: _passwordController,
            obscureText: true,
            decoration: const InputDecoration(hintText: 'Password'),
            autofocus: true,
          ),
          actions: <Widget>[
            TextButton(
              child: const Text('Cancel'),
              onPressed: () {
                Navigator.of(context).pop();
                setState(() {
                  _status = 'Password entry cancelled.';
                });
              },
            ),
            TextButton(
              child: const Text('Open'),
              onPressed: () {
                Navigator.of(context).pop();
                _openPdfWithPassword(_passwordController.text);
              },
            ),
          ],
        );
      },
    );
  }

  Future<void> _openPdfWithPassword(String password) async {
    if (_pickedFilePath == null) {
      setState(() {
        _status = 'No file picked to open.';
      });
      return;
    }

    setState(() {
      _isLoading = true;
      _status = 'Attempting to open PDF...';
      _decryptedPdfPath = null;
    });

    try {
      final String? tempPath = await DecryptPdf.openPdf(
        filePath: _pickedFilePath!,
        password: password,
      );

      if (tempPath != null) {
        _decryptedPdfPath = tempPath;
        _status = 'PDF decrypted successfully! Path: $tempPath';
      } else {
        // This case might not happen if openPdf throws exceptions for failures
        _status = 'Failed to decrypt PDF. Temp path is null.';
      }
    } catch (e) {
      _status = 'Error opening PDF: $e';
      // If it's an invalid password, you might want to prompt again
      // For example, check e.code or e.message
      if (e.toString().toLowerCase().contains("invalid_password")) {
        _status = 'Incorrect password. Please try again.';
        // Optionally, re-show password dialog
        // _showPasswordDialog();
      }
    } finally {
      setState(() {
        _isLoading = false;
      });
    }
  }

  @override
  void dispose() {
    // Clean up temporary files if any were created and you want to manage them
    // For this example, cache files are usually managed by the OS.
    // If you create files in a persistent location, manage them here.
    // For example:
    // if (_decryptedPdfPath != null) {
    //   try {
    //     File(_decryptedPdfPath!).delete();
    //   } catch (e) {
    //     print("Error deleting temp file: $e");
    //   }
    // }
    _passwordController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: _isLoading == true ? LinearProgressIndicator() : null,
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            children: <Widget>[
              ElevatedButton.icon(
                icon: const Icon(Icons.file_open),
                label: const Text('Pick Encrypted PDF'),
                onPressed: _isLoading ? null : _pickFile,
              ),
              const SizedBox(height: 20),

              Text(_status, textAlign: TextAlign.center),
              const SizedBox(height: 10),
              if (_pickedFilePath != null &&
                  !_isLoading &&
                  _decryptedPdfPath == null &&
                  !_status.contains("PDF is not protected"))
                ElevatedButton(
                  onPressed: _showPasswordDialog,
                  child: const Text('Enter Password & Open'),
                ),
              const SizedBox(height: 20),
              if (_decryptedPdfPath != null && !_isLoading)
                Expanded(
                  child: PdfView(
                    controller: PdfController(
                      document: PdfDocument.openFile(_decryptedPdfPath!),
                      initialPage: 0,
                    ),
                    scrollDirection: Axis.vertical,
                  ),
                ),
            ],
          ),
        ),
      ),
    );
  }
}
1
likes
140
points
26
downloads

Publisher

verified publisherbhavuk.space

Weekly Downloads

A Flutter plugin to decrypt password-protected PDF files on Android and iOS.

Repository (GitHub)
View/report issues

Documentation

API reference

License

Apache-2.0 (license)

Dependencies

flutter, flutter_web_plugins, plugin_platform_interface, web

More

Packages that depend on decrypt_pdf

Packages that implement decrypt_pdf