個人開発でReactとFlutterを同時に使うとどうなるか
はじめに
この記事は、個人で作っているサービスをアプリとWebの両方に展開しいて感じたことを書いたものです。
作っているもの
著者は、個人でRabbytという、デジタルキャンバスサービスを作っています。
タブレットやスマホに全画面で絵を表示することで、「タブレットで絵を飾れる」サービスです。
壁にかけたりスタンドを使ったりすれば簡単に絵が飾れます。
アプリもリリースを重ね、今は1.0.8になり、最近のリリースではファイルをアップロード出来るようになりました。
使用している技術を図にすると上のようになります。
色々使っていますが、
- Go (バックエンド)
- Flutter (アプリ)
- React (Web)
- GraphQL (API)
を使っていることが、この記事での重要な点です。
モノレポ
Rabbytは1人で作っていて、リポジトリを分けても楽にはなりません。
そのため、必要なものは全て1箇所に集めています。
リポジトリのディレクトリを簡略化すると下のようになっています。
.
├── app (Flutter用)
│ ├── android
│ ├── ios
│ └── lib
│ ├── gqls
│ ├── models
│ ├── providers
│ ├── screens
│ │ ├── account_setting
│ │ ├── contact
│ │ └── ...
│ ├── utils
│ └── widgets
├── backend (バックエンド用)
│ ├── cmd
│ ├── graph
│ ├── handlers
│ └── models
├── cloud_functions (Cloud Function用)
│ └── thumbnail_gen
└── frontend (React用)
├── public
└── src
├── components
├── contexts
├── generated
├── models
├── pages
│ ├── about
│ ├── account_setting
│ └── ...
└── utils
新しく機能を作るときには、
- バックエンド・Reactを作る
- デプロイする
- Reactと同じ機能をFlutterにも作る
- アプリを審査に提出。公開を待つ
という流れになっています。
レイアウト修正など細かい変更の場合はFlutterを先に弄ることもありますが、反映が簡単なので、大抵の場合はReactを先に変更します。
両方開発するのは辛くない
前の記事に書きましたが、Rabbytを作り始めた時にはReactで作ることしか考えていませんでした。
なので、アプリに手を出すか考えていたときには「アプリまでやったら絶対つらいんだろうな」と思っていました。
しかし、やってみると意外と辛くありませんでした。
- 同じ機能を作るから2回目は気軽になる
- ロジックはコピペだから時間がかからない
- 2回作るとリファクタリング気分が味わえる
というような感想を持っていて、「思ったよりも簡単だな」と思っています。
同じ機能を作るから2回目は気軽になる
作り始めて気づいたのですが、面倒なところの多くは「どうやって作ろうかな」とか「エラー処理どうしようかな」とか考える部分です。
そういう負荷は、時間だけでなく精神的にもかかっていて、書いているときだけでなく、食事中や入浴中にもかかっています。
しかし、そういうものはReactで動くようになれば無くなります。
Reactが出来てしまえば、Flutterの方は「どうやって作るかはわからないけど、ま、どうにかなるだろ」と考えるようになります。
なので、Flutter部分を作る時には手を動かすだけで、気軽です。
ちなみに、同じような負荷は仕事をしている時にもあるはずですが、仕事ではあまり感じたことがありません。
仕事の場合は他の人の決定を待つ時間があったり、「なる早」を適当に流すことが出来るので、余裕があるからなのかなと思っています。
1人で作っている時の「なる早」は本当の「なる早」です。
ロジックはコピペだから時間がかからない
Rabbytをアプリでも作っているのは、アプリの方がユーザー体験が良いからです。
なので、ReactとFlutterは同じ機能を持っていて、コードもほとんど同じです。
主だったディレクトリを見るとわかりやすいのですが、
.
├── app (Flutter用)
│ └── lib
│ ├── gqls (GraphQLの自動生成ファイル用)
│ ├── models
│ ├── providers (グローバル変数用)
│ ├── screens (個別の画面用)
│ │ ├── account_setting
│ │ ├── contact
│ │ └── ...
│ ├── utils
│ └── widgets (画面の共通要素用)
└── frontend (React用)
└── src
├── components (画面の共通要素用)
├── contexts (グローバル変数用)
├── generated (GraphQLの自動生成ファイル用)
├── models
├── pages (個別の画面用)
│ ├── about
│ ├── account_setting
│ └── ...
└── utils
上のように、ReactとFlutterの主要なディレクトリはほぼ同じです。
そして、ロジックも同じです。
レイアウトの方法が違うので、その部分はコピペするわけには行きませんが、GraphQLのクエリもほぼ同じで、エラー処理やバリデーションも同じです。
なので、あまり時間は掛かりません。
体感としては「React3に対して、Flutter1」です。
2回作るとリファクタリング気分が味わえる
また、2回同じ機能を書くと単純に楽しくなります。
「同じ機能を3回作ると見えてくる」というような話がありますが、違う言語を使っていてもそうです。
更に、ReactとFlutterでは使える機能に差があるので、「あー、こうやると楽なのか」といった発見が出来ます。
GraphQLの恩恵は無い
当然といえば、当然なのですが、GraphQLの恩恵は感じないです。
GraphQLは柔軟に通信内容を変えられますが、同じ機能を作っているので通信の内容はほぼ同じです。
RESTを使っていたとしてもさほど変わらなかったかな、と思います。
アプリが古いバージョンで動いていることがあるので、APIの互換性を保ちやすいという部分は恩恵かなと思わないでもないですが、機能の追加ばかりしていて破壊的な変更はしていないので、「まぁ、どっちでも変わらないだろうな」という感想です。
ただ、どちらも同じであれば、流行的にGraphQLの方を使うのが自然かなと思うので、GraphQLを選んだことに後悔もありません。
面倒なこと
というように、ReactとFlutterの2種類で作っていて特段問題は無いのですが、やはり面倒なことはあります。
- 画面の内容を微妙に変える必要がある
- コミットとデプロイのタイミングが合わない
という点です。
画面の内容を微妙に変える必要がある
ReactとFlutterではレイアウトの方法も違えば画面の操作方法も違うので、ロジックのようにコピペで終わらせることは出来ません。
例えば、Flutterではグリッドレイアウトのように、サイズに応じてレイアウトを変える仕組みが整っていないので、「xs
とmd
の値を変えて縦並びか横並びを変える」というような強引な方法は出来ません。
素直に両方書く必要があります。
また、思想やライブラリも違うので、各フレームワークに合わせて値の受け渡し方法や関数の分割場所などを変えています。
つまり、レイアウトについては、別の物を作っている感覚です。
コミットとデプロイのタイミングが合わない
特にFlutterに関してですが、iosとAndroidではアプリが承認されるタイミングが違います。
なので、「メインブランチのコードが動いている最新のコード」とはなりません。
すぐに承認されれば気にならないですが、タイミングが1・2日ずれることが多いので少し気持ち悪いです。
しかも、リジェクトされれば更にずれます。
これで面倒だったのが、細かい変更を短期に連続でリリースしようとした時です。
「React分の修正は全部デプロイまで終わり、Flutterの修正を始めているのに、Appleにリジェクトされた」という時がありました。
その時は修正した部分を含めて再審査に出してしまいましたが、ちゃんとやるのであれば、同じ変更でもReactとFlutterを別ブランチに分けたほうがよいとおもいます。
が、まぁ、面倒なので分ける気はありませんが・・・
終わりに
以上、Rabbytというサービスを個人で作ってきた感想でした。
Webもアプリもあるので、気になったら、サービス自体もよろしくお願いします。
Discussion