widget_to_pdf_kit 1.0.1
widget_to_pdf_kit: ^1.0.1 copied to clipboard
High-performance Flutter widget to PDF generator. Rasterizes onscreen or offscreen widgets into crisp, paginated PDF documents with custom formats and scaling.
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:widget_to_pdf_kit/widget_to_pdf_kit.dart';
void main() {
runApp(const WidgetToPdfExampleApp());
}
class WidgetToPdfExampleApp extends StatelessWidget {
const WidgetToPdfExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Widget To PDF Kit Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF6366F1),
brightness: Brightness.light,
),
useMaterial3: true,
),
home: const InvoiceExportScreen(),
);
}
}
class InvoiceExportScreen extends StatefulWidget {
const InvoiceExportScreen({super.key});
@override
State<InvoiceExportScreen> createState() => _InvoiceExportScreenState();
}
class _InvoiceExportScreenState extends State<InvoiceExportScreen> {
final GlobalKey _invoiceKey = GlobalKey();
bool _isGenerating = false;
Uint8List? _lastGeneratedPdf;
String _statusMessage = 'Ready to export PDF';
Future<void> _exportOnscreenInvoice() async {
setState(() {
_isGenerating = true;
_statusMessage = 'Generating PDF from onscreen invoice...';
});
try {
final stopwatch = Stopwatch()..start();
final pdfBytes = await WidgetToPdfKit.fromKey(
_invoiceKey,
format: PdfPageFormat.a4,
pixelRatio: 2.5,
);
stopwatch.stop();
setState(() {
_lastGeneratedPdf = pdfBytes;
_statusMessage =
'Success! Generated 1-page PDF (${(pdfBytes.lengthInBytes / 1024).toStringAsFixed(1)} KB) in ${stopwatch.elapsedMilliseconds}ms';
});
} catch (e) {
setState(() {
_statusMessage = 'Error: $e';
});
} finally {
setState(() {
_isGenerating = false;
});
}
}
Future<void> _exportMultiPageOffscreen() async {
setState(() {
_isGenerating = true;
_statusMessage = 'Synthesizing multi-page offscreen report...';
});
try {
final stopwatch = Stopwatch()..start();
final page1 = _buildOffscreenReportPage(
pageNumber: 1,
title: 'Executive Summary & Financial Metrics',
body: 'Q3 gross transaction volume reached \$4.2M with a 99.98% platform uptime. Operating margin increased by 14.2% quarter-over-quarter.',
);
final page2 = _buildOffscreenReportPage(
pageNumber: 2,
title: 'User Acquisition & Global Distribution',
body: 'Active enterprise seats grew from 1,200 to 4,850. International expansion across EMEA contributed 34% of net new ARR.',
);
final pdfBytes = await WidgetToPdfKit.fromWidgets(
context,
pages: [page1, page2],
format: PdfPageFormat.a4,
pixelRatio: 2.0,
);
stopwatch.stop();
setState(() {
_lastGeneratedPdf = pdfBytes;
_statusMessage =
'Success! Generated 2-page PDF (${(pdfBytes.lengthInBytes / 1024).toStringAsFixed(1)} KB) in ${stopwatch.elapsedMilliseconds}ms';
});
} catch (e) {
setState(() {
_statusMessage = 'Error: $e';
});
} finally {
setState(() {
_isGenerating = false;
});
}
}
Widget _buildOffscreenReportPage({
required int pageNumber,
required String title,
required String body,
}) {
return Container(
color: Colors.white,
padding: const EdgeInsets.all(48),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'ACME CORP ANNUAL REPORT',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
letterSpacing: 1.5,
color: Color(0xFF6366F1),
),
),
Text(
'Page $pageNumber of 2',
style: const TextStyle(color: Colors.grey),
),
],
),
const Divider(height: 32, thickness: 1),
const SizedBox(height: 24),
Text(
title,
style: const TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
const SizedBox(height: 16),
Text(
body,
style: const TextStyle(fontSize: 15, height: 1.6, color: Colors.black54),
),
const Spacer(),
const Text(
'Confidential - Generated by widget_to_pdf_kit',
style: TextStyle(fontSize: 11, color: Colors.grey),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF8FAFC),
appBar: AppBar(
title: const Text('Widget To PDF Kit'),
centerTitle: true,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Live Onscreen Invoice Card
RepaintBoundary(
key: _invoiceKey,
child: Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.06),
blurRadius: 18,
offset: const Offset(0, 8),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'INVOICE',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
letterSpacing: 1.2,
),
),
Text(
'#INV-2026-089',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 13,
),
),
],
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: const Color(0xFF10B981).withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(8),
),
child: const Text(
'PAID',
style: TextStyle(
color: Color(0xFF10B981),
fontWeight: FontWeight.bold,
),
),
),
],
),
const Divider(height: 36),
_buildLineItem('Cloud Enterprise Subscription', '1x', '\$299.00'),
const SizedBox(height: 8),
_buildLineItem('Dedicated CDN Bandwidth (10TB)', '1x', '\$150.00'),
const SizedBox(height: 8),
_buildLineItem('Priority 24/7 SLA Support', '1x', '\$99.00'),
const Divider(height: 36),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Text(
'Total Amount',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Text(
'\$548.00',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Color(0xFF6366F1),
),
),
],
),
],
),
),
),
const SizedBox(height: 24),
// Controls
FilledButton.icon(
onPressed: _isGenerating ? null : _exportOnscreenInvoice,
icon: const Icon(Icons.picture_as_pdf),
label: const Text('Export Onscreen Invoice to PDF'),
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 14),
),
),
const SizedBox(height: 12),
OutlinedButton.icon(
onPressed: _isGenerating ? null : _exportMultiPageOffscreen,
icon: const Icon(Icons.layers_outlined),
label: const Text('Generate 2-Page Offscreen Report PDF'),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 14),
),
),
const SizedBox(height: 16),
// Status info
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.grey.shade300),
),
child: Row(
children: [
if (_isGenerating)
const Padding(
padding: EdgeInsets.only(right: 12),
child: SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(strokeWidth: 2),
),
)
else
Icon(
_lastGeneratedPdf != null
? Icons.check_circle
: Icons.info_outline,
color: _lastGeneratedPdf != null
? Colors.green
: Colors.grey,
size: 20,
),
const SizedBox(width: 8),
Expanded(
child: Text(
_statusMessage,
style: const TextStyle(fontSize: 13),
),
),
],
),
),
],
),
),
),
),
);
}
Widget _buildLineItem(String item, String qty, String price) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
item,
style: const TextStyle(fontSize: 14, color: Colors.black87),
),
),
Text(
qty,
style: const TextStyle(fontSize: 14, color: Colors.grey),
),
const SizedBox(width: 24),
Text(
price,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
),
],
);
}
}