The right way to convert HTML to PDF in Flutter depends on whether you need browser-like HTML/CSS rendering or a PDF built from Flutter/PDF widgets. For Android and iOS, a native WebView converter such as flutter_native_html_to_pdf is the most direct documented route. For desktop and web targets, the printing workflow converts HTML into PDF widgets and then creates a PDF document. Test your actual HTML on every target: package platform labels do not guarantee identical CSS support.
Choose the conversion path first
| Need | Best starting point | What it does | Important qualification |
|---|---|---|---|
| Android or iOS with browser-like HTML/CSS | flutter_native_html_to_pdf |
Loads HTML in a native WebView and exports a file or PDF bytes. | The package documentation lists Android and iOS; verify the installed version and test your templates. |
| Android, iOS, Linux, macOS, Windows, or web | printing with its HTML-to-widget example |
Converts HTML to PDF-package widgets, adds them to a pw.Document, and returns PDF bytes for saving, previewing, printing, or sharing. |
Its platform listing does not mean every HTML/CSS feature renders identically everywhere. |
| Existing legacy code using HTML strings or files | flutter_html_to_pdf |
Provides conversion methods for HTML content, a File, or a file path. |
Its documentation is older in the search index; check maintenance, dependency constraints, and platform support before starting a new project. |
Method 1: Native WebView conversion on Android and iOS
flutter_native_html_to_pdf documents an HtmlToPdfConverter. Android loads the markup into an off-screen WebView and exports through Android’s print framework. On iOS, the package documentation describes WKWebView.createPDF on iOS 14 and later, with UIPrintPageRenderer for iOS 12–13. These are package documentation claims, so confirm behavior with your dependency version.
Convert an HTML string to a file
import 'package:flutter_native_html_to_pdf/flutter_native_html_to_pdf.dart';
Future<String?> createPdf(String outputDirectory) async {
final converter = HtmlToPdfConverter();
final file = await converter.convertHtmlToPdf(
html: '''
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
@page { margin: 18mm; }
body { font-family: Arial, sans-serif; color: #222; }
h1 { color: #1455a0; }
</style>
</head>
<body>
<h1>Invoice</h1>
<p>Generated from Flutter HTML.</p>
</body>
</html>
''',
targetDirectory: outputDirectory,
targetName: 'invoice',
);
return file?.path;
}
The result is a saved PDF file when conversion succeeds. The exact return type and nullability can vary by package version, so inspect the API exposed by the version in your pubspec.yaml.
Request PDF bytes instead
final converter = HtmlToPdfConverter();
final bytes = await converter.convertHtmlToPdfBytes(
html: '<h1>Report</h1><p>Hello</p>',
);
if (bytes == null || bytes.isEmpty) {
throw StateError('HTML conversion returned no PDF bytes');
}
// Pass bytes to your storage, sharing, or upload code.
Bytes are useful when you do not want to manage a temporary file, or when another API in your app expects a Uint8List. Keep remote images reachable by the WebView and provide absolute URLs or data URLs; relative paths that work in a browser may not resolve from an in-memory HTML string.
Recommended Free Tools
#1 Best Overall
Method 2: Convert HTML through printing and PDF widgets
The printing package documents Android, iOS, Linux, macOS, Windows, and web support. Its HTML example uses HTMLToPdf().convert(body), adds the resulting widgets to a PDF document, and supplies the generated bytes to Printing.layoutPdf.
Complete document-and-preview example
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
class HtmlPdfButton extends StatelessWidget {
const HtmlPdfButton({super.key});
static const html = '''
<h1>Project report</h1>
<p>This content is converted to PDF widgets.</p>
<ul><li>First item</li><li>Second item</li></ul>
''';
Future<Uint8List> buildPdf() async {
final document = pw.Document();
final widgets = await HTMLToPdf().convert(html);
document.addPage(
pw.MultiPage(
build: (context) => widgets,
),
);
return document.save();
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () async {
await Printing.layoutPdf(
onLayout: (format) => buildPdf(),
);
},
child: const Text('Preview or print PDF'),
);
}
}
Save or share the generated bytes
buildPdf() returns PDF bytes. Use your preferred file-system or sharing integration to write those bytes, and use the sharing and file-output APIs documented by your chosen versions of printing and pdf. Keep the conversion function separate from the button so the same bytes can feed preview, print, upload, or share actions.
Understand the fidelity trade-off
This route does not ask a browser to print the page. It translates supported HTML into widgets understood by the PDF library. That can fit reports with headings, paragraphs, lists, and controlled styling, but complex browser CSS, JavaScript-driven layout, and unusual HTML may require template changes. Test page breaks, fonts, tables, and images on each target.
Rank #2
Method 3: Existing projects using flutter_html_to_pdf
The legacy API documents three input forms:
convertFromHtmlContentfor an HTML string.convertFromHtmlFilefor aFile.convertFromHtmlFilePathfor a path.
It is described as handling most common HTML markers, but the documentation is older. Before adopting it, check whether its dependency constraints work with your Flutter and Dart versions, whether your target platforms are supported, and whether unresolved issues affect your templates. Do not select it solely because an older sample compiles.
Prepare HTML that converts reliably
Make layout deterministic
- Use explicit margins, widths, colors, and font sizes instead of relying on browser defaults.
- Keep critical content in normal document flow; positioned elements can overlap or disappear when pagination changes.
- Use print-oriented CSS such as
@pageonly after confirming the selected converter honors it. - Provide a viewport declaration for WebView rendering and avoid scripts that must finish after an unbounded delay.
Control images and file size
The flutter_html_to_pdf documentation warns that many document images can significantly increase the final file size. Resize images to their intended print dimensions, avoid embedding unnecessarily large originals, and compress them before conversion when quality allows. The documentation mentions flutter_image_compress as one possible aid; it is not required by the conversion API.
Handle fonts and remote assets
Bundle or otherwise make fonts available to the rendering path you selected. A WebView may load remote resources differently from a PDF widget converter, and a web build may be subject to browser-origin rules. For repeatable output, prefer local, versioned assets and check the generated PDF rather than assuming the source HTML’s appearance is preserved.
Platform checks before release
- Android/iOS: exercise the native converter on the minimum OS versions your app supports, including offline and slow-network cases.
- Desktop: confirm that the exact conversion API, not merely the package’s general platform badge, is available on Linux, macOS, and Windows.
- Web: do not infer native plugin support from mobile support. Flutter’s web documentation treats embedded non-Flutter content as a platform-view concern, and platform views have their own pointer-event and integration behavior.
- Document-heavy web apps: Flutter’s web guidance notes that text-rich, flow-based static content may fit a document-centric web model better than an app-centric one. If your primary requirement is web document publishing, evaluate whether Flutter should own the rendering at all.
Testing checklist
- Generate a short document and a multi-page document.
- Test long paragraphs, tables, lists, page breaks, links, and missing images.
- Compare output on every target platform and on both small and large screens where WebView is involved.
- Open the PDF in more than one viewer and verify selectable text, page count, margins, and file size.
- Test malformed HTML, empty input, network failures, and cancellation paths.
- Record the package versions used for release and re-run the same fixtures after upgrades.
Troubleshooting common failures
The output is blank
Check that the HTML string is non-empty and valid, that remote resources are reachable, and that conversion is awaited before reading the file or bytes. For WebView rendering, wait for the page’s required content rather than starting export immediately.
Images are missing
Use absolute URLs or embedded data, verify network permissions and certificates, and test the same URL from the target device. For widget conversion, confirm that the HTML-to-widget converter supports the image element and its source format.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesCSS looks different
Switching between WebView rendering and PDF widgets changes the rendering engine. Reduce reliance on unsupported CSS, set explicit dimensions, and create a small fixture that isolates the failing rule.
Rank #4
The PDF is unexpectedly huge
Inspect image pixel dimensions and compression first. Downsample images for their final printed size, remove unused assets, and avoid embedding the same large image repeatedly.
It works on mobile but not web
Native Android/iOS APIs are not automatically available in a browser. Use the printing route if its exact web conversion path meets your needs, or provide a web-specific implementation and test it independently.
A legacy package will not resolve
Review its SDK constraints and transitive dependencies. If they conflict with your current Flutter release, migrate to a maintained route rather than forcing incompatible versions.
Best Value
Or skip the browser setup
If your HTML is already available at a public URL and you need a server-generated PDF, ScreenshotNeo provides a website screenshot API and MCP server. Its capture endpoint can return a PDF; it also accepts the cookie or consent banner before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result.
curl -G "https://api.screenshotneo.com/v1/shot"
-d access_key=YOUR_API_KEY
--data-urlencode url=https://example.com/report
-d format=pdf
-o report.pdf
See the ScreenshotNeo API documentation for the current PDF parameters and authentication details. The service also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Every plan includes the features; the free plan includes 1,000 screenshots per month with no card, and paid plans start at $5 for 3,000 shots. Create an account at ScreenshotNeo’s free sign-up page.
Which approach should you ship?
- Choose the native WebView converter when Android/iOS browser fidelity is the priority and its documented platform scope matches your app.
- Choose
printingwhen you need the broader listed platform range and can express the document through PDF widgets. - Keep the legacy package only when its API and maintenance status are acceptable for your existing project.
- Use ScreenshotNeo when the source is a reachable web page and a remote PDF capture, consent handling, and agent access are more useful than embedding a converter in the app.
Frequently Asked Questions
Can I convert an HTML file instead of an HTML string?
Yes. The legacy flutter_html_to_pdf documentation lists separate methods for a File and for a file path. The native converter example accepts an HTML string; read your file first and pass its contents if that is the API exposed by your installed version.
Does Flutter HTML-to-PDF conversion execute JavaScript?
Do not assume it. WebView and PDF-widget routes use different rendering paths, and JavaScript-dependent output must be tested in the exact converter and platform you plan to ship.
Is a package’s platform badge proof that HTML conversion works on that platform?
No. Verify the specific conversion API, dependency version, and representative documents on each target.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




