🖥

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

2021/08/30に公開

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の利用

  • 通常の Text Widgetではコピー&ペーストができないため、SelectableText Widgetを使う必要があります
  • 使い方は 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