flutterでPWA


この記事はPWA Advent Calendar 2019の14日目の記事です。
flutter on the webでのPWA対応についてです。
13日は、migiiさん
15日は、asmzさんです。

fluter for web

flutter on the webがbetaになりました🎉
個人的には、flutterはPWAととても親和性があるツールだと考えています。React Native for webなどからわかるように、最近はmobile Appの知見をWebに還元する動きが高まっているし、そもそもFlutterはWebも含めたマルチプラットフォームを目指しています。(しかも、Dartは元々、Alternative JSとして作られています)
今後、flutterはシングルコードでWeb(PWA)、Android、iOSアプリを作ることのできる夢のような可能性を持っています。

(先日のFlutter interactでの画像)

公式サイトにも、PWAを前提とした記述があります。
A connected Progressive Web Application built with Flutter
Web support for Flutter enables existing mobile-based applications to be packaged as a PWA for reach to a broader variety of devices, or to provide a companion web experience to an existing app.

しかし、現状はPWA化についてのドキュメントはありませんし、pwa Packageについてもflutter webのbadgeはついているものの、flutterでの使い方についてなどはありません。
今後、Packageの対応やNuxtのようにベースとしてPWAで作れるようになると思いますが、現段階でflutterをPWA化したい人に向けて、自分がやった方法を紹介したいと思います。

flutter on the webでPWA

flutter on the webの始め方はこちらを参考にしてください。
とりあえず、今回でいうPWA化はinstallPopUpを表示し、ホーム画面にインストール可能にする部分までを実装しようと思います。(Offline対応などは調査中)

前提として、flutterはDartで書かれていますが、Build後のファイルはproduction JavaScript compilerによって最適化されたJavaScriptにコンパイルされています。つまり、Build後の構成は他のWebAppと同じように、メインとなるindex.htmlとbuildされたjsファイルによって構成されています。
よって、buildして生成されたdeployフォルダにservice workermanifest.jsonを追加し、index.htmlに数行の記述を追加すればPWA化することができます。

flutter on the webのビルドコマンドは以下で
flutter build web
build/webが生成されます。そしてbuild/web下にmanifest.json,sw.jsを追加してあげます。

manifest.json
{
    "name": "pwa_sapmle",
    "short_name": "pwa",
    "icons": [
        {
            "src": "assets/assets/icon-512x512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ],
    "start_url": "/",
    "display": "standalone"
}
sw.js
self.addEventListener('fetch', function(event) {});

ここで重要なのは、icon画像をどこに配置するかという点で、直接build/webに追加してあげてもいいですが、flutterのassetsに追加してあげてもいいです。上記の場合は、プロジェクト下にassetsをフォルダを作成し、その中にicon画像を配置します。そしてpubspec.yamlに以下を記述します。

pubspec.yaml
  assets:
    - assets/

そうすると、build後build/assets/assetsにicon画像が追加されます。

最後にweb/下のindex.htmlに以下のように記述します。(build/webではないです)

index.html
<!DOCTYPE html>
<html>

<head>
  <link rel="manifest" href="manifest.json">
  <meta charset="UTF-8">
  <title>pwa_sample</title>
</head>

<body>
  <script src="main.dart.js" type="application/javascript"></script>
</body>

</html>

<script>
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('sw.js').then(function (registration) {
    }).catch(function (err) {
    });
  }
</script>

これでどこかに適当にbuild/webをhostingしてあげると、

このようにPWA化が可能になります。

最後に

sampleをhttps://github.com/dennougorilla/flutter_pwa_sample にあげておくのでよかったら確認してみてください。
PWA対応の定義は色々とあると思いますが、個人的にはここまでやっとけば、一応PWAと呼んでいいんじゃないかと思っています。offline対応やpush通知に関しては後々調べつつ、PackageやFlutter自身の対応を気長に待ちます。

PWAバンザイ!