こんにちは。ナビタイムジャパン Android/iOSアプリ開発担当の KSK です。
最近流行りのFlutterの流れに乗ってみたい一心で Flutterをプロダクションに導入したお話をしたいと思います。
はじめに
FlutterとはGoogle製のクロスプラットフォームのアプリ開発フレームワークです。2018年12月に正式版として1.0がリリースされて以降、徐々にプロダクションに採用する企業が増えてきました。弊社でも2019年に入り、複数のプロダクトにFlutterを採用・リリースしましたので、その経緯などをご紹介します。
どのプロダクトに採用したのか?
新規アプリケーションで採用しました。みなさんがご存知の「NAVITIME」や「乗換NAVITIME」は既に大規模なネイティブアプリが運用されており、これをFlutterで置き換える選択肢は今のところありません。既存のアプリをFlutterに置き換えるべきか、という議論は後ほど改めて触れたいと思います。
採用理由
採用理由は主に以下4点です。
(1)Flutterの安定性と普及率
冒頭で述べたことと重複しますが、正式版がリリースされたことで安定性が、そして海外だけではなく国内での採用事例も増加傾向にあることで信頼性も向上しました。加えて、Googleが開発していることも大きなプラス要素です。
過去に他のマルチプラットフォームを検討したこともありましたが、その時は様々な観点から熟考の末、見送りになりました。他のマルチプラットフォームの場合、描画エンジンがネイティブのViewを使っているために、OSアップデートのキャッチアップにも不安がありました。一方、Flutterの描画エンジンはskiaを用いた独自の描画エンジンのため、OSに大きく依存することはありません。その点での信頼性も評価しています。また、FlutterのコードにはGoogleの次期OSと噂されるFuchsiaという名前もあり、Androidが終わってFuchsiaに移行したとしてもFlutter製のアプリは動くのではないかと言われています。正直、その頃まで今のアプリを運営しているかはわかりませんが、Flutterとはそれだけ先を見据えたものであり、将来性もあると言えます。
(2)対象アプリの機能要件を満たせること
皆さんもご存知のような弊社のナビゲーションアプリは、センサー類など端末の機能を使います。また、ナビゲーション機能は複雑なロジックを含むこともあり、センサー類の処理と一緒に社内ライブラリとしてAndroid/iOSそれぞれ用意しています。そのため、弊社のナビゲーションアプリをFlutterで作ろうとしても、社内ライブラリとの接続部分でネイティブの実装が多くなるため相性が良くありません。しかし、今回の案件ではセンサー類を複雑に扱う機能はなかったため、Flutterという選択肢が現実的なものとなりました。
(3)Flutterの画面と、ネイティブの画面との共存が可能
Flutterでは MethodChannel という機能があり、FlutterからAndroidのActivity、iOSのViewControllerを呼び出してネイティブの画面を表示することができます。
この存在が、心理的にも大きな後押しになりました。最悪、Flutterで実現できない画面があれば、MethodChannel
を経由してネイティブで実装できる、という後ろ盾を得た私は、Flutterの採用を本格的に検討しました。
(4)事業としての戦略的判断
私のチームではアプリの新規開発案件が近年増加傾向にあります。その中でAndroid/iOSそれぞれのエンジニアを揃えることが難しいこともあります。中にはひとりで両OSを作れるエンジニアもいますが、そのようなエンジニアは大抵重宝されているので確保することはより困難です。仮にFlutterでの開発が事業としての選択肢に加えることができれば、少ないリソースで効率的に開発することもでき、案件の受注の際に開発リソースの心配をする必要が減るかもしれません。
以上のことを見据えて、Flutterの採用を決定しました。
Flutterを採用して良かったこと
OS間の仕様調整が不要
同一コードで両OSを実装できることは、開発工数の削減はもちろん、OS差分が出ずに仕様調整のコストがなくなるのは大きいです。通常なら各OSごとにチーム(または各OS1人ずつ)で開発しますが、Flutterの場合は1チームで同じコードを作り上げるため、OS間の認識齟齬は発生しません。
学習コストは、高くない
別のFlutter案件ではAndroidエンジニア、iOSエンジニア両者にjoinしてもらいましたが、二人とも特に苦労せずにFlutter/Dartに慣れてくれた様に見受けられました。BloCパターンなど、すべてを完全に理解することは時間のかかることかもしれませんが、簡単なロジックを組むことやUIを構築すること自体はさほど難しくはありません。私のチームではモブプロでチームみんなで勉強しながら開発を進めました。
ちなみに、Android Studioで開発できるため、Android開発者にとっては慣れ親しんだ開発環境で開発しやすいと思います。
UIパーツの作成が容易
Flutterでは様々なUIパーツが用意されており、簡単にキレイなUIパーツを作ることができます。個人的なお気に入りはListTileです。ちょっとしたリストを作るときは、大抵これで事足ります。マージンもデフォルトで設定されているため、マージンが画面によって違う、ということも起こりにくくとても便利です。マージンのズレにとてもうるさい私にはとても嬉しいポイントですね。
非同期処理が簡潔に記述可能(async/await)
言語として非同期処理の async/await が組み込まれています。また、これと組み合わせることを前提としたUIパーツとして、StreamBuilder やFutureBuilder といったWidgetが用意されているため、非同期処理とUIとの繋ぎこみも簡潔に記述できます。
Flutterを採用して大変だったこと
Flutterは決して銀の弾丸ではありません。苦労もとても多かったです。
言語機能の弱点
Dartが採用された理由のひとつに、アプリエンジニアの参入障壁が低い(学習コストが低い)ということが述べられています(参考記事:なぜFlutterにおいてDartを使用するのか?)。
これには賛否両論かもしれませんが、私は賛成です。ただ、Kotlin/Swiftに比べると言語としての機能が弱いのは確かです。主に以下の点が弱いと言われる理由です。
- enumが弱すぎる
- extensionが使えない
- null safetyでない
Kotlin/Swiftの場合は、enumに文字列や数値などを持たせたりと様々な拡張ができますが、Dartのenumは拡張が一切できないため使い勝手がよくありません。
しかしこれは徐々に解消されつつあります。Dart2.6でextensionが導入されたことで、enumの弱点もある程度解消できると思います。(stable channelにはまだ入っていませんが…)
null safetyに関しても対応は進められているようですので、こちらの対応も時間の問題かと思います。
設計パターン
FlutterはBloCパターンと呼ばれる設計と相性がよく、頻繁に使われています。
長めだけどたぶんわかりやすいBLoCパターンの解説
弊社でもBloCパターンを採用していますが、BloCパターンをベースにどう設計すればキレイな実装になるのかは、試行錯誤を繰り返しました。Android/iOSであれば、ある程度のパターンが自分の中に出来上がってていたり、参考になるようなアプリも世の中に多く存在していますが、手探りでの設計は大変でした。そこの初期工数は多少かかることを想定して見積もることをおすすめします。もちろん、楽しいのですが…
Flutter自体に含まれる不具合
少し特殊な使い方をすると、原因特定が困難な不具合に遭遇することも多々ありました。
今でこそ遭遇率も低くなりましたが、回避がとても難しいことも多かったです。
多言語化が大変
Google製だから、Androidのstrings.xmlのようなノリでいけると思っていたら痛い目に合います。弊社では翻訳文言をスプレッドシートで管理し、翻訳ファイルをGASで出力するスクリプトを組んで管理しています。書いてしまえば一言ですが、ここにはかなりのコストを注ぎ込んで整備しました。詳細は下記記事をご参照ください。
Flutterの多言語ファイルを管理する
ライブラリのデファクトが決まっていない
数多くのライブラリ(Package/Plugin)がこちらに公開されています。ライブラリを組み合わせることでより実装は簡単となりますが、ライブラリが乱立していることでデファクトが決まっていない、という実情もあります。公式が提供しているライブラリでさえバージョンが1.0未満であることも多く、まだ発展途上の感はあります。とはいえ、便利なライブラリが豊富にあるので、自分の仕様に合致したPluginを根気よく探してみましょう。
また、ライブラリにはそれぞれ自動採点のScoreが付与されているため、そのスコアと参考にすると良いです。
https://www.slideshare.net/NavitimeJapan/flutter-pluginscore-1
WebViewが大変
webviewには落とし穴が多く、思わぬ不具合に遭遇することが多いです。簡単なことをやりたいだけなのに、WebViewだとハードルがかなり高くなります。最悪、WebViewはMethodChannel
を使ってネイティブの1画面として実装するのが良いかもしれません。
弊社ではいくつかの plug in を試した結果、一番安定していたflutter_inappbrowserを採用しています。
UIのプレビューが見れない
XcodeのstoryboardやAndroidのxmlのようなUIのプレビュー機能は現時点ではまだありません。Hot reloadが強力なのでプレビューがなくても支障はない、とは聞きますが、プレビューはやはりあった方が便利です。コードだけ見てもどんな画面なのかイメージしづらいので、プレビューがあるとコードの可読性はずっと上がると思います。
プレビュー機能は現在開発中の模様。
FlutterのHotUI、超期待( ´・‿・`)全Flutter開発者が望んでいたものっぽい( ´・‿・`)https://t.co/UsWJ9Hm4qk pic.twitter.com/hukO8Z5zdF
— mono 🎯 @自宅 (@_mono) September 20, 2019
既存のアプリはFlutterで置き換えるべきか?
では最後に既存のアプリもFlutterで置き換えるべきなのか?という冒頭の話に戻りたいと思います。
基本的には「No」だと思います。
フルスクラッチでの開発になりますので、時間と工数がとてもかかります。ただし、既存アプリが古すぎる、ネイティブで続けるとしてもフルスクラッチでやらないとマズい、などの状況であれば、Flutterもひとつの選択肢になるでしょう。また、メンバーの技術であったり、モチベーションもひとつの判断材料です。Dartを毛嫌いする人、Flutterが作るマテリアルライクな世界観を良しとしない人。反対意見が多い中でFlutterを強制するのもあまり得策ではないでしょう。
また、AddToApp という既存アプリにFlutterを組み込む仕組みも用意されています。用途としては、ネイティブアプリに新しく画面をFlutterで追加する場合などに有用です。が、まだプレビュー版の域は出ず、実運用にはまだおすすめできません。
まとめ
弊社でFlutterを採用した経緯と、そこで感じたメリット、デメリットについてお話してきました。Flutterはとても強力な武器になり得ますが、まだ課題も多く、必ずしもすべてのプロジェクトでメリットを享受できるわけではありません。ネイティブの方が実現できることも当然多いため、採用するかどうかは多角的な面での検討が必要です。
とはいえ、一緒にFlutterを盛り上げてくれる方々が増えることを期待しています。本記事が今現在、そしてこれからFlutterの採用を検討している方の参考になれば幸いです。