【Flutter】公式WebViewプラグインでVALUのハイブリッドアプリを作る


VALU Advent Calendar 2019の18日目の記事になります。よろしくお願いします。
Flutterさんのところとは一切関係がありません。が、興味がありましたらそっちも見てくださいね。

わたしは最近VALUのフロントエンドを担当しています。
VALUには優秀なフロントエンドエンジニアがたくさんいる ので、アプリをWebViewで作ってみてもいいんじゃね?と思い、今回この記事を書くに至りました。

ハイブリッドアプリとは

すごく簡単に言うと、WebViewを一部、もしくは全てに用いてサービスを提供しているアプリになります。WebViewアプリと呼ばれたりもしますね。

例えば、メニュー表示などはネイティブ側の実装で行い、メインの表示はWebViewに任せる、といった運用もできます。

ハイブリッドアプリのメリット/デメリット

メリット

アプリを開発する人員/工数を削減できる

ハイブリッドアプリを作る場合、ほとんどがこの目的によるものではないでしょうか。
自社のWebサービスをアプリ化しようと思うと、アプリを作れる人員を新たに確保する必要があったり、開発に多くの工数が必要となります。ハイブリッドアプリの場合はWebViewを使うことで、低難易度で素早くアプリを作れるため、試しにアプリを出したいという要求にもすぐに答えることができます。

iOS/Androidで見た目を(ほぼ)統一できる

アプリを作る上で、iOSにはiOSのお作法が、AndroidにはAndroidのお作法がありますが、ハイブリッドアプリはモバイルページをWebViewで表示することがメインなので、お作法にあまり縛られません。モバイルページがきちんと作られていれば、デザイン崩れも気にする必要がないでしょう。

デメリット

モバイルページの完成度をあげる必要がある

ハイブリッドアプリはあくまでもWebページがしっかりレスポンシブ対応していることが前提の運用です。そのため、フロントエンド側の負担が増えることになります。

iOS/Android特有のデザインを出しづらい

こちらはメリットと対になる部分です。
ネイティブで作る場合はAndroidのMaterial DesignやiOS HIGなどに沿って作るかと思いますが、Webの場合はその限りではありません。モバイルページをブラウザで表示するのと差が出にくくなり、味気なくなってしまう可能性もあります。

どっちが良いのか?

これに関しては自社の状況によるためなんともいえません。
アプリの開発人員が足りていたり、アプリをきっちり作りたいというケースがあればネイティブアプリを開発したほうがよいですし、リソースはないがとにかくアプリを早く出したいというケースにおいては、ハイブリッドアプリにするという選択もあります。

ハイブリッドアプリを作る

前置きはここまでにして、早速ハイブリッドアプリを作っていきましょう。
とはいえ、今回は弊社サービスのVALUをWebViewで表示するだけなので、作った!という感じでもないのですが・・

今回はみなさんが大好きな!!!Flutterを使っていきます

Flutterについて

FlutterはGoogleが提供するクロスプラットフォームアプリ開発用フレームワークです。iOS、Android向けですが、ここ最近はweb向けの開発も始まっていたり、何かと話題になっています。

個人的にFlutterの好きなところは

  • コードベースでUIを組むので、UI作成ツールと格闘しなくてよい
    • アンチStoryBoredです
  • Material Designが標準採用されている
  • Hot Reloadが早い
    • UIのちょっとした変更が即時反映されるのは大きいです
    • ただ、わたしはこれまでの癖でリロードしがちなので、直したいところです

今回使用するもの

  • Flutter
    • 最新バージョンです (v1.12.13)
  • webview_flutter
    • Flutter公式で出しているWebViewプラグインです (^0.3.18+1)

これまでFlutterのWebViewといえば、flutter_webview_pluginを使っている方が多かったと思います。
もちろんこちらも使えますが、WebView上にFlutterのコンポーネントを置けないなどの制約がありました。

Warning: The webview is not integrated in the widget tree, it is a native view on top of the flutter view.
You won't be able see snackbars, dialogs, or other flutter widgets that would overlap with the region of the screen taken up by the webview.

webview_flutterも執筆時点でDevelopers Previewの状態ですが、将来性を見越してこちらを使うのはありだな、と思います。

つくる

webview_flutterのサンプルが色々用意してくれているので、そのまま持ってきて改造します。
下記はbuild部分を抜粋したものです。
*Flutter初心者ですので、ちょっと変な書き方あるかもしれません。

main.dart
  final initialUrl = 'https://valu.is';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
        actions: <Widget>[
          NavigationControls(_controller.future),
          Menu(_controller.future),
        ],
      ),
      body: Builder(builder: (BuildContext context) {
        return WebView(
          initialUrl: initialUrl,
          javascriptMode: JavascriptMode.unrestricted,
          onWebViewCreated: (WebViewController webViewController) {
            _controller.complete(webViewController);
            print('onWebViewCreated');
          },
          javascriptChannels: <JavascriptChannel>[
            _toasterJavascriptChannel(context),
          ].toSet(),
          navigationDelegate: (NavigationRequest request) {
//            if (request.url.startsWith('https://valu.is/********')) {
//              print('blocking navigation to $request}');
//              _controller.future.then((controller) {
//                controller.evaluateJavascript(
//                    'Toaster.postMessage("ごめんね、********は非対応だよ");');
//              });
//              return NavigationDecision.prevent;
//            }
            print('allowing navigation to $request');
            return NavigationDecision.navigate;
          },
          onPageStarted: (String url) {
            print('Page started loading: $url');
          },
          onPageFinished: (String url) {
            print('Page finished loading: $url');
          },
        );
      }),
      floatingActionButton: favoriteButton(),
    );
  }

コメントアウトしていますが、navigationDelegateを使うことで、URLをフックすることもできます。
また、javascriptChannelsを使うことで任意の場所から定義した処理を実行することもできます。
ネイティブのWebViewとほぼ変わらないことができるのではないでしょうか。

できたもの

Github

左がiOSで、右がAndroidです。

所用時間は環境構築も含め6時間もかかっていませんが、それなりに動くものはできました。
あとは投稿ボタンからの投稿などを追加すれば、それなりに使えるアプリになりそうです。
(APIを外部公開してないので、今回は実装していません。)

おわりに

Flutterを用いてハイブリッドアプリを作りました。
クロスプラットフォーム対応のフレームワークを使うことでさらに開発時間を短縮でき、一瞬で作れてしまうのはすごいですね。

アプリ作りたいけど工数に悩んでいるみなさん、よろしければFlutter * WebViewをお試しください!