Dart PDF
last modified January 28, 2024
In this article we show how to create PDF documents in Dart language.
Portable Document Format (PDF) is a versatile file format created by Adobe that gives people an easy, reliable way to present and exchange documents.
$ dart pub add pdf
To produce PDF files in Dart, we use the pdf package.
Dart PDF simple example
The following is a simple Dart example which produces a PDF document.
import 'dart:io';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
void main() async {
final pdf = pw.Document();
pdf.addPage(pw.Page(
pageFormat: PdfPageFormat.a4,
build: (pw.Context context) {
return pw.Center(
child: pw.Text("An old falcon"),
);
}));
final file = File('simple.pdf');
await file.writeAsBytes(await pdf.save());
}
The program creates a new PDF file and writes it to the disk. It contains centered text.
import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw;
We import the required packages.
final pdf = pw.Document();
A new empty document is created.
pdf.addPage(pw.Page(
pageFormat: PdfPageFormat.a4,
build: (pw.Context context) {
return pw.Center(
child: pw.Text("An old falcon"),
);
}));
A page is added with addPage. We provide the page format and the
content. The content consists of a Text widged which is centered
on the page with Center.
final file = File('simple.pdf');
await file.writeAsBytes(await pdf.save());
The document is written to a file.
Dart PDF header
A document header is created with Header.
import 'dart:io';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
void main() async {
final txt = '''
The Battle of Thermopylae was fought between an alliance of Greek city-states,
led by King Leonidas of Sparta, and the Persian Empire of Xerxes I over the
course of three days, during the second Persian invasion of Greece.
''';
final pdf = pw.Document();
pdf.addPage(pw.Page(
pageFormat: PdfPageFormat.a4,
build: (pw.Context context) {
return pw.Column(children: [
pw.Header(level: 0, text: 'The Battle of Thermopylae'),
pw.Paragraph(text: txt)
]);
},
));
final file = File('header.pdf');
await file.writeAsBytes(await pdf.save());
}
The document contains a header and a paragraph of text.
pdf.addPage(pw.Page(
pageFormat: PdfPageFormat.a4,
build: (pw.Context context) {
return pw.Column(children: [
pw.Header(level: 0, text: 'The Battle of Thermopylae'),
pw.Paragraph(text: txt)
]);
},
));
The header and the paragraph are added to a column widget. We specify the level and text for the header and text for the paragraph.
Dart PDF bullets
A bullet is created with the Bullet widget.
import 'dart:io';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
void main() async {
final pdf = pw.Document();
pdf.addPage(pw.Page(
pageFormat: PdfPageFormat.a4,
build: (pw.Context context) {
return pw.Column(children: [
pw.Header(level: 1, text: 'Programming languages'),
pw.Bullet(text: 'Dart'),
pw.Bullet(text: 'F#'),
pw.Bullet(text: 'Clojure'),
pw.Bullet(text: 'Go'),
pw.Bullet(text: 'Groovy'),
pw.Bullet(text: 'Raku'),
pw.Bullet(text: 'Python'),
]);
}));
final file = File('bullets.pdf');
await file.writeAsBytes(await pdf.save());
}
We have a header and a list of programming languages. Each language is displayed as a bullet.
Dart PDF rectangles
A rectangle is created with Rectangle.
import 'dart:io';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
void main() async {
final pdf = pw.Document();
pdf.addPage(pw.Page(
pageFormat: PdfPageFormat.a4,
build: (pw.Context context) {
return pw.Row(children: [
pw.SizedBox(
width: 80,
height: 80,
child: pw.Rectangle(fillColor: PdfColors.grey),
),
pw.Spacer(flex: 1),
pw.SizedBox(
width: 80,
height: 80,
child: pw.Rectangle(fillColor: PdfColors.amber300),
),
pw.Spacer(flex: 1),
pw.SizedBox(
width: 80,
height: 80,
child: pw.Rectangle(fillColor: PdfColors.green500),
),
pw.Spacer(flex: 1),
pw.SizedBox(
width: 80,
height: 80,
child: pw.Rectangle(fillColor: PdfColors.blue600),
),
]);
})); // Pa
final file = File('rectangles.pdf');
await file.writeAsBytes(await pdf.save());
}
We create a row of SizedBox widgets. In the boxes, we place
recangle widgets.
pw.SizedBox( width: 80, height: 80, child: pw.Rectangle(fillColor: PdfColors.grey), ),
For each rectangle, we define a fill colour.
Dart PDF footer
In the next example, we add a footer to our pages.
import 'dart:io';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
void main() async {
final pdf = pw.Document(pageMode: PdfPageMode.outlines);
final title = pw.LoremText();
pdf.addPage(
pw.MultiPage(
footer: _buildFooter,
build: (context) => [
pw.Header(level: 1, text: title.sentence(1)),
pw.SizedBox(height: 20),
pw.Lorem(length: 50),
pw.SizedBox(height: 20),
pw.Lorem(length: 70),
pw.SizedBox(height: 20),
pw.Lorem(length: 150),
pw.SizedBox(height: 20),
pw.Lorem(length: 250),
pw.SizedBox(height: 20),
pw.Lorem(length: 350),
],
),
);
final file = File('footer.pdf');
await file.writeAsBytes(await pdf.save());
}
pw.Widget _buildFooter(pw.Context context) {
return pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
crossAxisAlignment: pw.CrossAxisAlignment.end,
children: [
pw.Text(
'Page ${context.pageNumber}/${context.pagesCount}',
style: const pw.TextStyle(
fontSize: 12,
color: PdfColors.blue700,
),
)
],
);
}
The footer consists of a row that contains the current and the total page numbers.
final title = pw.LoremText();
The lorem methods provide some filler text for the document. This is a common practice in desktop publishing when testing.
pdf.addPage(
pw.MultiPage(
footer: _buildFooter,
build: (context) => [
pw.Header(level: 1, text: title.sentence(1)),
pw.SizedBox(height: 20),
pw.Lorem(length: 50),
pw.SizedBox(height: 20),
pw.Lorem(length: 70),
pw.SizedBox(height: 20),
pw.Lorem(length: 150),
pw.SizedBox(height: 20),
pw.Lorem(length: 250),
pw.SizedBox(height: 20),
pw.Lorem(length: 350),
],
),
);
We have a multi-page document. The creation of the footer is delegated to the
_buildFooter function.
pw.SizedBox(height: 20), pw.Lorem(length: 50),
We add some empty space with SizedBox. The Lorem
method adds 50 characters of some latin text.
pw.Widget _buildFooter(pw.Context context) {
return pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
crossAxisAlignment: pw.CrossAxisAlignment.end,
children: [
pw.Text(
'Page ${context.pageNumber}/${context.pagesCount}',
style: const pw.TextStyle(
fontSize: 12,
color: PdfColors.blue700,
),
)
],
);
}
This builds the footer. It is a row with Text widget. It contains
the current page number and the count of all pages. Also, we style the text:
we define its size and colour.
Dart PDF table
A table is created with the Table widget. It is a complex widget
having many options.
import 'dart:io';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
class User {
late String name;
late String occupation;
User(String name, String occupation) {
this.name = name;
this.occupation = occupation;
}
String getIndex(int idx) {
switch (idx) {
case 0:
return name;
case 1:
return occupation;
}
return '';
}
}
final users = <User>[
User("John Doe", "gardener"),
User("Roder Roe", "driver"),
User("Joe Smith", "teacher"),
];
void main() async {
final pdf = pw.Document();
pdf.addPage(pw.Page(
pageFormat: PdfPageFormat.a4,
build: (pw.Context ctx) {
return genTable(ctx);
}));
final file = File('table.pdf');
await file.writeAsBytes(await pdf.save());
}
pw.Widget genTable(pw.Context ctx) {
const headers = [
'Name',
'Occupation',
];
return pw.Table.fromTextArray(
border: null,
cellAlignment: pw.Alignment.centerLeft,
headerDecoration: pw.BoxDecoration(
borderRadius: const pw.BorderRadius.all(pw.Radius.circular(1)),
color: PdfColors.blue500,
),
headerHeight: 25,
cellHeight: 40,
cellAlignments: {
0: pw.Alignment.centerLeft,
1: pw.Alignment.centerLeft,
},
headerStyle: pw.TextStyle(
color: PdfColors.white,
fontSize: 10,
fontWeight: pw.FontWeight.bold,
),
cellStyle: const pw.TextStyle(
color: PdfColors.blueGrey800,
fontSize: 10,
),
rowDecoration: pw.BoxDecoration(
border: pw.Border(
bottom: pw.BorderSide(
color: PdfColors.blueGrey900,
width: .5,
),
),
),
headers: List<String>.generate(
headers.length,
(col) => headers[col],
),
data: List<List<String>>.generate(
users.length,
(row) => List<String>.generate(
headers.length,
(col) => users[row].getIndex(col),
),
),
);
}
We display users in a table.
class User {
late String name;
late String occupation;
User(String name, String occupation) {
this.name = name;
this.occupation = occupation;
}
String getIndex(int idx) {
switch (idx) {
case 0:
return name;
case 1:
return occupation;
}
return '';
}
}
The table displays users. The getIndex method is called by table's
data function. It returns the corresponding fields.
final users = <User>[
User("John Doe", "gardener"),
User("Roder Roe", "driver"),
User("Joe Smith", "teacher"),
];
The datasource for the table is a list of users.
headerHeight: 25,
cellHeight: 40,
cellAlignments: {
0: pw.Alignment.centerLeft,
1: pw.Alignment.centerLeft,
},
We have options that define the look of the table. Here we define the header and cell heights and cell alignments.
rowDecoration: pw.BoxDecoration(
border: pw.Border(
bottom: pw.BorderSide(
color: PdfColors.blueGrey900,
width: .5,
),
),
),
We define table bottom borders. We set the border colour and size.
headers: List<String>.generate( headers.length, (col) => headers[col], ),
We set the headers for the table.
data: List<List<String>>.generate(
users.length,
(row) => List<String>.generate(
headers.length,
(col) => users[row].getIndex(col),
),
),
The table is filled with data.
Source
Dart pdf library - documentation
In this article we how shown how to generate PDF documents in Dart.
Author
List all Dart tutorials.