Nurullah Türkoğlu

30 June 2024 · 2 min read

Creating PDFs in Flutter With Syncfusion

How to create PDF files in Flutter with syncfusion_flutter_pdf, covering pages, page numbers, text, images, shapes, tables and lists, with a full example app.

Flutter is a framework that has quickly become popular for mobile development. Cross-platform support and its widget system make a lot of things easier. This post covers the basic steps of creating PDF files in Flutter with the syncfusion_flutter_pdf package: creating a PDF, adding text and saving the output. It also goes into pages, links and tables.

Introduction

PDF is a universal file format that displays documents in a fixed layout. It is used everywhere from business to education, and Flutter apps often need to create or edit PDFs.

Installing the packages

Add the syncfusion_flutter_pdf package to the project. The package versions here are the latest ones at the time of writing. When you read this, use the versions that are current for you. Add the following to your pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  syncfusion_flutter_pdf: ^26.1.39
  open_file: ^3.3.2
  path_provider: ^2.1.3

Then run pub get in the terminal to download the packages.

flutter pub get

Creating a PDF with syncfusion_flutter_pdf

First we create the PDF document and set up the basics. The code below creates a new PDF document and adds a page.

import 'package:syncfusion_flutter_pdf/pdf.dart';

Future<void> createPdf() async {
  // Create a new PDF document
  final PdfDocument document = PdfDocument();
  
  // Add the first page
  document.pages.add();
  
  // Save and display the PDF document
  List<int> bytes = document.save();
  document.dispose();
  
  // Write the bytes to a file and open it on the device (needs an app-specific method)
  saveAndLaunchFile(bytes, 'Output.pdf');
}

Adding text to a PDF

We use the PdfTextElement class to add text. The example below adds a title and a subtitle to a page.

void addTextToPdf(PdfDocument document) {
  final page = document.pages[0];
  
  // Title
  final title = PdfTextElement(
    text: 'Creating PDFs With Flutter',
    font: PdfStandardFont(PdfFontFamily.helvetica, 30),
    brush: PdfSolidBrush(PdfColor(0, 0, 0)),
  );
  
  // Subtitle
  final subtitle = PdfTextElement(
    text: 'Using syncfusion_flutter_pdf',
    font: PdfStandardFont(PdfFontFamily.helvetica, 20),
    brush: PdfSolidBrush(PdfColor(0, 0, 0)),
  );
  
  // Draw the text on the page
  title.draw(page: page, bounds: Rect.fromLTWH(0, 0, 500, 50));
  subtitle.draw(page: page, bounds: Rect.fromLTWH(0, 40, 500, 50));
}

Saving and opening the PDF

You can use your own methods to store the PDF and show it on the device. The example below has a helper that saves the file and opens it. Note that the open_file package is what opens the created file.

import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:open_file/open_file.dart';

Future<void> saveAndLaunchFile(List<int> bytes, String fileName) async {
  final path = (await getExternalStorageDirectory())!.path;
  final file = File('$path/$fileName');
  await file.writeAsBytes(bytes, flush: true);
  OpenFile.open('$path/$fileName');
}

Managing pages

Adding pages

You can add several pages to a PDF document. The example below adds two pages.

void addPagesToPdf(PdfDocument document) {
  // First page
  document.pages.add();
  
  // Second page
  document.pages.add();
}

Page numbers

Page numbers make a document more organized and easier to read. The example below adds a page number at the bottom of each page.

void addPageNumbers(PdfDocument document) {
  final PdfPageTemplateElement footer = PdfPageTemplateElement(
      Rect.fromLTWH(0, 0, document.pageSettings.size.width, 50));
  final PdfPageNumberField pageNumber = PdfPageNumberField(
      font: PdfStandardFont(PdfFontFamily.helvetica, 12),
      brush: PdfSolidBrush(PdfColor(0, 0, 0)));
  pageNumber.numberStyle = PdfNumberStyle.numeric;

  final PdfCompositeField compositeField = PdfCompositeField(
      font: PdfStandardFont(PdfFontFamily.helvetica, 12),
      brush: PdfSolidBrush(PdfColor(0, 0, 0)),
      text: 'Page {0} of {1}',
      fields: <PdfAutomaticField>[pageNumber, PdfPageCountField()]);
  compositeField.bounds = footer.bounds;

  footer.graphics.drawString(compositeField.text,
      PdfStandardFont(PdfFontFamily.helvetica, 12),
      brush: PdfSolidBrush(PdfColor(0, 0, 0)),
      bounds: footer.bounds,
      format: PdfStringFormat(alignment: PdfTextAlignment.center));
  document.template.bottom = footer;
}

Adding and styling text

Headings and subheadings

Adding a heading and subheading is very common in PDF documents. The example below adds both to a page.

void addHeadingAndSubheading(PdfDocument document) {
  final PdfPage page = document.pages[0];

  // Heading
  final PdfTextElement heading = PdfTextElement(
    text: 'Creating PDFs With Flutter',
    font: PdfStandardFont(PdfFontFamily.helvetica, 30),
    brush: PdfSolidBrush(PdfColor(0, 0, 0)),
  );

  // Subheading
  final PdfTextElement subheading = PdfTextElement(
    text: 'Using syncfusion_flutter_pdf',
    font: PdfStandardFont(PdfFontFamily.helvetica, 20),
    brush: PdfSolidBrush(PdfColor(0, 0, 0)),
  );

  heading.draw(page: page, bounds: Rect.fromLTWH(0, 0, page.getClientSize().width, 50));
  subheading.draw(page: page, bounds: Rect.fromLTWH(0, 50, page.getClientSize().width, 50));
}

Paragraphs

You can use paragraphs to add long text to a PDF. The example below adds a paragraph to a page.

void addParagraph(PdfDocument document) {
  final PdfPage page = document.pages[0];

  final PdfTextElement paragraph = PdfTextElement(
    text: 'Flutter is an open source UI software development kit (SDK) developed by Google. '
          'Flutter lets you build near-native apps for Android, iOS, Linux, macOS, Windows, Google Fuchsia '
          'and the web from a single codebase.',
    font: PdfStandardFont(PdfFontFamily.helvetica, 12),
    brush: PdfSolidBrush(PdfColor(0, 0, 0)),
  );
  
  paragraph.draw(page: page, bounds: Rect.fromLTWH(0, 100, page.getClientSize().width, page.getClientSize().height - 100));
}

Fonts and styles

Font and style settings make the text easier to read and nicer to look at.

void addStyledText(PdfDocument document) {
  final PdfPage page = document.pages[0];

  // Bold text
  final PdfTextElement boldText = PdfTextElement(
    text: 'This text is bold.',
    font: PdfStandardFont(PdfFontFamily.helvetica, 12, style: PdfFontStyle.bold),
    brush: PdfSolidBrush(PdfColor(0, 0, 0)),
  );

  // Italic text
  final PdfTextElement italicText = PdfTextElement(
    text: 'This text is italic.',
    font: PdfStandardFont(PdfFontFamily.helvetica, 12, style: PdfFontStyle.italic),
    brush: PdfSolidBrush(PdfColor(0, 0, 0)),
  );

  // Underlined text
  final PdfTextElement underlineText = PdfTextElement(
    text: 'This text is underlined.',
    font: PdfStandardFont(PdfFontFamily.helvetica, 12, style: PdfFontStyle.underline),
    brush: PdfSolidBrush(PdfColor(0, 0, 0)),
  );

  boldText.draw(page: page, bounds: Rect.fromLTWH(0, 0, page.getClientSize().width, 50));
  italicText.draw(page: page, bounds: Rect.fromLTWH(0, 50, page.getClientSize().width, 50));
  underlineText.draw(page: page, bounds: Rect.fromLTWH(0, 100, page.getClientSize().width, 50));
}

Images and graphics

Adding an image

We can use the PdfBitmap class to add an image to a PDF document.

void addImageToPdf(PdfDocument document) {
  final PdfPage page = document.pages.add();
  
  final ByteData data = await rootBundle.load('assets/flutter_logo.png');
  final Uint8List bytes = data.buffer.asUint8List();
  
  final PdfBitmap image = PdfBitmap(bytes);
  page.graphics.drawImage(image, Rect.fromLTWH(0, 0, 200, 200));
}

Charts and shapes

We can use the PdfGraphics class to add a chart or shape to a PDF document.

void addChartToPdf(PdfDocument document) {
  final PdfPage page = document.pages.add();
  
  page.graphics.drawRectangle(
    bounds: Rect.fromLTWH(0, 0, 100, 100),
    pen: PdfPen(PdfColor(255, 0, 0), width: 2),
    brush: PdfSolidBrush(PdfColor(255, 0, 0)),
  );

  page.graphics.drawEllipse(
    bounds: Rect.fromLTWH(150, 0, 100, 100),
    pen: PdfPen(PdfColor(0, 255, 0), width: 2),
    brush: PdfSolidBrush(PdfColor(0, 255, 0)),
  );
}

Tables and lists

Simple tables

We can use the PdfGrid class to add simple tables to a PDF document.

void addSimpleTable(PdfDocument document) {
  final PdfPage page = document.pages.add();

  final PdfGrid grid = PdfGrid();
  grid.columns.add(count: 3);

  final PdfGridRow header = grid.headers.add(1)[0];
  header.cells[0].value = 'ID';
  header.cells[1].value = 'Name';
  header.cells[2].value = 'Age';

  final PdfGridRow row1 = grid.rows.add();
  row1.cells[0].value = '1';
  row1.cells[1].value = 'John Doe';
  row1.cells[2].value = ‘30’;

  final PdfGridRow row2 = grid.rows.add();
  row2.cells[0].value = ‘2’;
  row2.cells[1].value = ‘Jane Smith’;
  row2.cells[2].value = ‘25’;

  grid.draw(page: page, bounds: Rect.fromLTWH(0, 0, page.getClientSize().width, page.getClientSize().height));
}

You can also use PdfGrid for tables that are filled with data dynamically.

void addDynamicTable(PdfDocument document) {
  final PdfPage page = document.pages.add();

  final PdfGrid grid = PdfGrid();
  grid.columns.add(count: 3);

  final PdfGridRow header = grid.headers.add(1)[0];
  header.cells[0].value = 'ID';
  header.cells[1].value = 'Name';
  header.cells[2].value = 'Age';

  final List<Map<String, String>> data = [
    {'ID': '1', 'Name': 'John Doe', 'Age': '30'},
    {'ID': '2', 'Name': 'Jane Smith', 'Age': '25'},
    {'ID': '3', 'Name': 'Sam Brown', 'Age': '20'},
  ];

  for (final item in data) {
    final PdfGridRow row = grid.rows.add();
    row.cells[0].value = item['ID'];
    row.cells[1].value = item['Name'];
    row.cells[2].value = item['Age'];
  }

  grid.draw(page: page, bounds: Rect.fromLTWH(0, 0, page.getClientSize().width, page.getClientSize().height));
}

Lists and bullet points

We can use the PdfOrderedList and PdfUnorderedList classes to add lists and bullet points to a PDF document.

void addListsToPdf(PdfDocument document) {
  final PdfPage page = document.pages.add();

  final PdfOrderedList orderedList = PdfOrderedList(
    items: ['First item', 'Second item', 'Third item'],
    font: PdfStandardFont(PdfFontFamily.helvetica, 12),
    brush: PdfSolidBrush(PdfColor(0, 0, 0)),
  );

  orderedList.draw(page: page, bounds: Rect.fromLTWH(0, 0, page.getClientSize().width, page.getClientSize().height));

  final PdfUnorderedList unorderedList = PdfUnorderedList(
    items: ['First item', 'Second item', 'Third item'],
    font: PdfStandardFont(PdfFontFamily.helvetica, 12),
    brush: PdfSolidBrush(PdfColor(0, 0, 0)),
  );

  unorderedList.draw(page: page, bounds: Rect.fromLTWH(0, 100, page.getClientSize().width, page.getClientSize().height));
}

Example app

To show the whole process, here is a detailed example. It creates a PDF, adds a heading, text, a table, an image and a link, then saves the file on the device and opens it.

import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_pdf/pdf.dart';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:open_file/open_file.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Flutter PDF Creation')),
        body: Center(
          child: ElevatedButton(
            onPressed: createPdf,
            child: Text('Create PDF'),
          ),
        ),
      ),
    );
  }

  Future<void> createPdf() async {
    final PdfDocument document = PdfDocument();
    final PdfPage page = document.pages.add();

    // Heading
    final PdfTextElement heading = PdfTextElement(
      text: 'Creating PDFs With Flutter',
      font: PdfStandardFont(PdfFontFamily.helvetica, 30),
      brush: PdfSolidBrush(PdfColor(0, 0, 0)),
    );
    heading.draw(page: page, bounds: Rect.fromLTWH(0, 0, page.getClientSize().width, 50));

    // Subheading
    final PdfTextElement subheading = PdfTextElement(
      text: 'Using syncfusion_flutter_pdf',
      font: PdfStandardFont(PdfFontFamily.helvetica, 20),
      brush: PdfSolidBrush(PdfColor(0, 0, 0)),
    );
    subheading.draw(page: page, bounds: Rect.fromLTWH(0, 50, page.getClientSize().width, 50));

    // Paragraph
    final PdfTextElement paragraph = PdfTextElement(
      text: 'Flutter is an open source UI software development kit (SDK) developed by Google. '
            'Flutter lets you build near-native apps for Android, iOS, Linux, macOS, Windows, Google Fuchsia '
            'and the web from a single codebase.',
      font: PdfStandardFont(PdfFontFamily.helvetica, 12),
      brush: PdfSolidBrush(PdfColor(0, 0, 0)),
    );
    paragraph.draw(page: page, bounds: Rect.fromLTWH(0, 100, page.getClientSize().width, page.getClientSize().height - 100));

    // Table
    final PdfGrid grid = PdfGrid();
    grid.columns.add(count: 3);

    final PdfGridRow header = grid.headers.add(1)[0];
    header.cells[0].value = 'ID';
    header.cells[1].value = 'Name';
    header.cells[2].value = 'Age';

    final List<Map<String, String>> data = [
      {'ID': '1', 'Name': 'John Doe', 'Age': '30'},
      {'ID': '2', 'Name': 'Jane Smith', 'Age': '25'},
      {'ID': '3', 'Name': 'Sam Brown', 'Age': '20'},
    ];

    for (final item in data) {
      final PdfGridRow row = grid.rows.add();
      row.cells[0].value = item['ID'];
      row.cells[1].value = item['Name'];
      row.cells[2].value = item['Age'];
    }

    grid.draw(page: page, bounds: Rect.fromLTWH(0, 200, page.getClientSize().width, page.getClientSize().height));

    // Image
    final ByteData data = await rootBundle.load('assets/flutter_logo.png');
    final Uint8List bytes = data.buffer.asUint8List();

    final PdfBitmap image = PdfBitmap(bytes);
    page.graphics.drawImage(image, Rect.fromLTWH(0, 300, 200, 200));

    // Internal link
    final PdfDestination destination = PdfDestination(page, Offset(0, 0));
    final PdfTextWebLink link = PdfTextWebLink(
      text: 'Click to go to the top.',
      font: PdfStandardFont(PdfFontFamily.helvetica, 12),
      brush: PdfSolidBrush(PdfColor(0, 0, 0)),
      url: destination,
    );
    link.draw(page: page, bounds: Rect.fromLTWH(0, 500, page.getClientSize().width, 50));

    // Save and display the PDF document
    List<int> bytes = document.save();
    document.dispose();

    await saveAndLaunchFile(bytes, 'Output.pdf');
  }

  Future<void> saveAndLaunchFile(List<int> bytes, String fileName) async {
    final path = (await getExternalStorageDirectory())!.path;
    final file = File('$path/$fileName');
    await file.writeAsBytes(bytes, flush: true);
    OpenFile.open('$path/$fileName');
  }
}

When you press the button in this example app, the PDF is created and shown on the device.