🖥

Flutter Webで注意した方が良さそうなこと

8 min read

Flutter Webの今について、改めて整理する機会がありましたので記録として残しておきます。

発展が早いので随時更新する形になると思います。

CHANGELOG
  • 2021.09.18
    • Flutter stable 2.5.0 環境に更新
    • 画面遷移のトランジションアニメーションの指定が不要になったのを追記

改めて記事にせずとも、公式ドキュメントに全て集約されているのでまずはこちらを一読することをオススメします。

https://flutter.dev/web

下記、執筆時の環境です。

[] Flutter (Channel stable, 2.5.0, on macOS 11.4 20F71 darwin-x64, locale ja-JP)
    • Flutter version 2.5.0 at /Users/tsuruoka/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 4cc385b4b8 (10 days ago), 2021-09-07 23:01:49 -0700
    • Engine revision f0826da7ef
    • Dart version 2.14.0

基本的なこと

  • 2021年3月に発表されたFlutter 2でFlutter Webがstableチャンネルでも利用できるようになりました。※それまではFlutter Webを利用するためにbetaやdevチャンネルを指定する必要がありました。

    As of today, Flutter’s web support has transitioned from beta to the stable channel.
    What's New in Flutter 2.0

  • よくある一般的なフロントエンドと同様、$flutter build webとすると静的ファイル群がbuild > web配下に出力されます。
    • デプロイ時はFirebase HostingやNetlifyなどのホスティングサーバにそのまま置いて上げれば良いです
  • 商用環境で使えるのかという点については、まだ挙動に不安定さもあり微妙な感覚です(多分Flutter界隈皆同じ感覚のはず)

Flutter Webで注意すること(2021年9月時点)

1. CanvasKitのレンダリング方法だと、読み込み時に日本語が豆腐(□)になる

  • Flutter Webでよく言及されている話ですが未だに発生します。デスクトップブラウザの場合、デフォルトではCanvasKitが指定されているのでそのまま使うのが好ましいですが、どうしても許容できない場合はHTMLレンダラーを指定すれば回避は可能です。
$ flutter build web --web-renderer html

(参考)Flutter | Web renderers

2. Hot ReloadがなくHot Restartのみ

  • ネイティブアプリ開発ではHot Reloadが効いてステートが保持されたまま再レンダリングすることができますが、Flutter WebはHot Restartのみの対応となっています。

Flutter web currently supports hot restart but not hot reload.
How to perform a hot reload

  • そのため、開発時は比較的画面の大きいAndroid EmulatorやiPad Proなどで実行しHot Reloadの恩恵を受けつつ、要所でChrome実行する形が捗って良いです。
  • Android Studioの場合、runConfigurationsにネイティブ実行とChromeブラウザ実行の両方のxmlファイルを準備して切り替えるのがおすすめです。

3. ネイティブアプリで使えるパッケージが利用できないケースがある(逆も然り)

  • 普段ネイティブアプリ開発で利用しているパッケージが使えないことがあります。上手く動かない場合は一度シミュレーターなどで起動してみて、原因の切り分けをまずはしてみたほうが良さそうです。
  • PackageのWEBバッジが付与されているか否かで対応可否を確認すると良いです。
  • 例えばflutter_svgなどは以前使えなかったです
    • WEBバッジは未だについていませんが、該当Issueはクローズされていました

https://github.com/dnfield/flutter_svg/issues/173

4. SelectableTextの利用

  • 通常のTextWidgetではコピペができないため、SelectableTextWidgetを使う必要があります
  • 使い方はTextとほぼ同等に使えるはずです

To make SelectableText react to touch events, use callback onTap to achieve the desired behavior.

onTapメソッドでタッチイベントが取得できる点などの違いがあります。

https://api.flutter.dev/flutter/material/SelectableText-class.html

5. 【済】Navigator APIで普通にpushすると不要なアニメーションがちらつく

2021.09.18 追記
stable版 2.5.0でもFlutter Web, デスクトップでの画面遷移時にデフォルトでのアニメーションがつかなくなりました。これまでは単にNavigator APIでpushすると、Webアプリケーションっぽくない横スライドするアニメーションがデフォルトで付いてしまい削る実装が必要でしたが、その必要がなくなりました

82596 Removed default page transitions for desktop and web platforms. (framework, f: material design, cla: yes, waiting for tree to go green)
(参考)Flutter 2.5.0 release notes

※下記monoさんのツイートの通り2021年7月時点のbeta最新以降で対応されていたそうです。

https://twitter.com/_mono/status/1413377062346190849?s=20
Flutter Stable 2.5.0以前のワークアラウンド

下記Routerクラスなどを用意して、画面遷移時のアニメーションを切っておくとWebアプリケーションっぽい動きになると思います。Flutter Galleryでもそのような実装がなされていました。
https://github.com/flutter/gallery/blob/ef9aa9784171ccc94f7989bc38f58a1dde17ee58/lib/routes.dart#L106

class Router {
  Route<dynamic> onGenerateRoute(RouteSettings settings) {
    return NoAnimationPageRoute<void>(
      builder: (context) => const HomePage(),
      settings: settings,
    );
  }
}

class NoAnimationPageRoute<T> extends MaterialPageRoute<T> {
  NoAnimationPageRoute({
    required WidgetBuilder builder,
    required RouteSettings settings,
  }) : super(builder: builder, settings: settings);

  
  Widget buildTransitions(
    BuildContext context,
    Animation<double> animation,
    Animation<double> secondaryAnimation,
    Widget child,
  ) {
    return child;
  }
}

6. デフォルトで付与されるURLの「#」を取り除く

これも他の記事で散見されるのでご存知の方も多いと思いますが、Flutter WebのナビゲーションはデフォルトではHashで制御されるため、https://xxxx.com/#/といった形で#がパスの最初に付与されます。公式ドキュメントにも記載されておりますが、Flutter WebではURLナビゲーションとして2つの方法をとります(Vue.jsだとHashモードとHistoryモードと言われたりします。)

  • Hash(default)
  • Path

https://flutter.dev/docs/development/ui/navigation/url-strategies

These are set using the setUrlStrategy API with either a HashUrlStrategy or PathUrlStrategy.

上記の通り、ナビゲーションの方法はsetUrlStrategyメソッドでflutter_web_pluginsの所定のクラスを指定すれば適用できます。Pathによるナビゲーションを希望する場合、つまりURLの「#」を取り除来たい場合は、PathUrlStrategyクラスをsetUrlStrategyメソッドに適用すると解消されます。

pubspec.yaml
dependencies:
  flutter_web_plugins:
    sdk: flutter
main.dart
void main() {
  // `runApp()`が呼ばれる前に記述します
  setUrlStrategy(PathUrlStrategy());
  runApp(MyApp());
}

Firebase SDKの設定をFlavorごとに分けたい場合

WebアプリでFirebase SDKを利用するにはindex.htmlで各種設定を読み込む必要があり、アプリの時同様に--dart-defineを使ったFlavor対応はできません。
monoさんの下記ツイートが簡潔で、現時点ではこれ以外の方法が無さそうです。

https://twitter.com/_mono/status/1431162546170437633?s=20

個人的には、普段はDevelopment環境の設定ファイルを読み込みつつ、それ以外のデプロイ時にファイルを上書きするスクリプトを書いています。

# $ ./deploy.sh production
if [ $FLAVOR = 'production' ]; then
    # overwrite firebase configuration for production app.
    \cp -f ./firebase_initialize_production.js ./web/firebase_initialize.js
fi
index.html
<body>
  <script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-app.js"></script>
  <!-- 省略 -->
  <script src="firebase_initialize.js"></script>
  <script src="main.dart.js" type="application/javascript"></script>
</body>

Flutter Demo

Flutter Webの操作感や実装の参考にはFlutter Galleryを拝見すると良いです。Flutter公式ドキュメントに記載されているデモサイトで、ソースコードも公開されており、実装の参考になります。また、「Flutter Webでできることできないこと」の肌感を把握するのにも役立つと思います。

参考

Discussion

ログインするとコメントできます