flutterでPWA

この記事は[PWA Advent Calendar 2019](https://qiita.com/advent-calendar/2019/pwa)の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アプリを作ることのできる夢のような可能性を持っています。
![1_x-dPfV_g-n9ukA_IU663kg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183325/666b58f5-dc1f-89c6-e4b7-016f8e802816.png)
(先日のFlutter interactでの画像）

[公式サイト](https://flutter.dev/web)にも、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](https://pub.dev/packages/pwa)についてもflutter webのbadgeはついているものの、flutterでの使い方についてなどはありません。
今後、Packageの対応やNuxtのようにベースとしてPWAで作れるようになると思いますが、現段階でflutterをPWA化したい人に向けて、自分がやった方法を紹介したいと思います。
##flutter on the webでPWA
flutter on the webの始め方は[こちら](https://flutter.dev/docs/get-started/web)を参考にしてください。
とりあえず、今回でいうPWA化はinstallPopUpを表示し、ホーム画面にインストール可能にする部分までを実装しようと思います。（Offline対応などは調査中）

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

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

```json:manifest.json
{
    "name": "pwa_sapmle",
    "short_name": "pwa",
    "icons": [
        {
            "src": "assets/assets/icon-512x512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ],
    "start_url": "/",
    "display": "standalone"
}
```
```js:sw.js
self.addEventListener('fetch', function(event) {});
```
ここで重要なのは、icon画像をどこに配置するかという点で、直接`build/web`に追加してあげてもいいですが、flutterのassetsに追加してあげてもいいです。上記の場合は、プロジェクト下にassetsをフォルダを作成し、その中にicon画像を配置します。そして`pubspec.yaml`に以下を記述します。

```yaml:pubspec.yaml
  assets:
    - assets/
```
そうすると、build後`build/assets/assets`にicon画像が追加されます。

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

```html: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してあげると、
![Screenshot_20191213-051508_Chrome.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183325/47f2c787-bcef-ff5f-9fcc-7abfc14fb74a.jpeg)
このようにPWA化が可能になります。

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

PWAバンザイ！