![見出し画像](https://assets.st-note.com/production/uploads/images/155207355/rectangle_large_type_2_01546fc0ec76ee83ffc4acfb491d8fea.png?width=1200)
【Flutter】「PDFの作成」及び「プレビューと印刷」
概要
Flutterアプリ開発における「PDF作成」及び「プレビュー/印刷」の実装についての備忘録を記す📝
アプリにPDFの機能を付ければ
・入力に応じPDFの作成
・特定フォーマットにおけるPDF出力
・結果一覧のPDF出力
・CSVなどを所定の表示にしてPDF出力
・PDF作成アプリ
などなど、開発の幅が一気に広がるであろう。
また、PDF出力のみ有料オプションにしても良いだろう。
ちなみに、記事を書きながら思ったのが「note」記事のエクスポート機能だが『PDF』形式にも追加対応してくれたら良いかもねと思った。
![](https://assets.st-note.com/img/1726974874-umzaTntfChRIlPwKUX5gF7Dj.png?width=1200)
PDF機能の実装
検証環境
●検証環境
macOS Sonoma 14.3
VSCode 1.92.1
Flutter 3.22.3
Dart 3.4.3
※ 基本的なFlutter開発環境は整っている事を前提とします
プロジェクトを作成
●新規プロジェクトの作成
1.コマンドパレット(Command + Shift + P)を開き「flutter」と入力し、「Flutter: New Project」を選択。
2.一番上の「Application」を選択。
3.ワークスペースとなるディレクトリを選択(この中にプロジェクトが作成されます)
プロジェクト名は「note_flutter_pdf」とします(今回の記事では画像は省略)。
下準備
●ビルド対象
ビルド対象デバイスをWebにしておく。
(Android/iOSの動作確認は後でする)
・コマンドパレット(Command + Shift + P)を開き「flutter」と入力し、「Flutter: Select Device」を選択。
![](https://assets.st-note.com/img/1727059529-DtudMRTFIrsj0EUcXVf7SK5J.png?width=1200)
「Chrome」を選択。
パッケージの導入
以下、2つのパッケージを導入する
pdf:PDFの作成で使用
printing:PDFの出力で使用
PDFを作成しても出力しなければ意味がないので、「pdf」と「printing」は基本、セットで導入します。
「pubspec.yaml」のdependencies:ブロックに追記します
pdf: ^3.11.1
printing: ^5.13.2
ウィジェット/インポート
●ウィジェット
PDFのコンテンツには、標準ウィジェットは使えません。
そのため、pdf/widgets.dartにある「PDF用ウィジェット」を使って作成します。
●インポート
import 'package:flutter/material.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
各パッケージのインポート。
pdf/widgets.dartですが、標準ウィジェットと名前が被ります。
衝突を避けるため、別名インポートします。
( PDFコンテンツ作成には、「pw.XXXX」のように、PDF用ウィジェットを使う)
PDF作成
PDFを作成していきましょう。
●Documentの生成
final pdf = pw.Document();
まずは、Documentインスタンスの生成をします。
●ページ作成
final page = pw.Page(
build: (pw.Context context) {
return pw.Center(
child: pw.Text("PDF Test"),
); // Center
}
);
次にページを作成します。
ページコンテンツは「PDF用ウィジェット」にて作成していきます。
とは言え、ほぼ標準ウィジェットと同じ感覚で使えます。
違いと言えば、先ほど別名インポートした「pw」を使う(pw.XXXX)ぐらいでしょうか。
●ページ追加
pdf.addPage(page);
ドキュメントにページを追加する
●関数化
PDF作成用の関数です。
Future makePdf() async {
final pdf = pw.Document();
final page = pw.Page(
build: (pw.Context context) {
return pw.Center(
child: pw.Text("PDF Test"),
); // Center
}
);
pdf.addPage(page);
return pdf;
}
単に関数にしただけです。
PDFの作成は「時間が掛かる処理」とされるため非同期にします。
プレビュー表示
●PdfPreviewウィジェット
作成したPDFをプレビュー表示させましょう。
PdfPreview(// プレビューの表示
build: (format) async {
final pdf = await makePdf();
// .save()で「Uint8List」形式の作成
return await pdf.save();
},),
);
PdfPreviewウィジェットでプレビューの表示が出来ます。
.save()メソッドで「Uint8List」形式にして返します。
●画面側
画面側の実装をしましょう。
void main() {
final body = Center( // ボディー
child: PdfPreview(// プレビューの表示
build: (format) async {
final pdf = await makePdf();
// .save()で「Uint8List」形式の作成
return await pdf.save();
},),
);
final sc = Scaffold(
body: body, // ボディー
);
final app = MaterialApp(home: sc);
runApp(app);
}
main関数はこんな感じになります。
全体のコード
main.dart(コピペで使えます)
import 'package:flutter/material.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
void main() {
final body = Center( // ボディー
child: PdfPreview(// プレビューの表示
build: (format) async {
final pdf = await makePdf();
// .save()で「Uint8List」形式の作成
return await pdf.save();
},),
);
final sc = Scaffold(
body: body, // ボディー
);
final app = MaterialApp(home: sc);
runApp(app);
}
// PDF作成
Future makePdf() async {
final pdf = pw.Document();
final page = pw.Page(
build: (pw.Context context) {
return pw.Center(
child: pw.Text("PDF Test"),
); // Center
}
);
pdf.addPage(page);
return pdf;
}
とにかく短さを意識し「40行未満」で書きました。
おそらく世界最短レベル。
動作確認
では、動作確認をして見ましょう
![](https://assets.st-note.com/img/1727061874-hnK1Hq3PdGFwjDC6M2UXTolr.png?width=1200)
一見、真っ白だが、テキストはど真ん中に表示しているので
![](https://assets.st-note.com/img/1727061874-CJz0bSBlQqhWVtEw1e7xud98.png?width=1200)
下にスクロールすると「PDF Test」が出てくる。
プレビューワーの機能許可
アンダーバーに以下の機能選択が付加されてます
1.「印刷」ボタン
2.「シェア」ボタン
3.「ページフォーマット」の選択
4.「向き(縦か横)」の切り替え
5.「デバッグ」機能の可否
順々に試して行きましょう
![](https://assets.st-note.com/img/1727061874-3xrHseqnKlZ80FuRWNdpVfPX.png?width=1200)
1.「印刷」ボタンで印刷画面表示。この「保存」ボタンで保存が出来る。
2.「シェア」ボタンですが、Webの場合は押した瞬間にPDFがダウンロードされます。
![](https://assets.st-note.com/img/1727061874-94sFkRojaGyC02K1cEUlwVXM.png)
3.「ページフォーマット」の選択。
A4かLetterの選択肢。
![](https://assets.st-note.com/img/1727061874-h8OE3Hc9RzJD1qdX5ps0libP.png)
4.「向き(縦か横)」の切り替え
Webのためか、テキストがど真ん中に一行だけのためか、切り替えても余り変わらないような気もした
![](https://assets.st-note.com/img/1727061874-L0oKJ9aQZGBUbqNxpi4hskIW.png?width=1200)
5.「デバッグ」機能の可否
こんな感じになる
●パラメーター調整
サジェストでPdfPreviewの引数を確認してみる。
![](https://assets.st-note.com/img/1727061874-L6OjMnhAmTx7sDUpPHWu5Ezi.png?width=1200)
まあ、これは一部ですがパラメーターの調整をしてみましょう。
![](https://assets.st-note.com/img/1727061874-Tq15ESwQarUjinLcOVgM7Xp9.png?width=1200)
印刷許可だけ残して見ると
![](https://assets.st-note.com/img/1727062782-sUZnwqQK4VcTFoEfYL0gljrb.png?width=1200)
「印刷」ボタンのみになる🖨
![](https://assets.st-note.com/img/1727061874-TxHqOnW1ypGNm3wlR074SFdu.png?width=1200)
印刷許可も残さないようにすると
![](https://assets.st-note.com/img/1727061874-GscArZmWzD74j1UYfhHJIOdE.png?width=1200)
プレビューワーのみになる📄
それにしても、たったの40行のコードだけで、これだけの機能が実装できるとは ……
Flutterのパッケージ恐るべしですね。
ただ、このままでは日本語を使うと文字化けします。
次項にて「日本語フォントに対応」して見ましょう。
日本語フォントに対応する
フォントをダウンロードする
●日本語フォント対応
日本語フォント対応は、2つの方法がある。
1.日本語フォントをダウンロードして、アセットに設置する
2.PdfGoogleFontsクラスによるフォント取得(Googleの用意したフォントに限られます)
本記事では「1」のアセットに設置する方法を説明する
(「2」に関しては技術書出版の予定)
●フォントファイルの種類
FlutterのPDF出力では「.ttfファイル」を使います。
●GoogleFonts公式
GoogleFonts公式(https://fonts.google.com)にアクセス
![](https://assets.st-note.com/img/1726975723-UGN42cxjgVPeF3nSOt0aby5k.png?width=1200)
最初は、この「Noto Sans Japanese」が良さげに思ったのですが、FlutterのPDF出力で使うと何故か文字化けしてまう ……
●フォントのダウンロード
で、調べて見ると、Shippori Mincho(しっぽり明朝)が良さげなので、今回はこれを使います。
![](https://assets.st-note.com/img/1726975555-Hn6p5Ky8kjzu3Ze4WhPrGvib.png?width=1200)
shippori で検索をかける
![](https://assets.st-note.com/img/1726974603-6bakFtCyP07mVfgcOjwqlDiJ.png?width=1200)
「Get Font」ボタン押下
![](https://assets.st-note.com/img/1726974603-N2rzp0YKmyVPBbLH8UFtSs3u.png?width=1200)
「Download All」ボタン押下
![](https://assets.st-note.com/img/1726974603-ZO8jID34HJaAm9RnWklfTXLs.png)
すぐ左にある、このボタンからもダウンロード出来そう
![](https://assets.st-note.com/img/1726974603-3wQCKjBkL2sIYgOWl6Jr5m8b.png?width=1200)
ダウンロード完了したらディレクトリを開く
![](https://assets.st-note.com/img/1726974603-lQNVrPueMGixB3Y7RUs12qd5.png)
.zipファイルなので解凍する
![](https://assets.st-note.com/img/1726974603-XrjTWRSiVqhfuo13KQ8Msmlc.png?width=1200)
太さの段階が一通りあるので
![](https://assets.st-note.com/img/1726974603-hBnPxtMS0R3gACfbNyK5QwU7.png)
「Regular」を使いましょう。
注意点
【注意点】
フォントを使う際には
・有料か無料か?
・商用利用は可か?
など、規約を確認しておきましょう。
『しっぽり明朝』 については、「無料」かつ「商用利用可」とあります(※ SILオープンフォントライセンス)
アセットに設置する
●アセットに設置する
プロジェクト内にフォントファイルを置きます。
慣例的には
1.assetsフォルダを作成
2.その中にfontsフォルダを作成
3.その中にフォントファイルを置く
となるかと思います。
![](https://assets.st-note.com/img/1726978208-o98cXQEjl2wZBGROMSb4yIxp.png?width=1200)
![](https://assets.st-note.com/img/1726978417-kvVF2Ea1GmcS0eIyNrdxPZWC.png?width=1200)
●アセットの定義
アセットに置いたフォントファイルを使うには「pubspec.yaml」にパスを定義する必要があります。
flutter:
uses-material-design: true
fonts:
- family: Shippori
fonts:
- asset: assets/fonts/ShipporiMincho-Regular.ttf
![](https://assets.st-note.com/img/1726974603-ro8IibewDMPuYQ02NGnZRUgx.png?width=1200)
実装例
●フォントの取得 コード内では、rootBundleを使いフォントファイルを取得します。
( servicesパッケージのインポートが必要です )
import 'package:flutter/services.dart'; // これを追記
・rootBundleを使い、アセットにあるフォントを取得する例
final fontData = await rootBundle.load('assets/fonts/ShipporiMincho-Regular.ttf');
final font = pw.Font.ttf(fontData);
・使用例
final page2 = pw.Page(
pageTheme: pw.PageTheme( // ページのテーマ
pageFormat: PdfPageFormat.a4, // ついでにA4にして見る
theme: pw.ThemeData.withFont(base: font), // フォントを設定
),
build: (pw.Context context) {
return pw.Center(
child: pw.Text("テキスト"),
); // Center
}
);
全体のコード
main.dart(コピペで使えます)
import 'package:flutter/material.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
import 'package:flutter/services.dart'; // rootBundleで使用
void main() {
final body = Center( // ボディー
child: PdfPreview(// プレビューの表示
build: (format) async {
final pdf = await makePdf();
// .save()で「Uint8List」形式の作成
return await pdf.save();
},),
);
final sc = Scaffold(
body: body, // ボディー
);
final app = MaterialApp(home: sc);
runApp(app);
}
// PDF作成
Future makePdf() async {
// フォントの読み込み
final fontData = await rootBundle.load('assets/fonts/ShipporiMincho-Regular.ttf');
final font = pw.Font.ttf(fontData);
final pdf = pw.Document();
final page = pw.Page(
build: (pw.Context context) {
return pw.Center(
child: pw.Text("PDF Test"),
); // Center
}
);
final page2 = pw.Page(
pageTheme: pw.PageTheme( // ページのテーマ
pageFormat: PdfPageFormat.a4, // ついでにA4にして見る
theme: pw.ThemeData.withFont(base: font), // フォントを設定
),
build: (pw.Context context) {
return pw.Center(
child: pw.Text("テキスト"),
); // Center
}
);
pdf.addPage(page);
pdf.addPage(page2);
return pdf;
}
●動作確認
![](https://assets.st-note.com/img/1727064542-gJtDMAlwuZNc6KQnvPqTSdo8.png?width=1200)
動作確認しましょう。
![](https://assets.st-note.com/img/1727064542-cWkYwumqilRZ53jPXEtf0LOv.png?width=1200)
まずは先ほどの1ページ目です
![](https://assets.st-note.com/img/1727064542-dA4VmCbs3g7GYX2ha6iEew8p.png?width=1200)
日本語を使っている2ページ目です。
文字化けせず表示されているのが分かります。
「Pixel Fold」での動作確認
Web以外でもテストして見ましょう。
●Android
![](https://assets.st-note.com/img/1727066540-XxHIkFh5vjOW7m62ioNJaGPz.png?width=1200)
Androidのエミュレーター(Pixel Fold)を使います
![](https://assets.st-note.com/img/1727066541-iUVruYJ1sNpCHfhbdk7tRKEv.png?width=1200)
やはりM3のMacbookAirだとサクサク動いて快適ですね✨
![](https://assets.st-note.com/img/1727066541-iZzjCQdEDW0OlqLMXbfKk4Ia.png?width=1200)
1ページ目の真ん中
![](https://assets.st-note.com/img/1727066699-aHGkugyzUeZM6bTOvXV4mY78.png?width=1200)
2ページ目の真ん中
![](https://assets.st-note.com/img/1727066541-7na84HyqN1eKL5dS3iAZtf6F.png?width=1200)
「印刷」ボタン押下
![](https://assets.st-note.com/img/1727066811-l4fasV3DR1vYOMHIG8xzBQct.png?width=1200)
いいですね〜🖨
![](https://assets.st-note.com/img/1727066764-5XQbo3U1w0FAStkcj42VzTJm.png)
左上のボタン押下で、表示されるメニュー
![](https://assets.st-note.com/img/1727066541-AHSvBruLKYp0s14RmXIjVzPw.png?width=1200)
「シェア」ボタンも押しちゃうぞ💡
![](https://assets.st-note.com/img/1727066541-bhdYZFxDMteNW97UaOJ4qVmv.png?width=1200)
このように色々選べる
●iOS
iOSの方もテストしたいと所ですね。
先週辺りに「Xcode16」がリリースされたので、それをFlutterと連携させてテストする予定です。
「Xcode16」導入の記事にて記載します📝
Flutter技術書の執筆予定
なお、Flutterの技術書(電子書籍)を執筆する予定です。
・表形式での表示(一覧など)
・PdfGoogleFontsクラスによるフォント取得(アセット設置の手間が無い)
・印刷ボタンの単体処理(プレビュー無し)
・PDFのマージン
・PDFの向き
・最大ページの調整
・ローディングバーの調整
・メタデーター(author: や creator: など)の指定
・MultiPageウィジェットによる複数ページに跨いでの表示
など、さらに凝った内容を書くので、ご興味のある方は是非ご購読頂ければと思います。
早ければ、2024年10月半ばより着手。
2025年の年明け辺りに出版出来ればなと考えております。
↓
※(2024-11-26 追記)Flutterアプリ開発の入門書を出版しました📚
著書
『 プログラマーにおくるFlutterアプリ開発の入門書』
2024年11月時点での最新技術をぎっしりと詰め込んであるので、アプリ開発に参画するエンジニアの人は、是非ともご覧になって頂ければと思います📱