Flutter web - display pdf file generated in application. (Uint8List format)
Updated on Feb 12th, 2021:
After more rounds of googling and digging into documentation, I have got it to work. Here are the main points:
1- There is no need to insert any javascript code into <app_directory>/web/index.html
file as I had pointed before. Sorry! I am learning flutter/dart...
2- I had to create a Future<html.Blob>
method (please have a look at myGetBlobPdfContent() async {...}
) to deal with html/pdf content, to save (await pdf.save()
) and to return a Blob
value. I'm not the right person to say it was related with a "non complete future" was throwing an null point on pdf stuff...
3- Also, Those RaisedButton's onPressed
methods were changed to async, to allow to get the pdf content as a Blob value (createObjectUrlFromBlob( await myGetBlobPdfContent());
).
4- Finally, I was having warning messages: Helvetica has no Unicode support see https://github.com/DavBfr/dart_pdf/wiki/Fonts-Management
, so that I have installed custom font like this cookbook instructions, in this example I have set up Arial ttf (data = await rootBundle.load("fonts/arial.ttf");
) and those warning messages went away.
5- Here is my pubspec.yaml
's snippet:
...
dependencies:
flutter:
sdk: flutter
pdf: ^2.1.0
universal_html: ^1.2.4
...
fonts:
- family: Arial
fonts:
- asset: fonts/arial.ttf
- asset: fonts/arialbd.ttf
weight: 700
- asset: fonts/ariali.ttf
style: italic
...
6- And an minimal working example (full main.dart
file):
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:universal_html/html.dart' as html;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: PdfDemo(),
);
}
}
class PdfDemo extends StatelessWidget {
Future<html.Blob> myGetBlobPdfContent() async {
var data = await rootBundle.load("fonts/arial.ttf");
var myFont = pw.Font.ttf(data);
var myStyle = pw.TextStyle(font: myFont, fontSize: 36.0);
final pdf = pw.Document();
pdf.addPage(pw.Page(
pageFormat: PdfPageFormat.a4,
build: (pw.Context context) {
return pw.Center(
child: pw.Text(
"Hello World",
style: myStyle,
),
// child: pw.Text("Hello World", style: myStyle),
);
}));
final bytes = await pdf.save();
html.Blob blob = html.Blob([bytes], 'application/pdf');
return blob;
}
@override
Widget build(BuildContext context) {
myGetBlobPdfContent();
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
children: <Widget>[
RaisedButton(
child: Text("Open"),
onPressed: () async {
final url = html.Url.createObjectUrlFromBlob(
await myGetBlobPdfContent());
html.window.open(url, "_blank");
html.Url.revokeObjectUrl(url);
},
),
RaisedButton(
child: Text("Download"),
onPressed: () async {
final url = html.Url.createObjectUrlFromBlob(
await myGetBlobPdfContent());
final anchor =
html.document.createElement('a') as html.AnchorElement
..href = url
..style.display = 'none'
..download = 'some_name.pdf';
html.document.body.children.add(anchor);
anchor.click();
html.document.body.children.remove(anchor);
html.Url.revokeObjectUrl(url);
},
),
],
mainAxisAlignment: MainAxisAlignment.center,
),
),
);
}
}
7- At the end, the PDF generation (and download) is working as expected:
Hope this helps.
Initial post (Only to keep the information log)
@Spatz's answer might solve any pdf issue for web platform. I don't know why it is not working as expected, at least for my setup (see below).
I mean the webpage tries to open the pdf and throws an error: Failed to load PDF document.
Also, the example downloads a pdf file (some_name.pdf
) successfully, but it fails when I try to open the pdf file (error: file corrupted
).
I have seen that this post talks about insert javascript code into <app_directory>/web/index.html file. I have insert those lines and no success as well.
The source code I am using is a one main.dart
file:
import 'package:flutter/material.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:universal_html/html.dart' as html;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: PdfDemo(),
);
}
}
class PdfDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
final pdf = pw.Document();
pdf.addPage(pw.Page(
pageFormat: PdfPageFormat.a4,
build: (pw.Context context) {
return pw.Center(
child: pw.Text("Hello World"),
);
}));
final bytes = pdf.save();
final blob = html.Blob([bytes], 'application/pdf');
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
children: <Widget>[
RaisedButton(
child: Text("Open"),
onPressed: () {
final url = html.Url.createObjectUrlFromBlob(blob);
html.window.open(url, "_blank");
html.Url.revokeObjectUrl(url);
},
),
RaisedButton(
child: Text("Download"),
onPressed: () {
final url = html.Url.createObjectUrlFromBlob(blob);
final anchor =
html.document.createElement('a') as html.AnchorElement
..href = url
..style.display = 'none'
..download = 'some_name.pdf';
html.document.body.children.add(anchor);
anchor.click();
html.document.body.children.remove(anchor);
html.Url.revokeObjectUrl(url);
},
),
],
mainAxisAlignment: MainAxisAlignment.center,
),
),
);
}
}
My setup is:
- Flutter (Channel beta, 1.26.0-17.3.pre, on Microsoft Windows [Version 10.0.19042.746], locale en-150)
- Android tool chain develop for Android devices (Android SDK version 30.0.3)
- Chrome - develop for the web
- Android Studio (version 4.1.0)
Thank you :)
We can do it without additional plug-ins, because the web browser itself can display (or download) pdf
document.
updated for pdf 2.0.0 and newer
Since method save now returns Future, button handlers become asynchronous (of course you can use FurureBuilder or something else).
import 'package:flutter/material.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:universal_html/html.dart' as html;
class PdfLabPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final pdf = pw.Document();
pdf.addPage(pw.Page(
pageFormat: PdfPageFormat.a4,
build: (pw.Context context) {
return pw.Center(
child: pw.Text('Hello World'),
);
}));
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () async {
final bytes = await pdf.save();
final blob = html.Blob([bytes], 'application/pdf');
final url = html.Url.createObjectUrlFromBlob(blob);
html.window.open(url, '_blank');
html.Url.revokeObjectUrl(url);
},
child: Text('Open'),
),
ElevatedButton(
onPressed: () async {
final bytes = await pdf.save();
final blob = html.Blob([bytes], 'application/pdf');
final url = html.Url.createObjectUrlFromBlob(blob);
final anchor = html.AnchorElement()
..href = url
..style.display = 'none'
..download = 'some_name.pdf';
html.document.body?.children.add(anchor);
anchor.click();
html.document.body?.children.remove(anchor);
html.Url.revokeObjectUrl(url);
},
child: Text('Download'),
),
],
),
),
);
}
}
original answer
import 'package:flutter/material.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:universal_html/html.dart' as html;
class PdfDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
final pdf = pw.Document();
pdf.addPage(pw.Page(
pageFormat: PdfPageFormat.a4,
build: (pw.Context context) {
return pw.Center(
child: pw.Text("Hello World"),
);
}));
final bytes = pdf.save();
final blob = html.Blob([bytes], 'application/pdf');
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
children: <Widget>[
RaisedButton(
child: Text("Open"),
onPressed: () {
final url = html.Url.createObjectUrlFromBlob(blob);
html.window.open(url, "_blank");
html.Url.revokeObjectUrl(url);
},
),
RaisedButton(
child: Text("Download"),
onPressed: () {
final url = html.Url.createObjectUrlFromBlob(blob);
final anchor =
html.document.createElement('a') as html.AnchorElement
..href = url
..style.display = 'none'
..download = 'some_name.pdf';
html.document.body.children.add(anchor);
anchor.click();
html.document.body.children.remove(anchor);
html.Url.revokeObjectUrl(url);
},
),
],
mainAxisAlignment: MainAxisAlignment.center,
),
),
);
}
}