Integração completa para geração de PDFs em apps Flutter/Dart
Em pubspec.yaml:
dependencies:
http: ^1.1.0
Copie o código da seção abaixo
final pdfService = Cloud2PdfService('seu_token_aqui');
final result = await pdfService.generateAsync('<h1>Hello!</h1>', 'ref-123');
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}');
}
}
}
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),
],
),
),
);
}
}
Use path_provider e permission_handler
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!');
}
Mostre loading states durante geração
Use compute() para não bloquear a UI
Use Provider, Riverpod ou Bloc para state management
Configure AndroidManifest.xml e Info.plist para storage