🍣

Flutter × Firebase ではじめる個人開発

2024/03/20に公開

変更履歴

2024/03/20 初版

はじめに

Flutter と Firebase を使って個人でアプリ開発をするときのロードマップを紹介します。 本記事を一通り読めばアプリを公開するまでにやるべきことの大枠をイメージできるようにしました。 広く浅く紹介しているので、もし気になる内容があれば、個別に深掘りしてみてください。

個人開発にフォーカスした記事になっているとはいえ、いくつかの Flutter のお仕事を請けた私自身の経験も反映しているため、お仕事でも活用できる内容も含まれていると思います。

本記事は、ざっくり次の流れになっています。

  • アプリの構想と具体化
  • 実装に入る前の準備
  • 基本機能の実装
  • よりよい実装にするための Tips
  • 開発フロー
  • アプリの公開
  • 収益化の話

想定読者

  • Flutter × Firebase を使って個人開発をしようとしているひと
  • または、既に個人開発をしているひと

Flutter を導入するか悩んでいる方へ

Flutter を導入するか悩んでいる方向けに Flutter のことを説明したドキュメントを 株式会社ゆめみ さんが外部に公開してくれました (公開時のX(旧Twitter)のポスト)。導入を悩んでいる方は目を通してみるとよいと思います!

https://yumemi.notion.site/Flutter-3d0dd70c87c342e29a9a6f11b479b96c

それでは、まずはどんなアプリを作るのかの構想からはじめていきます!

自分が使いたいアプリを作ろう

作りたいアプリは決まっていますか?

もし決まっていない、または何を作ったら良いか悩んでいる場合、なぜ作るのかを考えてみると良いと思います。 個人でアプリ開発をする理由は人それぞれ、例えば次の理由が考えられます。

  • スキルアップ
  • お小遣い稼ぎ(あわよくばこれで生計を立てたい)
  • 就職のためのポートフォリオ
  • 自分(や自分に近い人)が使うから

どんな理由であれ、自分(や自分に近い人)が使うアプリを作る のが、一番モチベーションが長く続くので個人的には良いと感じています。自分が一番のヘビーユーザーになるアプリなら、リリース後のバグ対応や機能追加も苦にならず、作りっぱなしになりません。

ちなみに、私が公開している 医療費管理 アプリは、私自身が確定申告するときに医療費の集計が面倒だと感じて、自分で使いたいから作りました。もうひとつの ピークフロー日記 は妻に欲しいと言われて作りました。私の場合はどちらのアプリも自分(や自分に近い人)が使いたいという理由から開発がスタートしています。

作りたいアプリが無い場合

何を作ったら良いのか悩んでいる時は次のことを考えると良いと思います。「買い物リスト」のように既に多くのアプリが公開されていても、どこか不便な点があったり、実は全然メンテナンスしていなかったり、参入できる余地があるかもしれません。

  • 自分が普段使いしてるアプリの不満点を解消するアプリを作ってしまう
  • 普段の生活の中でアプリで解決できるものがあるかを考える

どんなアプリにするかもう少し具体化しよう

作りたいアプリが決まったら、手を動かす前に、どんなアプリを作るのかもう少し具体化していきましょう。いわゆる 要件定義 というやつです。しかし、個人アプリなので、仕事レベルの要件定義は必要はなく、アプリを作り始めるのに必要最小限でよい と思います。

具体的には次に挙げる 5 点を決めていきます。

  • ざっくりデザイン
  • リリースするプラットフォーム
  • 英語のアプリ名
  • アプリの識別子
  • クラウドサービス ( mBaaS ) の選定

ざっくりデザイン

どんな感じのデザインにするのかざっくりイメージを固めていきます。詳細な画面遷移図は不要ですが、少なくとも 最初に起動する画面(オンボーディングではなく実際に使い始めた後に起動するトップ画面)をどうするのかくらいのイメージは持ったほうがよい と思います。

トップ画面のよくあるパターンを 3 つ挙げておきます。

ナビゲーションバー ボトムアップバー ドロワー

類似アプリを使い倒す

作りたいアプリに似たアプリを片っ端からインストールして使ってみましょう。こんな機能が必要かな、この機能はいらないな、このデザインいいな、などと 使いながら完成イメージを膨らませるとざっくりデザインが浮かび上がってきます。 逆に自分の中で作りたいアプリのイメージが強い場合でも、類似アプリを使うと新しい発見があることが多いです。

ギャラリーサイトを活用する

デザイン性の良いアプリのギャラリーサイトを眺めるのも有効です。オンボーディング画面、サインイン画面、サインアップ画面など、どんなアプリでも似たような画面になるので、特に参考になります。

https://giginc.co.jp/blog/giglab/apps-uidesign

https://blog.nijibox.jp/article/app_ui_design/

マテリアルデザインを知る

Flutter でアプリを実装する際、マテリアルデザインを採用することがほとんどだと思います。マテリアルデザインが用意している様々な UI コンポーネントをどういうユースケースで使った方が良いのかを知っておく と、実装を開始したときにスムーズに UI を組んでいくことが出来るので、おさえておくと良いと思います。

https://m3.material.io/

リリースするプラットフォーム

リリースするプラットフォームを決めましょう。Flutter は次のプラットフォームに対応しています。

  • Android
  • iOS
  • Web
  • Windows
  • macOS
  • Linux

今回は、Android と iOS の 2 つのプラットフォームに対応することにします。

英語のアプリ名

後述するアプリの識別子やリポジトリ名で使うための英語のアプリ名を決めます。今後いろいろなところで使うので、個人的には 短かく 1 語 にするのがオススメです(多くても 2 語)。

例えば「買い物リスト」アプリの場合、ShoppingList だと 2 語になってしまうので、造語でも良いので短く Shoplist などにしたほうがよいです。ストアにリリースするときの日本語のアプリ名と多少乖離があっても大丈夫です。

アプリの識別子

Android だとパッケージ名、iOS だと バンドルID と呼ぶ、アプリを一意に識別する識別子を決めます。好き勝手に決められますが、一般的な命名規則(逆さにしたドメイン+アプリ名)に従っておいた方がベターです。

例えば、ドメイン名が keyber.jp で、アプリ名が Shoplist なら、 jp.keyber.shoplist になります。ドメインを持っていない場合は、jp + 自分を表すニックネーム とかにしておけばよいかと思います。

アプリ名が 2 語だと面倒なことになる

もしアプリ名が ShoppingList のように 2 語だったとすると、アプリの識別子は次のようになります。

プラットフォーム アプリの識別子
Android jp.keyber.shopping_list
iOS jp.keyber.shopping-list もしくは jp.keyber.ShoppingList

実はプラットフォームによって使える単語の区切りが異なります。

Android の場合、_ が使えて - が使えず、逆に iOS の場合は _ が使えず - が使えます。
統一できないことが致命的な問題になるわけではありませんが、統一できていた方が今後余計なことを考えずに済みます。

クラウドサービス( mBaaS )の選定

アプリだけで完結するアプリ(スタンドアロンアプリと呼んだりします)であれば不要ですが、故障や機種変に対応してアカウント機能を実装するのなら、Firebase などの mBaaS を利用しましょう。 mBaaS とは mobile Backend as a Service の略で、モバイルアプリに特化したクラウドサービスのことです。

私は mBaaS に Firebase をよく使います。ほぼ無料で利用でき(利用数が増えると少しずつ費用は増えていくことに注意が必要です)、個人開発レベルに必要な機能は一通りそろっていますし、AdMobAnalytics など、Google の他サービスとの連携もスムーズです。

Firebase 以外の mBaaS もいろいろあるようです(だれか比較記事書いてくれたらうれしいです!)。下記記事には言及ありませんが、最近は Supabase という選択肢も出てきました。

https://qeee.jp/magazine/articles/17082

Firebase におけるシステム構成

私の個人開発でよく使う Firebase の各種サービスを使ったシステム構成図は次のとおりです。すべて使うべき、というわけではなく、自分のアプリにあったサービスを見つけて導入してみてください。本記事の後半で個々に説明しています。

上記には取り上げていませんが、他にも Remote Config、App Distribution、Messaging など魅力的なサービスもあるので是非調べてみてください。

実装に入る前の準備をしよう

どんなアプリを作るかだいたい具体化できたら、実装に入る前の開発準備をします。

具体的には次に挙げる 3 つの準備をします。

  • Flutter 用の GitHub リポジトリを作る
  • Firebase をセットアップする
  • Firebase 用の GitHub リポジトリを作る

Flutter 用の GitHub リポジトリを作る

まずは Flutter のソースコードを管理するための GitHub リポジトリを作成しましょう。個人開発であっても、後から修正を見直したり、特定の修正を取り消したり、使うメリットは大きいと思います。

リポジトリ名は、先ほど決めた英語のアプリ名を使って shoplistshoplist-app などがわかりやすくて良いと思います。「Flutter」製のアプリであることを強調したい場合は flutter-shoplist もよさそうです。

Flutter ではリポジトリ名の単語区切りに _ を使いがちですが、私は個人的な好みで - を使っています。 - を使っている理由を記事にしているので参考にして頂ければと思います。あくまで個人的な好みなので、_ でも良いと思います。

https://zenn.dev/susatthi/articles/20220410-064851-package-naming

GitHub の具体的な使い方はいろいろな記事が出てるので調べてみてください。

https://www.kagoya.jp/howto/it-glossary/develop/howtousegithub/
https://www.rstone-jp.com/column/107548/

Firebase をセットアップする

Firebase を利用するための準備をしましょう。

Firebase のプロジェクト名を決める

Firebase を導入するためには Firebase のプロジェクト名を決めなければいけません。Firebase のプロジェクト名は、全世界で一意である必要があります。 アプリ名( shoplist )だけで作れればよいですが、既に使われていた場合はアプリの識別子と同様にドメインとアプリ名を組み合わせて jp-keyber-shoplist のようにして作れば、他と被ることはまずないと思います(Firebase の仕様上、プロジェクト名の単語区切りは - を使います)。

環境分け

個人開発であっても、ユーザーの大切な情報を預かっている以上、 本番環境のみは避けるべきです。 本番環境上で開発をして誤って本番環境のデータを壊してしまったら目も当てられません。

よく仕事で見かけるのは、開発(Develop)/検証(Staging)/本番(Production)の 3 系統ですが、個人開発ではテストチームがいるわけではないので、私の場合は開発と本番の 2 系統で済ませています。

Firebase プロジェクトを作成する

次のように環境毎に Firebase プロジェクトを作成しましょう(「買い物リスト」の例)。

環境 Firebase のプロジェクト名
開発環境 jp-keyber-shoplist-dev
本番環境 jp-keyber-shoplist-prod

次の記事では、flutterfire_cli を使って Firebase を環境分けして導入する手順について紹介しています。

https://zenn.dev/susatthi/articles/20220904-151314-flutter-fire-flavor

Firebase 用の GitHub リポジトリを作る

Firebase コンソール上でセキュリティファイルなどを直接編集することが可能ですが、Firebase で管理するファイルも GitHub リポジトリで管理するが望ましいと思います。 GitHub リポジトリで管理しておくと、開発環境で色々試した後、事故無く本番環境へデプロイすることができます。

また、前述した Flutter の GitHub リポジトリ内に Firebase で管理するファイルを含んでしまうこともできますが、アプリのリリースサイクルと Firebase のリリースサイクルは異なるので(後述する開発フローが異なる、タグ打ちのルールが異なるなど)、 Firebase 用のリポジトリは別で分けておいた方が管理が楽になると思います。

GitHub リポジトリ名は、アプリ名の後ろに -firebase をつけて、例えば shoplist-firebase のようにするとわかりやすいと思います(好みの問題なのでお好きにしてください)。

使う Firebase サービスにもよりますが、次のファイルがコミット対象になります。

  • Firestore のセキュリティルール
  • Firestore のインデックス
  • Storage のセキュリティルール
  • Functions のコード
  • Hosting にデプロイする HTML ファイル群

Terraform でさらなるコード管理も

私はやったことありませんが、Terraform などの IaC(Infrastructure as Code)ツールを使って、Firebase コンソール上でポチポチやる設定自体もコードで管理することもできます。

https://zenn.dev/aldagram_tech/articles/54729ac3954484
https://zenn.dev/cloud_ace/articles/b791cce386d523

基本機能を実装しよう

Flutter と Firebase の開発準備が整ったら、いよいよ実装をはじめます。ここではどんなアプリでもだいたい実装が必要になる基本的な機能とその実装方法について紹介していきます。

アプリアイコン

アプリの起動アイコンは必ず用意が必要です。私は Figma の App Icon Toolkit を使って作っています。

https://www.figma.com/community/file/824894885635013369

次のように、ホーム画面のプレビューを見ながら修正ができるのでイメージしやすいです。

また、アイコン画像の素材は Iconduck というアイコン素材を利用しています。 素材が大量にあるのと、アイコンの線の太さや色を後から変えられるのが気に入っています。いくつかダウンロードすると途中から有料(買い切り $39 )になるので注意が必要です。

https://www.figma.com/community/plugin/1074084347757622122/iconduck-icons-emojis-illustrations-and-logos

アプリアイコンを Android と iOS に適用する際、複数の縦横サイズの画像に変換する必要がありますが、コマンド一発で変換して適用してくれる便利なパッケージがあります。

https://zenn.dev/susatthi/articles/20220401-060335-flutter-launcher-icons

スプラッシュ画面

無くてもよいですが、あるとなおよいスプラッシュ画面。ネイティブのスプラッシュ画面を簡単に作ることが出来る便利なパッケージがあります。

https://zenn.dev/susatthi/articles/20220406-061305-flutter-native-splash

スプラッシュ画面を自作してもよいと思います。

https://codewithandrea.com/articles/robust-app-initialization-riverpod/

対応 UI

UI の種類には大きく次の 3 種類があります。どの UI に対応するかを決めておく必要があります。

  • モバイル
  • タブレット
  • デスクトップ

初回リリース時点ではモバイルだけで良いと思います。タブレットでの利用者が増えてきたらタブレット UI への対応を検討してみるとよいでしょう。

ちなみに、複数の UI に対応することを「レスポンシブ対応」と呼びますが、レスポンシブ対応をするときにいつも私は次のパッケージを使っています。

https://pub.dev/packages/responsive_framework

デバイスの縦横切り替え

デフォルトだと、デバイスを横画面にしたときにアプリの画面も横画面に切り替わります。 もし切り替わらないようにしたい場合は、次の記事などを参考にして対応が必要です。

https://starhoshi.hatenablog.com/entry/2022/05/11/140819

文字サイズ変更

デフォルトだと、デバイスの設定で文字サイズを変更したときにアプリの文字サイズも変更されます。 もし変更されないようにしたい場合は、次の記事などを参考にして対応が必要です。個人的には、ユーザーの利便性を考えてデフォルトのままがよいと思います。

https://www.memory-lovers.blog/entry/2023/11/20/081927

私は文字サイズを変えたり文字ウェイト(文字の太さ)を変えたい場合は、次のサイトにある TextTheme クラスを使っています。文字がアプリ内で統一できますし、見やすいように上下左右の文字間隔も勝手に調整してくれます。

https://api.flutter.dev/flutter/material/TextTheme-class.html

// BAD
Text(
  '大きい文字',
  style: TextStyle(
    fontSize: 24,
  ),
);

// GOOD
Text(
  '大きい文字',
  style: Theme.of(context).textTheme.headlineSmall,
);

多言語対応

ほとんどの場合、初回リリース時では日本語のみの対応になると思いますが、一応ここであげておきます。現実的にはアプリのインストール数が増えていったときに英語対応をすることが多いと思います。

多言語対応をするためのパッケージが複数あるので、次の記事を参考にしてください。

https://zenn.dev/susatthi/articles/20220422-140216-flutter-localizations

Android と iOS のアプリ名など、ネイティブ側を多言語対応したい場合は次の記事が参考になりました。

https://seeds-digital.com/flutte-localize-app-name/

ダークモード対応

ダークモードを常用するユーザーもそれなりにいますので、余力があればダークモードへの対応もした方が良いと思います。Flutter にはダークモード対応を容易にできる仕組みがあります。

class App extends StatelessWidget {
  const App({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.light(),
+      darkTheme: ThemeData.dark(),
    );
  }
}

https://zenn.dev/sugitlab/books/flutter_poke_app_handson/viewer/step4

Flutter のダークモード機能を使うためには、Theme を適切に使いこなす必要があります。 もし BAD の例のように色を直接指定してしまうと、ダークモードにしても反映されません。

// BAD
Text(
  'エラー',
  style: TextStyle(
    color: Colors.red,
  ),
);

// GOOD
Text(
  'エラー',
  style: TextStyle(
    color: Theme.of(context).colorScheme.error,
  ),
);

Theme を使いこなす手助けになる、マテリアルカラーシステムを確認するツールを作ったので是非試してみてください。

https://susatthi.github.io/flutter-material-color-system/

ライトモード ダークモード

ソースコードも公開しています。

https://github.com/susatthi/flutter-material-color-system

カスタムフォント

デフォルトは Roboto で、日本語が中華風になってしまうので、日本語に適したフォントに変更したほうがよいと思います。

google_fonts パッケージを使えば簡単にフォントを変更できます。

https://pub.dev/packages/google_fonts

使いたいフォントを探すときは次のサイトを使っています。

https://fonts.google.com/?subset=japanese&noto.script=Jpan

google_fonts には無いフォントを使いたい場合は、フォントファイル自体をアプリ内に組み込む方法もあります。次の記事を参考にしてください。

https://zenn.dev/susatthi/articles/20220419-143426-flutter-custom-fonts

テスト

テストをやるのか、やるならどのテストをやるのか、を決めておきましょう。規模が小さく 1 人で開発する場合は不要なことが多いと思いますが、一応取り上げておきます。

Flutter には、いくつかの種類のテストが用意されています。

単体テスト

ロジックをテストします。

https://qiita.com/ikedaS-06/items/690ce3d93a48c5491325
https://zenn.dev/ncdc/articles/flutter_unit_test_mock

Widget テスト

Widget (画面や UI 部品)を操作してテストします。

https://isub.co.jp/flutter/flutter-dev-testing-with-vscode/

Golden テスト

画面キャプチャを撮って差分が無いかをテストします。

https://zenn.dev/arsaga/articles/f88b46aad05dba
https://moneyforward-dev.jp/entry/2023/12/08/155825

E2E テスト

yaml で書かれたテスト手順をシミュレーター上で実行しながらテストを実施してくれるテストフレームワークの maestro がよく使われているようです。

https://zenn.dev/heyhey1028/articles/enter-maestro

ユーザー認証機能

Firebase Authentication を使ってユーザー認証機能を実装します。ここでは、私が実装経験のある次の 5 つの認証方法について紹介します。

  • 匿名認証
  • メール/パスワード認証
  • メール/メールリンク認証
  • Google 認証
  • Apple 認証

上記以外にも様々な認証方法が提供されているので、興味があれば調べてみてください。

https://firebase.google.com/docs/auth?hl=ja

匿名認証

https://firebase.google.com/docs/auth/flutter/anonymous-auth?hl=ja

匿名認証を使うとユーザーのサインイン操作不要でユーザー ID を払い出すことが出来るのでとても便利です。アプリを初めて使い始めたユーザーにいきなりアカウントを登録してもらうのはハードルが高いと思います。そんなときに まずはこの匿名認証でユーザーを作っておいて、あとからメール/パスワード認証などでアカウントを作ってもらう、 というのがユーザーフレンドリーな設計だと思います。

メール/パスワード認証

https://firebase.google.com/docs/auth/flutter/password-auth?hl=ja

認証機能として一番最初に思い浮かべるのがこのメール/パスワード認証です。メール/パスワード認証を実装する場合は次の機能の実装が必要です。けっこう大変です。

  • サインアップ(アカウント登録)
  • サインイン
  • サインアップとサインイン時のメールアドレス確認リンクの送信(成りすまし防止)
  • メールアドレスの変更
  • パスワードの変更
  • パスワードリセット(パスワード忘れ対応)

メール/メールリンク認証

https://firebase.google.com/docs/auth/flutter/email-link-auth?hl=ja

パスワード認証は実装が大変なので、代わりにメールリンク認証を用いることもできます。メールリンクの場合は次のように実装する機能が減ります。

  • サインアップ(アカウント登録)
  • サインイン
  • メールアドレスの変更

Google 認証

https://pub.dev/packages/google_sign_in

Gmail を利用しているユーザーは非常に多いので、SNS 認証を導入するなら最初に対応しておいたほうがよいと思います。

実装は上記の公式 README 通りにやれば大丈夫だと思いますが、GCP を設定したり、Xcode で iOS の設定を変更する必要があるので、慣れていないと少し時間がかかるかもしれません。

Apple 認証

https://pub.dev/packages/sign_in_with_apple

Google 認証に対応した場合は Apple 認証にも対応しないと Apple 審査でリジェクトされてしまうため、対応が必須です。

iOS の実装は上記公式 README 通りにやればできますが、Android の実装(というか設定)がややこしくて、私は Android では Apple 認証を実装しませんでした。 Android で Apple 認証を使う人は少ないためか、今のところ無くてもクレームはきていません。

Firestore のユーザードキュメントの設計

ユーザーを認証したあと、ユーザーが作成したデータを Firestore に保存できるようにします。その際、他のユーザーのデータを参照したり変更しないように、適切に保護する必要があります。

よくやるのが、Authentication が払い出した UID (ユーザーID)を、Firestore のドキュメント ID にする というものです。詳細は次の記事が参考になります。

https://terakoya.sejuku.net/question/detail/15200
https://zenn.dev/saikou_kunisaki/articles/5a3470cbb6fbb7
https://zenn.dev/flutteruniv_dev/articles/0d1d8a383d3079

ユーザーのデータを適切に保護するためには、Firestore のセキュリティルールを適切に実装することが大切です。 セキュリティルールの書き方が詳細に解説されている次の記事を読むことをオススメします。最低限ユーザー認証部分の実装は必須です。Firebase Storage のセキュリティルールも基本的には同様の考え方で書くことができます。

https://qiita.com/KosukeSaigusa/items/18217958c581eac9b245

ユーザーにメールを送信するには

THANKS メールなどユーザーにメールを送信するケースがあると思います。Firebase Extensions にある Trigger Email を使えばメールを簡単に送信することができます。

特定の Firestore コレクションにデータを追加するとメールを送信してくれます。私は SendGrid と一緒に使っています。

https://extensions.dev/extensions/firebase/firestore-send-email

退会/ユーザーデータの削除

ユーザー認証をしてユーザーデータを Firestore に保存できるようにした場合、ユーザーがアプリの利用を辞めたいと思ったときに、ユーザーの手によって自分のデータを削除できるようにする必要があります。この機能を実装しないと、Android も iOS も審査にリジェクトされる可能性が高いです。

Firebase には、Authentication ユーザーが削除されたときに、ユーザーに紐づく Firestore ドキュメントを一気に削除してくれる Delete User Data という Extension があります。 この Extension を導入して、設定画面やアカウント画面から退会を実行したら Authentication のユーザーを削除するようにしておけば OK です。

https://extensions.dev/extensions/firebase/delete-user-data

ユーザー削除時の再認証を避けるには

匿名認証の場合はすぐに削除できますが、メール/Google/Apple 認証のユーザーの場合、ほとんどのケースでユーザー削除前に再認証を求められてしまいます。 再認証を避けたい場合は、次のように Functions で削除すれば実現可能です。ユーザーの再認証なしでユーザーを削除するので、取り扱いにはご注意ください。

  1. ユーザーが退会を実行したら Firestore のユーザー削除用のコレクションに削除したい UID を保存する。
  2. 保存をトリガーに発火した Functions でユーザーを削除する。

サービス利用規約とプライバシーポリシー

ユーザーデータを預かる場合、サービス利用規約とプライバシーポリシーを作成し、アプリを利用頂く前にユーザーに同意していただく必要があります。 これらの同意がないとトラブルが起きたときの対処が難しくなります。

https://keiyaku-watch.jp/media/kisochishiki/privacypolicy/

プライバシーポリシーの公開先

プライバシーポリシーは Google の PlayStore や Apple の AppStore に URL を登録する必要があるため、Web 上に公開しなければいけません。 公開方法をいくつか紹介しますので、自分のやりやすい方法で公開をしてください。

  • Firebase Hosting
  • GitHub Pages
  • Notion
  • AWS S3

なお、プライバシーポリシーはサービスやアプリを横断して 1 つ作れば基本的に使い回しが可能です。 私の場合は会社ホームページ上で公開しています。

サービス利用規約の公開先

サービス利用規約は必ずしも Web 上に公開する必要はないため、アプリ内に実装してしまってもよいと思います。ただし、Google 認証を実装する場合、同意画面に利用規約へのリンクを設定する必要があるため、プライバシーポリシーと同様に Web 上に公開しなければいけません。 サービス利用規約もプライバシーポリシーと同じように管理して Web 上に公開してしまうのが、管理も煩雑にならないため良いと思います。

アナリティクス

Firebase Analytics を導入すると、ユーザーの利用状況を分析することができます。 例えば、どの画面がよく見られているのか、どのボタンがよく押されているのか、Android と iOS のインストール数の比較、MAU(Monthly Active User) 数などを計測することができます。

導入自体は簡単なので、とりあえずいれておいて損はないと思います。

https://firebase.google.com/docs/analytics/get-started?platform=flutter&hl=ja

クラッシュログの計測

Firebase Crashlytics を導入すると、クラッシュログ(強制終了ログ)を収集して不具合を早期に修正することができます。

https://firebase.google.com/docs/crashlytics/get-started?hl=ja&platform=flutter

Firebase を不正アクセスから防ぐ

Firebase App Check を導入すると、Firebase の各サービスを不正リクエストから守ることができます。 詳細は次の記事をご参照ください。

https://zenn.dev/susatthi/articles/20230316-172748-flutter-firebase-app-check

よりよい実装にするための Tips

これまで基本機能をみてきましたが、ここでは、よりよい実装にするために役立つ Tips を紹介していきます。お仕事でもよく出てくる内容だと思うので把握しておくとスキルアップ間違いなしです。

アーキテクチャ(状態管理手法)の選定

アプリ内の様々な状態を適切に管理することは、バグが少なく保守性が高いアプリにするためには重要なことです。 Flutter における状態管理手法を含むアーキテクチャは 1 つに定まっておらず、数多くのアーキテクチャが提唱・実装されています。

https://zenn.dev/flutteruniv/books/flutter-architecture

https://zenn.dev/namioto/articles/4ff020a6835ea9

https://zenn.dev/heyhey1028/articles/78ccea1534dcd4

しかし、特に日本では、状態管理手法に Riverpod を使ったアーキテクチャが主流 になっています。Flutter エンジニアを目指している方は、Riverpod が扱えるようになっておくと良いと思います。

Riverpod を使う上で私が参考になったと思う記事をいくつか紹介しておきます。

https://medium.com/flutter-jp/architecture-240d3c56b597

https://techblog.enechain.com/entry/flutter-rearchitecture-from-mvvm

https://zenn.dev/chooyan/articles/eefc76dbd2ba25

https://codewithandrea.com/articles/flutter-app-architecture-riverpod-introduction/

ナビゲーションパッケージの導入

画面遷移を宣言的により簡単にできるパッケージがいくつかあるので紹介します。特に次にあげる条件に合致する場合はナビゲーションパッケージを導入したほうが良いと思います。

  • Flutter Web に対応する
  • ディープリンクに対応する(プッシュ通知をタップして特定の画面に遷移する場合など)

go_router

Flutter Favorite にもなっていて、記事も多く実装しやすいです。ボトムナビゲーションを表示したまま画面遷移できる ShellRoute が実装され、より使いやすくなりました。頻繁にバージョンアップもされています。

https://pub.dev/packages/go_router

context.go('users/1') のように文字列で画面遷移するのではなく UserRoute(1).go(context) のようにタイプセーフに使える go_router_builder もあります。

https://zenn.dev/susatthi/articles/20220801-135028-flutter-go-router-builder

auto_route

私は auto_route を好んで使っています。なんとなく Riverpod の ProviderScope の入れ子との相性が良い気がします。

https://zenn.dev/susatthi/articles/20230427-095829-flutter-auto-route

ロガーパッケージの導入

デバッグ中にログを出力したいときに print() を使うことがあると思いますが、print() はリリースビルドでも出力されてしまいます。その気になればユーザー自身でデバッグコンソールでログを見ることが出来てしまうため対策が必要です。

様々なロガーパッケージが公開されており、過去にいくつか試して比較してみました。

https://zenn.dev/susatthi/articles/20220413-153500-flutter-logger

roggle という シンプルでカラフルで拡張性の高いロガーパッケージ を pub.dev に公開していますので、是非使ってみてください。

https://zenn.dev/susatthi/articles/20220506-144617-flutter-roggle
https://pub.dev/packages/roggle

いつも私がやっているロガーの実装を紹介します。リリースビルド時はエラーログを Crashlytics に送信しています。INFO ログも Firebase コンソール上で確認ができるので、不具合修正がはかどります。

logger.dart
import 'dart:io';

import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:roggle/roggle.dart';

/// Release ビルドのとき main() で代入されることを想定している
FirebaseCrashlytics? crashlytics;

const _loggerName = '[APP]';

final logger = crashlytics != null
    ? Roggle.crashlytics(
        printer: CrashlyticsPrinter(
          loggerName: _loggerName,
          errorLevel: Level.error,
          onError: (event) async {
            await crashlytics?.recordError(
              event.exception,
              event.stack,
              fatal: true,
            );
          },
          onLog: (event) async {
            // ログの量が多くなるため info 以上のみに限定する
            if (event.level.index >= Level.info.index) {
              await crashlytics?.log(event.message);
            }
          },
        ),
      )
    : Roggle(
        printer: SinglePrettyPrinter(
          loggerName: _loggerName,
          // error 以上のときはスタックトレースを出力する
          stackTraceLevel: Level.error,
          // iOS はカラー非対応
          colors: !Platform.isIOS,
          // ログが長くなるので非表示
          printCaller: false,
        ),
      );
// エラーログ出力すれば Crashlytics に送信してくれる
logger.e('error');

linter の導入

linter (lint) とは、ソースコードを分析して問題点を指摘してくれる静的解析ツールのことです。 linter を導入することで、公式が推奨する Dart の書き方に沿ったコードになるため、他のプログラマが書いたコードも見やすくなりますし、自分のコードを見てもらうときも見られやすくなります。

flutter create したときに、デフォルトで flutter_lints が導入されていますが、私はより厳しめにした次の pedantic_mono という linter を愛用しています。

https://pub.dev/packages/pedantic_mono

さらに次のように自分好みにカスタマイズしています。

analysis_options.yaml
include: package:pedantic_mono/analysis_options.yaml

linter:
  rules:
    cascade_invocations: false
    comment_references: false
    constant_identifier_names: true
    library_private_types_in_public_api: false
    one_member_abstracts: false
    prefer_relative_imports: true
    use_setters_to_change_properties: false

おすすめ VSCode 設定

私は IDE に VSCode を使っていて、VSCode の設定は次のとおりです。個々の詳しい説明は省略しますが、保存時自動フォーマットしたり、自動でインポート文を整理したりしています。 興味のある方は覗いてみてください。

私の VSCode 設定
.vscode/settings.json
// Dart Recommended Settings: https://dartcode.org/docs/recommended-settings/
{
  "debug.openDebug": "openOnDebugBreak",
  "editor.renderWhitespace": "all",
  "editor.renderControlCharacters": true,
  "editor.minimap.enabled": false,
  "editor.bracketPairColorization.enabled": true,
  "[dart]": {
    "editor.rulers": [80],
    "editor.formatOnSave": true,
    "editor.formatOnType": true,
    "editor.selectionHighlight": false,
    "editor.suggest.snippetsPreventQuickSuggestions": false,
    "editor.suggestSelection": "recentlyUsedByPrefix",
    "editor.tabCompletion": "onlySnippets",
    "editor.wordBasedSuggestions": "off",
    "editor.codeActionsOnSave": {
      "source.organizeImports": "always",
      "source.addMissingImports": "always",
      "quickfix.insertSemicolon": "always",
      "source.fixAll": "always",
    }
  },
  "dart.flutterSdkPath": ".fvm/flutter_sdk",
  "dart.debugSdkLibraries": false,
  "dart.showSkippedTests": false,
  "dart.runPubGetOnPubspecChanges": "always",
  "dart.projectSearchDepth": 3,
  "search.exclude": {
    "**/.fvm": true,
    "**/*.freezed.dart": true,
    "**/*.g.dart": true,
    "**/*.gr.dart": true,
    "**/*.gen.dart": true
  },
  "files.watcherExclude": {
    "**/.fvm": true
  },
  "git.autofetch": true,
  "explorer.confirmDragAndDrop": false,
  "explorer.fileNesting.enabled": true,
  "explorer.fileNesting.expand": false,
  "explorer.fileNesting.patterns": {
    "pubspec.yaml": ".flutter-plugins, .packages, .dart_tool, .flutter-plugins-dependencies, .metadata, template.iml, .packages, pubspec.lock, build.yaml, analysis_options.yaml, all_lint_rules.yaml, flutter_launcher_icons-*.yaml, flutter_native_splash.yaml, lefthook.yaml",
    ".env.example": ".env.*",
    ".gitignore": ".gitattributes, .gitmodules, .gitmessage, .mailmap, .git-blame*",
    "readme.*": "authors, backers.md, changelog*, citation*, code_of_conduct.md, codeowners, contributing.md, contributors, copying, credits, governance.md, history.md, license*, maintainers, readme*, security.md, sponsors.md",
    "*.dart": "$(capture).g.dart, $(capture).gr.dart, $(capture).freezed.dart"
  },
}

fvm の導入

fvm は Flutter SDK のバージョン管理ツールです。fvm を使うと Flutter SDK のバージョンをプロジェクト毎に別々にすることができてとても便利です。

https://zenn.dev/altiveinc/articles/flutter-version-management

最近、fvm 3.0 がリリースされ、より進化しました。

https://zenn.dev/altiveinc/articles/flutter-version-management-3

私は使ったことはありませんが、asdf というツールもあるようです。

https://zenn.dev/altiveinc/articles/asdf-flutter

Makfile の導入

Flutter や Dart の各コマンドをショートカットするために Makefile を導入しています。例えば、次のファイル(ファイル名は Makefile )をプロジェクトルートに保存をした上で、make pub-get を実行すると、fvm flutter pub get を実行してくれます。

Makefile
.PHONY: pub-get
pub-get:
	fvm flutter pub get

次のように Fig を使うと自作のコマンドも補完してくれるのが嬉しいポイントです。

いつも私が使っている Makefile を貼り付けておきます。これをベースにプロジェクト毎に手直しして使っています。

私の Makefile
Makefile
FVM := $(shell which fvm)
FLUTTER := $(FVM) flutter

.PHONY: pub-get
pub-get:
	$(FLUTTER) pub get

.PHONY: pub-upgrade
pub-upgrade:
	$(FLUTTER) pub upgrade

.PHONY: clean
clean:
	$(FLUTTER) clean

.PHONY: analyze
analyze:
	$(FLUTTER) analyze
	$(FVM) dart run custom_lint

.PHONY: format
format:
	$(FLUTTER) format lib/

.PHONY: format-dry-exit-if-changed
format-dry-exit-if-changed:
	$(FLUTTER) format --dry-run --set-exit-if-changed lib/

.PHONY: build-runner-build
build-runner-build:
	$(FLUTTER) packages pub run build_runner build --delete-conflicting-outputs

.PHONY: build-runner-watch
build-runner-watch:
	$(FLUTTER) packages pub run build_runner clean
	$(FLUTTER) packages pub run build_runner watch --delete-conflicting-outputs

.PHONY: test
test:
	make analyze
	$(FLUTTER) test

.PHONY: flutter-native-splash
flutter-native-splash:
	$(FLUTTER) pub run flutter_native_splash:create

.PHONY: flutter-launcher-icons
flutter-launcher-icons:
	$(FLUTTER) pub run flutter_launcher_icons

### Android Relase Commands

.PHONY: build-android
build-android-dev:
	$(FLUTTER) build appbundle \
		--release

### iOS Relase Commands

.PHONY: build-ios
build-ios-dev:
	$(FLUTTER) build ipa \
		--release

シェルコマンドじゃなくて Dart でコマンドを書きたい場合は grinder というツールがあります。使ったことはないけど、時間があったら試してみたいです。

https://pub.dev/packages/grinder

Flutter 研修課題のレビュー観点表を活用する

https://yumemi.notion.site/Flutter-555155c98aea49f2bc745bbaff9d6ec7

株式会社ゆめみ さんが外部に公開してくれている『Flutter 研修課題のレビュー観点』を活用して、自分のコードと照らし合わせて、足りない点を改善してみましょう。

開発フロー

ここで紹介する開発フローに沿って開発を進めると、あとから修正差分が確認しやすくなったり、本番リリース事故のリスクが減るので、余力があれば導入しましょう。特に初回リリース後の継続リリース時に効果を発揮します。

Flutter アプリの開発フロー

個人開発ではありますが、私はリリース事故を極力なくすために開発フローに git-flow を採用しています。 git-flow を採用する企業も多いと思いますので、理解して運用できるとよいと思います。

https://cloudsmith.co.jp/blog/efficient/2020/08/1534208.html

そして、GitHub Actions を使って自動ビルド+自動アップロードをして、リリース時の手間を省いています(fastlane は使ったことない)。

https://zenn.dev/pressedkonbu/articles/github-actions-for-android

https://zenn.dev/pressedkonbu/articles/254ca2fc3cd1ab

Firebase の開発フロー

Firebase で git-flow はやり過ぎ感があったので、より単純な GitHub フローを採用しています。

https://www.kagoya.jp/howto/it-glossary/develop/githubflow/

こちらも GitHub Actions を使って Firebase の各サービスへ自動デプロイをしています。

アプリの公開

ここまで、基本機能、よりよい実装にするための Tips、開発フローを紹介してきました。紹介した内容を参考にしてゴリゴリ実装を進めましょう!そして、アプリの実装が終わって、ついにストアへアプリを公開!ここでは、公開前の動作確認の方法と、公開時の注意点を紹介します。

Android アプリの公開

Android アプリは、Google の PlayStore にアプリを公開します。そのために、Google に $25 払って、Google Play Console を使えるようにする必要があります。

https://support.google.com/googleplay/android-developer/answer/6112435?hl=ja

内部テストと呼ばれる公開前のテスト機能を使って、実際にストアからインストールして動作確認をすることができます。 実装が終わった時点では完璧だと思っても、実際にストアからインストールしたらよくバグが見つかるものです。特に初回リリース前は必須です。

https://qiita.com/d2cid-mmori/items/1cb8fcf532026fab71fe

もろもろ必要事項を登録して審査に出せば数日で公開です!スクリーンキャプチャが必要だったりするので、公開前にあらかじめ何が必要かを確認しておくと良いと思います。

https://tech-begin.com/programming-coding/android/how-to-android-app-release-overview/

iOS アプリの公開

iOS アプリは、Apple の AppStore にアプリを公開します。そのために、Apple に $99 (年間) 払う必要があります。
また、TestFlight と呼ばれるテスト機能を使って、疑似ストアである TestFlight からインストールして動作確認をすることができます。 Android 同様に、特に初回リリースは必須です。

https://zenn.dev/lisras/articles/14b90852fe5525

ここまででアプリの構想から公開までひととおり紹介してきました。ここからは少し雑談になります。

作るからにはお小遣い稼ぎをしたい

自分のために作るアプリであっても、お小遣いを稼げたらよりモチベーションもよりあがります。個人開発でお小遣いを稼ぐ方法はいくつかありますが、一長一短あるのでそれぞれ紹介していきます。

バナー広告

まず一番手軽なのがバナー広告です。アプリ内に広告を表示して、表示やクリックに応じてお金を稼ぐことが出来ます。 Flutter で一番簡単に導入できるのは AdMob だと思います。導入記事もたくさんあります。

https://pub.dev/packages/google_mobile_ads

バナー広告を導入するときは次の点に注意が必要です(経験談)。

  • ゲームなど長時間使ってもらうアプリではないとあまり稼げない
  • バナー広告の上部に FAB や Dialog などの Widget が被ると AdMob アカウントが BAN される
  • 卑猥な広告が配信されてしまう(AdMob の管理画面から設定変更が可能です)

インタースティシャル広告

バナー広告以外に、インタースティシャル広告といって、アプリの自然な切れ目や移行のタイミング(ステージをクリアした後など)で表示される全画面広告フォーマットもあります。私はインタースティシャル広告を見ると一定時間バナー広告が停止できるといった使い方をしています(が、あまり見られていません。。。)。

他にもいろいろな広告フォーマットがあります。

広告だけで生計が保てるくらい稼げれば良いのですが、実際はなかなか厳しいです。私がリリースしている 記録を残す系のアプリだと、アプリを起動している時間が短いため広告との相性は良くありません。 電車の中などで暇つぶしができる ゲーム、読書、勉強などのアプリと相性がよいと感じます。

サブスク

サブスクとはユーザーから月額料金を頂くモデルです。バナー広告非表示と組み合わせたりします(サブスクに入ると広告が非表示になります的な)。Stripe などの外部の決済手段で実装すると Apple の審査に通らない ので、最近はアプリ内課金によるサブスクが一般的だと思います。Flutter でアプリ内課金を一番簡単に導入できるのは RevenueCat だと思います。

https://pub.dev/packages/purchases_flutter

RevenueCat を使うと、iOS と Android 向けのアプリ内課金の実装がとても簡単にできます。月 $2,500 未満までの決済なら無料で使えます。日本円で 375,000 円なので、ほとんどの場合無料で収まると思います。

RevenuCat の導入は次の Zenn 本がオススメです(有料ですがその価値はありました!)。特に RevenueCat の商品設計の概念をつかんで設計するのにとても役に立ちました。

https://zenn.dev/moga/books/flutter_revenuecat

サブスクのように月額モデルではなく、1 回だけ支払ってもらう買い切りモデルも RevenueCat で実装できます。

その他の稼ぎ方

他には、有料アプリとしてストアにリリースすることもできますが、最近はあまり見なくなった気がします。面倒なサブスクの実装が不要になる反面、最初からお金を支払ってもらうのでインストールのハードルがあがってしまいます。無料版と有料版の 2 つをリリースすると管理も大変です。

個人アプリを営業ツールして二次活用することで、クラウドワークスやランサーズなどでお仕事を受注出来る確率が上がるかも知れません。アプリの出来がよくないと逆効果なので注意が必要です。

究極的には企業にバイアウトして稼ぐ方法もあります。私の周りにはみたことありませんが。。。

アプリの紹介

これまで私は Flutter と Firebase を使って個人アプリを 2 つリリースしています。本記事で紹介している内容も、これらのアプリで多く実装していますので、是非インストールして参考にしてもらえたらうれしいです!

医療費管理

医療費を管理してわかりやすくグラフ表示してくれます。確定申告向けにエクセルへエクスポートすることもできます。

http://apple.co/2USOdcQ
http://bit.ly/3ermPK5

ピークフロー日記

特にぜん息持ちの方向けに、ピークフローという呼吸計測器の測定値を記録するアプリです。シンプルな入力とわかりやすいグラフが特徴です。

https://apple.co/49w2htX
https://bit.ly/488Fl2J

まとめ

本記事では、これから Flutter × Firebase を使って個人開発をする方に向けて、どんなアプリを作るのかの構想からアプリの公開まで、どんなことを考えてやっていけばよいのかを広く浅く紹介してきました。紹介する範囲が広いため、個々の詳細な説明は紹介したリンクなどを参考に深掘りしていってください。

本記事を通してひとりでも多くの個人開発者が増えたらうれしいです!

最後に

Flutter 大学という Flutter エンジニアに特化した学習コミュニティに所属しています。オンラインでわいわい議論したり、Flutter の最新情報をゲットしたりできます!ぜひ Flutter 界隈を盛り上げていきましょう!

https://flutteruniv.com?invite_id=9hsdZHg0qtaMIr6RPRulAaRJfA83

Discussion