FlutterWebでサイトを作る時に注意すべきポイント7選

こんにちは、個人でアプリ開発をしているYuKiOです。

Flutter Webでアプリのコンセプトサイトを作成して公開したので、まだFlutterWebと触れ合ったことがない人向けに注意すべきポイントを7つ紹介していきたいと思います。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">NEW🎉一生懸命作りました！<br><br>死後のアカウント管理できていますか？<br><br>「もしも」のとき、大切な人にあなたのアカウントを公開できる！<br><br>「究極のパスワード管理」アプリが登場！？🙌<br><br>■アプリの詳細ページはこちら<a href="https://t.co/ou4al9EY9R">https://t.co/ou4al9EY9R</a><br><br>リツイート頂けたら泣いて喜びます😭 <a href="https://t.co/iSuHtlqn28">pic.twitter.com/iSuHtlqn28</a></p>&mdash; YuKiO｜アプリ個人開発｜Flutter × Firebase (@oo_forward) <a href="https://twitter.com/oo_forward/status/1569566546896293888?ref_src=twsrc%5Etfw">September 13, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 1.日本語が正常に表示されない
最初に引っかかるのが、Flutter Webでは中国語フォントが採用するのか、日本語が正常に表示されません。漢字や句読点の表示がおかしくなります。

### 日本語フォントを設定していない状態
![スクリーンショット 2022-09-13 17.01.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228439/30a92644-11cb-37d3-827b-d9daf9599e75.png)

### 解決策
まず[Googleフォント](https://fonts.google.com/?subset=japanese "Googleフォント")から日本語のフォントをダウンロードして、asstes/fonts/などに.otfファイルを保存しておきます。

まず、pubspec.yamlに記載します。
```pubspec.yaml
  fonts:
       - family: NotoSerifJP
         fonts:
           - asset: assets/fonts/NotoSerifJP-Regular.otf
           - asset: assets/fonts/NotoSerifJP-Bold.otf
       - family: NotoSansJP
         fonts:
           - asset: assets/fonts/NotoSansJP-Regular.otf
           - asset: assets/fonts/NotoSansJP-Bold.otf    
```
それぞれやり方はあると思いますが、全体のテーマでフォントを設定しておきます。
```dart:app_theme.dart
class AppTheme {
  static ThemeData defualt(BuildContext context) {
    return ThemeData(
      primarySwatch: Colors.blue,
      fontFamily: "NotoSansJP",
      appBarTheme: AppBarTheme(
          color: AppColors.main,
          toolbarTextStyle: TextStyle(color: AppColors.text)),
    );
  }
}
```
```dart:main.dart
//main.dartの一部抜粋
@override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: '究極のパスワード管理',
      theme: AppTheme.defualt(context),
        //〜〜省略〜〜//
       );
}
```
## 2.画像が表示されない
Flutter Webで地味にハマったのが、画像が表示できない問題です。
通常Flutterでアプリを作る場合、assets/〜以下に保存している画像ファイルは、ファイル名で読み出しすることが多いと思います。

しかし、Flutter WebでFirebase Hostingした際に、画像が表示されませんでした。これは画像の指定をプロジェクトルートから指定することで解決しました。

```dart:main.dart
//これだと表示される。
Image.asset("test.jpg");

//これだと表示されない。
Image.asset("assets/images/test.jpg");
```

## 3.レスポンシブ対応を考えないと痛い目に遭う。
Webではパソコンやスマートフォンなど多様なサイズで表示されるため、レスポンシブという表示サイズによって表示内容を変更する必要があります。

Flutter Webではブラウザサイズ幅を変更すると自動的に再ビルドが走ります。そのためブラウザいっぱいに画像を表示した場合、ブラウザサイズ幅によって柔軟に変更されるので、レスポンシブ対応されています。

そこで問題になってくるのが、文字サイズやPaddingサイズです。

普段アプリを作っている場合は、スマホサイズを意識しておけば問題ありませんが、Flutter webで作成してる場合、文字サイズやPaddingを定数で設定してしまうと、スマホで見た時は最適なサイズ感でも、パソコンで文字を見ると小さすぎる。またはその逆のパターンの方が悪影響で、パソコンのブラウザでデザイン作ってから、スマホでみたらレイアウト崩れまくりなんてことも。

それは文字やPaddingサイズが固定だと、画面が小さくなってもスペースをキープするので、相対的に大きくなりすぎて、うまく表示できなくなるのです。

レスポンシブ対応にするために、Paddingや文字サイズなど、サイズを司るものはすべて変数にしました。

```dart:main.dart
Widget build(BuildContext context) {
  final double screenWidth = MediaQuery.of(context).size.width;
  final double nomalFontSize = screenWidth < 500 ? (screenWidth * 0.06) : (screenWidth * 0.05);  
   //省略
}
```
もしアプリとWebを作る様な場合には、文字サイズやPaddingなど司る数値を管理するクラスを作った方が良いかもしれません。これは余談ですが、パソコン表示とスマホで表示方法を少し変えています。

### PCプラウザの表示
![スクリーンショット 2022-09-13 17.30.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228439/794a6bdf-6c29-6134-fbbe-f9db8e07f6a7.png)


### スマホブラウザの表示
![スクリーンショット 2022-09-13 17.31.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228439/ee259917-e5db-d09b-898b-bbe6d50954ad.png)

## 4.レンダラーによる表示の違い
Flutter Webでは、本番環境へデプロイする際にビルドをする必要があるのですが、Webを表示する際に使用するレンダラーを選択することができます。

■レンダラーの説明
https://docs.flutter.dev/development/platform-integration/web/renderers

簡単に言えば、パソコン用にしっかりと表示してくれるが重い「canvaskit」とスマホ用に軽量だが簡易表示になる「html」があります。

何も指定しないでビルドすると、Flutter Webが読み出ししてきた端末によって自動で判別するのですが、あらかじめ指定することができます。

この後に記述しますが、Flutter Webは一般的なWebサイトと比べて動作が遅いので、なるべく軽量化したい欲に駆られます。

そのためスマホ用のレンダラー「html」を指定したくなりますが、こちらにも罠があります。

「html」を使った場合に、正常に描画されないWidgetが存在したりするのです。

今回観測したものでは、CustomPaintの図形描画関連のWidgetが「html」を使用すると表示されませんでした。記憶が不確かですが、Flexibleなどの拡張されるWidgetもエラーが出て正常に動作しなかったかと思います。

事前検証せずに不要にWidgetを使いするぎると、思わぬところで表示されないということがあるので気をつけたほうが良いかなと思います。

## 5.読み込みが遅い、読み込み表示が真っ白。
Flutter Webでは初回読み込みする際に、レンダリング？するために少し時間が掛かります。PCなら気にならない、いや気にならないと言いかせて、なんとか我慢できますが、スマホの場合はもし商業サービスなら致命的な離脱を生むのでは？と思えるくらいに時間かかります。

おまけにデフォルトでは、真っ白な画面です。

読み込みを早くするのは現実的には難しいので、できる対策としては２つ
- 読み込み中ということを表示させる
- 読み込み中の時間をコンテンツ化する

ローディングについては、この記事がわかりやすかったので、チェックしてみてください。
https://techgamelife.net/2021/03/25/flutter-web-loading/

このローディング画面では、dartが使えません。装飾するにはHTMLとCSSの知識が必要になります。もしHTMLとCSSが初めてという方はそんなにむずかしくないので、この機会に勉強してみるといいと思います。

画像は、Firebase hostingであれば、 Flutterプロジェクトで画像の保存場所をasstes/test.jpgとしていた場合、assets/assets/test.jpgの配下になります。

```html:index.html
    <div class="loading">
      <ul>
        <li class="imageView"><img class="logImage" src="/assets/assets/logo.png" alt="ロゴ" title="ロゴ"></li>
        <li class="textView"><h1>Q もし明日が最後の日なら、あなたは何をしますか？</h1> </li>
        <li class="loadingVIew"> <div class="loader"></div><h2>　一生懸命読み込み中・・・</h2></li>
    </ul>   
    </div>
```

ここでは、少し質問を投げかけることで待ち時間へのストレスを軽減させてみようと思いました。このあたりJavaScriptの知識が豊富なら、簡単なゲームやアクションができるようなものを作っても面白いかもしれませんね。

![IMG_0188.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228439/7fc18380-0f42-9a3b-22ac-b525a2e83112.jpeg)


## 6.スマホをWeb画面上で再現
今回やってみたかった事として、Web上にスマホを再現することを挑戦してみました。
今回作成したページはアプリのランディングページなのですが、その中でアプリのデモンストレーションができればと考えてわけです。
せっかくスマホでも、Webでも動くのだからとやってみたのですが、これが難しくて、最終的にはあきらめました。

パッケージを使う方法もありますが不恰好ですし、こちらも要件を満たせませんでした。

まずpng形式のスマートフォンフレーム画像と、MaterialAppをStackさせてから、その配下でアプリの処理動かすようにしました。IgnorePointerを使えば、画像が上に重なってもタッチイベントを下に伝えられます。

こうすると構成はWeb用のMaterialAppの配下に、アプリ用のMaterialAppを配置する感じになります。

ここで問題なのが、これだとレスポンシブWebの場合、アプリ用のMaterialAppのサイズを抑制できませんでした。

調べた結果、採用を見送ったパッケージでも使用されているRepaintBoundarでApp用のMaterialAppを囲むことで、サイズを抑制ができました。

https://api.flutter.dev/flutter/widgets/RepaintBoundary-class.html

ただ、ここにも問題があり、ダイアログが枠を超えて飛び出てしまい抑制できていません。

もしかすると、ダイアログもRepaintBoundarで囲んだりすると抑制できるのかもしれませんが、時間が無かったので今回はここで諦めました。

## 7.ルーティングの作法が違う
画面推移を少し考えないとドツボにハマるんかなと思いました。今回は画面推移がないのでそこまで問題ありませんが、例えばアプリのように画面を重ねるルーティングにすると、Webで戻るを押した場合にサイト全体がバックしてしまうなんてことがあります。
反対にアプリ側でWebのように重ねずにリプレスで置き換えていくと、アプリとして画面管理が大変かと思います。

もしかしたらNavigator2.0などはうまい感じでやってくるのかもしれませんが、このあたりはアプリとWebを同時にやるのであれば、ここは地味に見逃せないポイントかなと思いました。


## FlutterWebとアプリの同時開発はありなのか？
個人的な感覚としては、予算を抑えたい、でもWebもアプリも欲しいという企業に限ってはFlutterWebを採用するのはアリかなとは思います。
しかしながら、Flutter Webの待ち時間を考えると商業サービスの場合は、モバイルの場合は必ずアプリにリダイレクトする、もしくはアプリ利用を促すようにしないと、ユーザー体験が悪くなってしまう可能性があるかなと思いました。

特にデザインの面で制約が多くなりそうな気もします。

また、アプリとWebと守備範囲を考えると、今後パッケージによって、アプリは問題なけれど、Webは問題が発生するなど、地雷などもありそうな気もする。

実装時のアプリとWebの切り分けの手間などを考えると、Flutterは使うけれども、FlutterアプリとFlutterWebは別のプロジェクトにしておいた方が幸せになれるのではないかなとも思いました。

部分部分でコピーすればいいので、同じプロジェクトで作る利便性と手間を天秤にかけると、同じくらいもしくは少しいいのではないかと予想しています。

個人的はFlutterの技術でそのままWebを作れるので、ランディングページなど静的なWebサイト作りでも活用できるかなと思いましたが、やっぱりパフォーマンスがネックになるかなと。

それなら、HTMLやCSSなどでサイトが組めないのであれば、素直にワードプレスやランディングページ作成サービスなどを使えばいいかなと感じました。

今回は静的なサイトでしたが、動的も作ってみよかなと思います。