2021-06-16

FlutterでWebサービスのアプリ化を技術検証してみた話

@gc_tech70

はじめに

こんにちは。エンジニアリング事業本部の@gc_tech70です。 普段はNuxt.jsを使ってフロントエンド領域をメインに開発をしています。

現在、弊社の運営しているWebサービスであるスマートマットライトの管理画面をアプリ化したいという展望があり、その際にFlutterを使ってどの程度の要件を満たせるか技術検証したので、その知見を共有したいと思います。 私自身アプリを開発した経験はないので、基本的にWebエンジニア目線となっています。

アプリ化の技術選定

まずアプリ化を検討した際の技術選定の経緯について、お話します。 今回はネイティブ(Kotolin,Swift)、 クロスプラットフォーム(ReactNative, Flutter)の中から選定しています。

前提として大きな要件としては以下が挙げられます。

  • フロントエンドエンジニア1人、バックエンドエンジニア1人での運用・保守を可能とする
    • アプリ開発経験なし
    • Web版との並行運用
    • バックエンドはWebで運用しているGo + マイクロサービスの環境を流用
  • Android, iOSでリリースする
  • 開発工数はできるだけ短くしたい
  • プッシュ通知の実装は必須

ネイティブ(Kotolin, Swift) vs クロスプラットフォーム

ネイティブとクロスプラットフォームでは特徴が大きく異なるため、まずはこの2つについて比較していきます。

ネイティブ

  • OS最新の機能が使える
  • クロスプラットフォームに比べるとパフォーマンスに優れる
  • 学習コストが大きく、それぞれのOSで専任でエンジニアが必要
  • 最新OSがリリース前に検証可能

クロスプラットフォーム

  • 1つのコードで両方のOSの開発が可能
  • ホットリロードがあるのでネイティブに比べて開発効率が優れる(ReactNative, Flutterの場合)
  • 最新OSはリリース後に各プラットフォームで対応されるまで待つ必要がある
  • パフォーマンス面でネイティブより劣る
  • 一部のネイティブ機能が使用できないが、プッシュ通知やカメラ、GPSなど一般的な端末固有機能は対応されている

結論

ネイティブのほうがOSアップデートに追従しやすく、パフォーマンスにも優れるため、リソースに余裕があればこちらを採用したいところではありますが、現状のリソース要件ではクロスプラットフォームの方が適しているため、今回はクロスプラットフォームを採用します。

ReactNative vs Flutter

ReactNative

  • Facebook製
  • 有名アプリでの採用実績
    • Facebook
    • Instagram
    • Airbnb
    • Skype
    • Discord
    • UberEats
    • etc...
  • ドキュメントが豊富
  • ネイティブUI
  • JavaScript(Reactベース)
  • Expoという開発ツールを使えばアプリ審査を通さずにアプリの更新が可能
    • ただし、ネイティブコードは書けなくなるらしい
  • Githubスター数 96.1K

Flutter

  • Google製
  • Dart言語
  • 公式ドキュメントが丁寧で充実(ただし、英語)
  • 独自UI、マテリアルデザイン
  • 豊富なWidget(UIコンポーネントのようなもの)
  • モバイルだけでなくWebやデスクトップアプリにも対応
  • Githubスター数 122K

結論

ReactNativeのほうが現状では有名アプリの実績が結構あったり、Reactベースで書けるので、Vueを採用している弊社では比較的学習コストも低く対応できると思います。 さらにExpoという開発ツールで開発すれば、ネイティブコードを書けなくなるという制約の代わりにアプリ審査を通さずにアプリの更新が可能というメリットもあります。

Flutterは個人的に検証前から触っていたのですが、DartもJavaとJSの開発経験があれば、割と見慣れた記法なのでそこまで難しい印象はなく、学習コストに関してはReactNative > Flutterかもしれませんが、大差はないかなという印象です。

また、パフォーマンスの面ではFlutterもReactNativeも一長一短なようです。 https://recruit.gmo.jp/engineer/jisedai/blog/reactnative-vs-flutter-performance/

UIの面ではReactNativeはプラットフォームによって見た目が変わりますが、Flutterはどのプラットフォームでも統一された見た目になります。 それぞれのプラットフォームで最適化されたUIとなるのが理想的ではありますが、リソース的にどのプラットフォームでも統一された見た目となるFlutterの方が単一のデザインを再現するだけで済むので現状の要件ではFlutterの方が都合がいいように思います。

上記のようにFlutterとReactNativeではどちらかが一方的に優れているというわけではないですが(JSしか書きたくないということであればReactNative一択だと思いますが)、Githubスター数からも分かるようにFlutterの方が最近は勢いがあることやモバイルアプリに限らずWebやデスクトップなど複数のプラットフォームに対応できる展望があることから将来性の面も加味すると、Flutterに軍配が上がるかなというところです。

ということで今回はFlutterを採用してアプリ化が可能かどうか技術検証していくことにしました。 ※ちなみに検証中の開発体験が悪かったり、技術的なブロッキングポイントが見つかれば別の技術に変える可能性はあり、とりあえず試してみるというぐらいのスタンスです。

参考記事

https://zenn.dev/tetsukick/articles/c297b6ee1e64397432e5 https://qiita.com/nskydiving/items/41e446ef5c821359ab79 https://macro-send.com/blog/reactnative https://bagelee.com/programming/react-native/react-native-apps-example/ https://qiita.com/tetsukick/items/a883f73b526eb2a63b8a https://qiita.com/nskydiving/items/c13c949cc17c6f980a67

機能検証

機能的な要件は以下の通りです。

  • プッシュ通知
  • バーコード読み取り
  • 接続可能なSSIDの一覧化
  • IoT機器(スマートマット)とのTCP通信
  • Login with Amazon(LwA)
  • 外部Webサイトをアプリ内(Webview)で表示
  • 特定のURLにアクセス時にアプリを起動(ディープリンク)

また、以下の非機能要件についても調査しました。

  • 開発中のアプリの社内配布

検証環境

  • iOS 14.2
  • Android 11

※この検証は2020/12 〜 2021/1に検証した内容なので、現在のライブラリやOSのバージョンによっては内容が異なる可能性があります。

検証結果

検証内容と結果を全部この記事に記述しようとしたのですが、ボリュームが大き過ぎて何の記事か分からなくなるレベルだったので、とりあえず今回はざっくりと結果だけ書いていきます。 それぞれの実装方法など検証内容の詳細については今後別記事として執筆していきたいと思います。

プッシュ通知

  • 両OS実装可能
  • Flutterのプッシュ通知に関してはFirebaseのFCM (Firebase Cloud Messaging)というプッシュ通知用のサービスが特にメジャーらしい
  • firebase_messagingというライブラリが公式でサポートされていて、ドキュメントも豊富
    • ただし、Android、iOSそれぞれでセットアップが必要であり、実装は結構手間がかかる印象
    • これについてはクロスプラットフォームだから実装が楽とかはなさそう

スクリーンショット 2021-06-16 18.26.57

バーコード読み取り

今回の検証にはこちらのライブラリを使用しました。 https://pub.dev/packages/barcode_scan

  • 両OS実装可能
  • バーコードに加えて、QRコードからも任意の値を取得が可能
  • ただし、2020/05/08よりメンテナンスが停止したことが明示されているため、OSのバージョンアップに注意が必要

バーコード読み取り

接続可能なSSIDの一覧化

  • Androidに関しては以下のライブラリで対応可能
  • iOSではSSIDの一覧を取得するのにNEHotSpotHelperというAPIの利用許可をAppleから貰う必要があるため、iOSに対応したライブラリは存在せず、ネイティブコードによる独自実装が必要 https://akiblog.org/how_to_get_ssid/
    • 今回の検証段階ではまだ提出できるアプリがなく、Appleから利用許可が下りなかったため未検証
    • 利用許可が降りないと技術検証もできず、本格対応した時にも利用許可が下りる保証がないため、Androidと同等の機能が実装できるのか懸念あり
    • OS標準のWi-Fiの設定画面にユーザーが遷移しないとSSIDの情報が取得できないらしいので、導線の設計に注意が必要

IoT機器(スマートマット)とのTCP通信

  • 両OS実装可能
  • Dartの標準APIでSocket classが用意されているため、ライブラリも不要でOSによる依存もなさそう
    • ただし、需要が少ないせいか参考になるドキュメントはほぼないため、エラーでハマる可能性がある

Login with Amazon(LwA)

  • 両OS実装可能
  • SDKがFlutterに対応していないため、OSごとにそれぞれネイティブコードでの実装が必要
    • さらにSwift、KotolinではなくObjective-CとJavaにしか対応していないため、Flutter => Swift => Objective-C => SDKのようにブリッジする必要があり、学習コスト大(今回の技術検証最大の苦行)
  • Amazon独自のSDKのため、パッケージ管理できず、それぞれのOSごとにローカルに落としてSDKをインポートする必要があるため、一工夫必要
    • この辺のセットアップ方法もあまりドキュメントがなく苦労した

外部Webサイトをアプリ内(Webview)で表示

特定のURLにアクセス時にアプリを起動(ディープリンク)

  • 両OSで実装可能
  • Firebase Dynamic Linksを使用
    • ブラウザやメールから特定のリンクを叩かれた時に、アプリを開くことができる
    • また、アプリを未インストールの場合、各OSに対応するアプリストアのダウンロード画面に遷移させることができる
    • https://pub.dev/packages/firebase_dynamic_links

screen-20210315-095104

開発中のアプリの社内配布

  • DeployGateで手軽に配布可能
    • 両OS対応
    • 基本無料
    • ビルドしたipa, apkファイルを管理画面からアップロードするだけ
      • 専用のアプリから配布したいアプリをダウンロードできる

DeployGate

所感

Flutterで諸々の技術検証を実施した結果、Flutterでも様々の機能の実装が可能なことが分かり、アプリ開発経験ゼロの自分でもある程度まともなアプリの実装が可能だということが分かりました。 特にUI的な面で強く、Webで実装できそうな機能要件の範囲ではネイティブと比べても大幅に実装工数を減らすことができそうです。

開発体験

Flutterはホットリロード、公式ドキュメントの充実、エディタ(VSCode、Android Studio)の拡張機能を公式がサポートなど、開発体験はかなりいいように思いました。 また、UIコンポーネントのような感覚で実装できるので、UI面に関しては複雑なデザインを求めていなければマテリアルデザインで整ったUIが手軽に実現できる点が魅力的でした。(CSS嫌いな人にもおすすめできそう)

その他、Dartはオブジェクト指向言語で静的な型付けも可能なため、補完が効いてサクサク書けるほか、Widgetのオプションなどもドキュメントを調べなくても定義ジャンプすればざっくりどんなものがあるか知れるので、そういった点についてもとても使いやすい点だと思いました。 例えば、Containerclassにカーソルを当てると、marginやpadding、width、height、colorなどが設定できることがエディタ上で分かります。

スクリーンショット 2021-06-15 23.34.20

一方で気になった点として、Flutterは性質上、コードのネストが深くなりやすいのでチーム開発する場合には、他の言語と比べても特に可読性を意識したコーティングが重要だと思いました。

Flutterに限らずクロスプラットフォームという目線で言うと、iOSで独自実装の必要が出た場合、XCode以外でコーディングできないので、慣れてないエディタでコーディングしなければいけない点やXCodeがアップデートを頻繁に要求してきて、その度にストレージも40GBぐらいは空けておかないとアップデートできなかったりするので、苦労するポイントはそこそこありました。

学習コスト

Dartは静的型付けに対応しているので、TypeScriptみたいな感覚で書けますし、Flutter自体もReact,Vue,Angularなどを書いたことがあれば同じノリでコンポーネント(FlutterではWidgetと呼ぶ)作っていけばいいので、Flutterの記法にさえ慣れれば概念的には大分似ていると思います。

非同期通信もおなじみのasync, awaitを使う感じですし(axiosっぽいライブラリもある)、状態管理もhooksが使えるので(あんまり検証してませんが)、ある程度モダンな環境でWebフロントエンドの開発経験がある人であれば学習コストは低めだと思います。 一方でWeb開発者目線だとReact,Vue,Angularなどのフレームワークに馴染みがない方だと状態管理やコンポーネントの設計まで覚える必要があるので、慣れるまでに少し時間がかかるかもしれないと思いました。

また、ネイティブコードを書く必要がある場合には、Flutterに加えてKotolinやSwiftも結局覚えなければならないので、学習コストは上がると思います。 さらに要件によってはObjective-CやAndroid向けのJavaまで覚える必要があるので、そこまで行くと慣れるまで学習コストは高いと言わざる得ないかもしれません。 自分が触った感触ではSwiftやKotolinはオブジェクト指向の言語触ってればまあなんとなくは読める感じだったので、そこまで問題じゃないかもしれませんがObjective-Cの癖が強くて、どうやって書けばいいか分からず大分苦戦しました。

実装上の課題

OS依存が強い機能では、同一ソースコードで複数のOSに対応できるクロスプラットフォームの強みを活かせず、OSごとに個別の実装が必要であったり、公式のライブラリが存在せず、サードパーティのライブラリだとメンテナンスが特定のOSのバージョンから止まってしまうなどのリスクもあり、ライブラリを使ったとしても最悪使えなくなった時の対策として、直接ネイティブコードを書いて同一の機能を自力で実装できるぐらいには実装方法をざっくり把握しておくことが必要だと思いました。 そのため、それぞれのOSのバージョンアップには常に警戒しておく必要がありそうです。

また、LwA(Login with Amazon)のようなベンダーのSDKを使いたい場合、現状ではほとんどFlutterに対応していることはないと思うので、それぞれのOSで個別にSDKをインポートして利用する必要があり、実装がかなり手間になってしまうようです。(場合によっては使用できない可能性もあるかもしれません)

ただし、これらの問題はおそらくFlutterに限らずクロスプラットフォーム全般に言えることなので、仮にReactNativeを採用したとしても同様の問題が発生すると思われます。

OSごとの実装難度

iOSのほうがAndroidに比べてセキュリティポリシー的に厳しい影響で、ライブラリもAndroidのみ対応しているものも多くあり、OS依存の機能が多い場合、iOSのほうがライブラリが使えない可能性が高いので、実装難度はiOS > Androidとなりそうな印象でした。

また、自分が参加したFlutterの勉強会ではAndroid開発者の方はKotolinやJavaではなく、あえてFlutterを使っていると言う人もいるぐらい開発体験がいいそうなので、AndroidアプリのみのサポートとなったとしてもFlutterを採用する価値はあるようです。

まとめ

Flutterのメリット

  • ホットリロードやエディタの拡張機能の公式サポートなど開発体験がよい
  • 公式ドキュメントが充実
  • UI部分は手軽に整った画面が実装できる
  • 一般的によく使用されている機能は大体実装できる
  • Flutterのみで実装を完結できる場合、学習コストは低め

Flutterのデメリット

  • OS依存が強い機能は公式ライブラリがなく、サードパーティーのライブラリの使用やネイティブコードを直接書く必要がある
    • ライブラリが継続メンテナンスされないリスクがある
    • 学習コストが上がる
  • ベンダーのSDKがFlutterに対応していないことが多い
  • ネストが深くなりやすいので、可読性を意識できないと保守しにくい

結論

結論、弊社のスマートマットライトのアプリ化に対してもFlutterを採用することはできますが、弊社のプロダクトのようなOS依存の強い機能やベンダー独自のSDKを使用する要件がある場合には相応の導入、運用コストがかかるので、注意する必要がありそうです。

Flutterの採用を検討する場合、以下のような順番でおすすめできるかと思います。 1. OS依存の機能要件なし、AndroidとiOS両方のサポート 2. OS依存の機能要件あり、Androidのみのサポート 3. OS依存の機能要件あり、AndroidとiOS両方のサポート

最新の記事