Flutter + Cloud2Pdf

Integração completa para geração de PDFs em apps Flutter/Dart

📱 iOS/Android 🌐 Web Support ⚡ HTTP Package

🚀 Quick Start

1

Adicionar Dependência

Em pubspec.yaml:

dependencies: http: ^1.1.0
2

Criar Service

Copie o código da seção abaixo

3

Usar no Widget

final pdfService = Cloud2PdfService('seu_token_aqui'); final result = await pdfService.generateAsync('<h1>Hello!</h1>', 'ref-123');

📦 Cloud2PdfService Class

Crie lib/services/cloud2pdf_service.dart

import 'dart:convert'; import 'dart:typed_data'; import 'package:http/http.dart' as http; class Cloud2PdfService { final String apiUrl; final String apiToken; Cloud2PdfService( this.apiToken, { this.apiUrl = 'https://api.cloud2pdf.com/v1', }); Map<String, String> get _headers => { 'Authorization': 'Bearer $apiToken', 'Content-Type': 'application/json', 'Accept': 'application/json', }; /// Gera PDF de forma assíncrona Future<Map<String, dynamic>> generateAsync( String html, { String? reference, String format = 'A4', bool landscape = false, bool useCdn = true, Map<String, dynamic>? margins, }) async { final body = { 'html': html, 'is_disk': useCdn, 'reference': reference, 'options': { 'format': format, 'landscape': landscape, if (margins != null) 'margins': margins, } }; final response = await http.post( Uri.parse('$apiUrl/pdf/generate-async'), headers: _headers, body: jsonEncode(body), ); if (response.statusCode == 202) { return jsonDecode(response.body); } else { throw Exception('Failed to generate PDF: ${response.statusCode}'); } } /// Verifica status do PDF Future<Map<String, dynamic>> checkStatus(int pdfId) async { final response = await http.get( Uri.parse('$apiUrl/pdf/status/$pdfId'), headers: _headers, ); if (response.statusCode == 200) { return jsonDecode(response.body); } else { throw Exception('Failed to check status: ${response.statusCode}'); } } /// Aguarda conclusão do PDF (polling) Future<Map<String, dynamic>> waitForCompletion( int pdfId, { int maxAttempts = 60, Duration delay = const Duration(seconds: 5), }) async { for (var i = 0; i < maxAttempts; i++) { final status = await checkStatus(pdfId); if (status['status'] == 'completed') { return status; } if (status['status'] == 'failed') { throw Exception('PDF generation failed: ${status['error']}'); } await Future.delayed(delay); } throw Exception('PDF generation timeout'); } /// Baixa PDF como bytes Future<Uint8List> download(int pdfId) async { final response = await http.get( Uri.parse('$apiUrl/pdf/download/$pdfId'), headers: {'Authorization': 'Bearer $apiToken'}, ); if (response.statusCode == 200) { return response.bodyBytes; } else { throw Exception('Failed to download PDF: ${response.statusCode}'); } } /// Gera PDF síncrono (apenas PDFs pequenos!) Future<Map<String, dynamic>> generateSync( String html, { String format = 'A4', bool landscape = false, }) async { final body = { 'html': html, 'is_disk': false, 'options': { 'format': format, 'landscape': landscape, } }; final response = await http.post( Uri.parse('$apiUrl/pdf/generate'), headers: _headers, body: jsonEncode(body), ); if (response.statusCode == 200) { return jsonDecode(response.body); } else { throw Exception('Failed to generate PDF: ${response.statusCode}'); } } }

📱 Exemplo Completo no Widget

import 'package:flutter/material.dart'; import 'services/cloud2pdf_service.dart'; class PdfGeneratorScreen extends StatefulWidget { @override _PdfGeneratorScreenState createState() => _PdfGeneratorScreenState(); } class _PdfGeneratorScreenState extends State<PdfGeneratorScreen> { final _pdfService = Cloud2PdfService('seu_token_aqui'); final _htmlController = TextEditingController(); final _referenceController = TextEditingController(); int? _pdfId; String _status = ''; bool _isLoading = false; @override void dispose() { _htmlController.dispose(); _referenceController.dispose(); super.dispose(); } Future<void> _generatePdf() async { setState(() { _isLoading = true; _status = 'Generating...'; }); try { final result = await _pdfService.generateAsync( _htmlController.text, reference: _referenceController.text, format: 'A4', landscape: false, margins: { 'top': '20mm', 'right': '15mm', 'bottom': '20mm', 'left': '15mm', }, ); setState(() { _pdfId = result['pdf_id']; _status = 'PDF ID: $_pdfId - Status: processing'; _isLoading = false; }); // Aguardar conclusão em background _waitForPdf(); } catch (e) { setState(() { _status = 'Error: $e'; _isLoading = false; }); } } Future<void> _waitForPdf() async { if (_pdfId == null) return; try { final completed = await _pdfService.waitForCompletion(_pdfId!); setState(() { _status = 'PDF Ready! URL: ${completed['public_link']['url']}'; }); // Mostrar dialog showDialog( context: context, builder: (context) => AlertDialog( title: Text('PDF Ready!'), content: Text('PDF generated successfully!'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('OK'), ), TextButton( onPressed: () { Navigator.pop(context); _downloadPdf(); }, child: Text('Download'), ), ], ), ); } catch (e) { setState(() { _status = 'Error: $e'; }); } } Future<void> _checkStatus() async { if (_pdfId == null) return; try { final status = await _pdfService.checkStatus(_pdfId!); setState(() { _status = 'Status: ${status['status']}'; }); } catch (e) { setState(() { _status = 'Error: $e'; }); } } Future<void> _downloadPdf() async { if (_pdfId == null) return; setState(() { _isLoading = true; _status = 'Downloading...'; }); try { final bytes = await _pdfService.download(_pdfId!); // Salvar arquivo ou compartilhar // Use path_provider e share_plus packages setState(() { _status = 'Downloaded ${bytes.length} bytes'; _isLoading = false; }); } catch (e) { setState(() { _status = 'Error: $e'; _isLoading = false; }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('PDF Generator'), ), body: Padding( padding: EdgeInsets.all(16), child: Column( children: [ TextField( controller: _htmlController, decoration: InputDecoration( labelText: 'HTML Content', border: OutlineInputBorder(), ), maxLines: 5, ), SizedBox(height: 16), TextField( controller: _referenceController, decoration: InputDecoration( labelText: 'Reference (optional)', border: OutlineInputBorder(), ), ), SizedBox(height: 16), Row( children: [ Expanded( child: ElevatedButton( onPressed: _isLoading ? null : _generatePdf, child: Text('Generate PDF'), ), ), SizedBox(width: 8), ElevatedButton( onPressed: _pdfId == null ? null : _checkStatus, child: Text('Check Status'), ), SizedBox(width: 8), ElevatedButton( onPressed: _pdfId == null ? null : _downloadPdf, child: Text('Download'), ), ], ), SizedBox(height: 16), if (_isLoading) CircularProgressIndicator(), SizedBox(height: 16), Text(_status), ], ), ), ); } }

📥 Salvar PDF no Dispositivo

Use path_provider e permission_handler

pubspec.yaml

dependencies: path_provider: ^2.1.1 permission_handler: ^11.0.1 share_plus: ^7.2.1
import 'dart:io'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:share_plus/share_plus.dart'; Future<void> downloadAndSavePdf(int pdfId) async { final pdfService = Cloud2PdfService('seu_token_aqui'); // Solicitar permissão de storage (Android) if (Platform.isAndroid) { final status = await Permission.storage.request(); if (!status.isGranted) { throw Exception('Storage permission denied'); } } // Baixar PDF final bytes = await pdfService.download(pdfId); // Obter diretório final directory = Platform.isAndroid ? await getExternalStorageDirectory() : await getApplicationDocumentsDirectory(); // Salvar arquivo final file = File('${directory!.path}/document-$pdfId.pdf'); await file.writeAsBytes(bytes); print('PDF saved: ${file.path}'); // Compartilhar (opcional) await Share.shareXFiles([XFile(file.path)], text: 'Check out this PDF!'); }

💡 Dicas

Use FutureBuilder para UI Reativa

Mostre loading states durante geração

Isolates para Tarefas Pesadas

Use compute() para não bloquear a UI

Gerenciamento de Estado

Use Provider, Riverpod ou Bloc para state management

Permissões no Android/iOS

Configure AndroidManifest.xml e Info.plist para storage