Dependencies
Copy
dependencies:
http: ^1.1.0
path_provider: ^2.1.0
open_file: ^3.3.0
Client
Copy
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
class FileloomClient {
final String apiKey;
final String baseUrl;
FileloomClient({required this.apiKey, this.baseUrl = 'https://api.fileloom.io/v1'});
Future<PdfResult> generatePdf({
String? html,
String? templateId,
Map<String, dynamic>? templateData,
String? filename,
}) async {
final payload = <String, dynamic>{};
if (templateId != null) payload['templateId'] = templateId;
else if (html != null) payload['htmlContent'] = html;
if (templateData != null) payload['templateData'] = templateData;
if (filename != null) payload['filename'] = filename;
final response = await http.post(
Uri.parse('$baseUrl/pdf/generate'),
headers: {'X-API-Key': apiKey, 'Content-Type': 'application/json'},
body: jsonEncode(payload),
).timeout(const Duration(seconds: 60));
final data = jsonDecode(response.body);
if (response.statusCode != 200) {
throw FileloomException(
message: data['error']?['message'] ?? 'Unknown error',
code: data['error']?['code'] ?? 'UNKNOWN',
);
}
return PdfResult.fromJson(data);
}
Future<File> downloadPdf(String url, String filePath) async {
final response = await http.get(Uri.parse(url));
final file = File(filePath);
await file.writeAsBytes(response.bodyBytes);
return file;
}
}
class PdfResult {
final String fileId, url, filename;
final int size, remainingCredits;
PdfResult({required this.fileId, required this.url, required this.filename,
required this.size, required this.remainingCredits});
factory PdfResult.fromJson(Map<String, dynamic> json) => PdfResult(
fileId: json['data']['fileId'],
url: json['data']['url'],
filename: json['data']['filename'],
size: json['data']['size'],
remainingCredits: json['usage']['remaining'],
);
}
class FileloomException implements Exception {
final String message, code;
FileloomException({required this.message, required this.code});
}
Usage
Copy
final client = FileloomClient(
apiKey: const String.fromEnvironment('FILELOOM_API_KEY'),
);
// Generate from HTML
final result = await client.generatePdf(
html: '<h1>Hello World</h1>',
filename: 'hello.pdf',
);
print('URL: ${result.url}');
// Generate from template
final invoice = await client.generatePdf(
templateId: 'tpl_invoice_v2',
templateData: {
'invoiceNumber': 'INV-001',
'customer': {'name': 'Acme Corp'},
'items': [{'description': 'Web Dev', 'quantity': 10, 'price': 150}],
'total': 1500,
},
filename: 'invoice.pdf',
);
Download and Open
Copy
import 'package:path_provider/path_provider.dart';
import 'package:open_file/open_file.dart';
Future<void> generateAndOpen() async {
final result = await client.generatePdf(html: '<h1>Report</h1>');
final dir = await getTemporaryDirectory();
await client.downloadPdf(result.url, '${dir.path}/report.pdf');
await OpenFile.open('${dir.path}/report.pdf');
}
Widget
Copy
class PdfButton extends StatefulWidget {
final String templateId;
final Map<String, dynamic> data;
const PdfButton({required this.templateId, required this.data});
@override
State<PdfButton> createState() => _PdfButtonState();
}
class _PdfButtonState extends State<PdfButton> {
bool _loading = false;
Future<void> _generate() async {
setState(() => _loading = true);
try {
final result = await client.generatePdf(
templateId: widget.templateId,
templateData: widget.data,
);
final dir = await getTemporaryDirectory();
await client.downloadPdf(result.url, '${dir.path}/doc.pdf');
await OpenFile.open('${dir.path}/doc.pdf');
} finally {
setState(() => _loading = false);
}
}
@override
Widget build(BuildContext context) => ElevatedButton(
onPressed: _loading ? null : _generate,
child: Text(_loading ? 'Generating...' : 'Download PDF'),
);
}

