【楽しい個人開発】FlutterでiOSアプリを作るときに通った全工程

この記事は [Flutter #2 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/flutter-2) の17日目の記事です :oden: おでんの美味しい季節ですね!

[link1]:https://apps.apple.com/jp/app/%E7%9B%AE%E6%A8%99%E7%AE%A1%E7%90%86%E3%82%A2%E3%83%97%E3%83%AA-%E3%82%84%E3%82%8B%E3%81%B2%E3%82%83%E3%81%8F/id1544283769

# はじめに

こんにちは、すぎっと (@sugitlab) です。
私は京都にある、とある老舗メーカーのエンジニアとして働きながら、趣味でプログラミングにいそしんでいます。今回は、Flutter製のiOSアプリ『 [やるひゃく][link1] 』の制作過程で通った全工程をまとめて共有しようと思います。
<img width="400" alt="やるひゃくAppStore" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266496/4f746097-e131-db14-9e89-3d0dcb2d5542.png">
[AppStoreはこちらから][link1]

やるひゃくは、私が毎年ノートに書いていた『やりたいことリスト100』をアプリにしたものです。これまでは、やりたいことリストを作るときはどうしても**タスク管理のようになってしまい**、やったかどうかの振り返りは結局年末までほったらかしになっていました。しかし、**やりたいことリストとして自ら掲げた事はもっと大事にした方がいいんじゃないか？** とふと考えたことをきっかけに、やりたいことリストをただのタスクとして管理するのではなく大切な思い出として記録できるアプリを作ろうと決意しました。
もうひとつのきっかけは、[友人と取り組んでいるエンジニアサークル](https://kyo-waku.com/)の活動でFlutterを触ってみて、大層ハマってしまったことにあります。これについては以前、[Zennの記事](https://zenn.dev/sugitlab/articles/d0b3858b300b0af64ed9)に書いています。愛が溢れております。

さて、このアプリ やるひゃく は、11月5日に作りはじめて、38日かかってAppStoreへの初回申請をしました。Flutter歴も半年もない私ですが、本業と育児の合間を縫いながらでもこの日数で初回リリースができたのは、Flutterの凄さだなぁと感じています。ログをTwitterに毎日あげていたので、よろしければご覧ください。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">【1日目】<br><br>・flutterプロジェクト作成 &amp; githubリポジトリ連携<br>・iPadにて仕様/UI決定<br>・flutterにてラフなUIモックを作成<br><br>１時間少々でできた<a href="https://twitter.com/hashtag/%E5%80%8B%E4%BA%BA%E9%96%8B%E7%99%BA?src=hash&amp;ref_src=twsrc%5Etfw">#個人開発</a><a href="https://twitter.com/hashtag/flutter?src=hash&amp;ref_src=twsrc%5Etfw">#flutter</a></p>&mdash; すぎっと٩( ᐛ )و 年内にアプリリリースするという無謀チャレンジ中のエンジニア (@sugitlab) <a href="https://twitter.com/sugitlab/status/1324359933916188674?ref_src=twsrc%5Etfw">November 5, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


前置きが長くなってしまい申し訳ありません。このアプリを作る時に通った全工程について紹介します。

# アプリ制作の全工程

この記事ではソースコードを実際にお見せしながらの説明はしていません。長くなるからです。ですが、とても参考になる他の記事を参照したり、また後日別の記事にして行こうと思います。

- 前提: 
   - iOSだけ対応します: iPhoneしか持っていないからです:sob:
   - AppleDeveloperProgramに入っておきましょう
   - 英語を頑張って読みましょう（Google、DeepLに頼ってでも英語記事を読みましょう）

## 1. 作りたいものの大まかなイメージを描く

まずは作りたいもののイメージを固めていきます。お絵描きしてもいいし、AdobeXDやFigmaなどを使って本格的に書いてもいいでしょう。この辺りはFlutterに限らず同じです。

Flutterに関して言えば、まずは公式のページでデザインの[サンプル](https://flutter.github.io/samples/#)を見ることができます。ここでWidgetでUIを組むという事はどういうことかがなんとなく見えてくると思います。
その次はYouTubeを見ることをお勧めします。Flutterは **Widget** と呼ばれる部品を適所に組み込んで仕上げていきます。じゃあ、どんなWidgetがあるのかを知らないとなかなか作るイメージが湧かないですよね。料理をするにも材料が目の前に並んでた方がレシピが浮かびやすいのと同じです。ジャガイモと玉ねぎとにんじんがあったらカレーか肉じゃが作りますよね。それと同じです。(？？

<img width=400 alt="Widgetoftheweek" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266496/2b194418-0ab5-e08e-3a60-bacbbf302f95.png">
[Flutter Widget of the Week](https://youtube.com/playlist?list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG)

これを一通り見てみてください。
こんなWidgetがあるならこんな画面が作れそうだな、というイメージをしながら見てみるといいと思います。説明は英語ですが、日本語字幕もありますし、説明自体あまり難しいことはしゃべっていないので、無音でも十分楽しめます。


## 2. StatelessWidgetで全てのUIを組んでモックを作ってしまう

完成の絵をコードに落とし込んでいきますが、**まず全部StatelessWidgetで組んでしまって大丈夫です。**
Flutterの勉強を始めるとまず最初にStatelessWidgetとStatefulWidgetが出てきて、状態がどうのこうのというのがあります。もっと深掘りするとElementの話も出てきたりします。-> この辺りを詳しく勉強しておきたいなら、monoさんの[Mediumの記事](https://medium.com/flutter-jp/dive-into-flutter-4add38741d07)が最高です。

さて、UIのモックを作る上ではStatelessWidgetだけで走り切ることができますと言いましたが、これはなぜかというと、**StatelessWidgetに後から状態を乗せてStatefulWidgetにする** なんていうことが簡単にできます。Flutterは良くできています。

実際に触れるようになると、ここのボタンはもっと上がいい、アニメーションつけた方がいい、そんな改善がたくさん見えてきます。とにかく **実機にインストールするところまではStatelessWidgetだけで進める** のがオススメです。

ここが個人的には一番難しいポイントだと思います。教本や学習サイトなどをみても、自分でアプリを作る時の参考にするにはシンプルすぎるものが使われているか、完成度が高すぎるものが使われているかのどちらかが多いように思います。なんか上手く作れないな〜にハマるか作り込みすぎてしまうかのどちらかに陥りやすいように感じました。

ここで私のつくった [やるひゃく][link1] の Ver.0.1 を、**こんなもんでええんやな〜** と思えるように公開しておきます。

<img width=400 alt="mock01" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266496/acf0b387-ed13-9e8d-7a5b-048149c38cf5.png">

ここでの目的はUIパーツのコア部品を選定する事だ、という程度にするように心がけてつくりました。あと、ひこにゃんかわいい٩( ᐛ )و


## 3. これなら使えるなという感覚を得るまでモックを改良する

とにかく実機上で触ります。ここで感じた違和感はこの時点で潰しておいたほうが良いと思います。状態管理やデータ構造にまで手が及ぶと流石のFlutterでも変更は骨が折れます。

UI周りの検討は [オブジェクト指向UIデザイン: 通称OOUI本](https://www.sociomedia.co.jp/10105) を参考に進めました。この本ではUIの考え方の大原則を説明してくれていますが、オシャレなものの作り方を説明するものではありません。比較的抽象的な部分、つまりは**最高に使いやすいアプリのモックを作るために必要な考え方** を提供してくれていると私は理解しています。

同じように、ソシオメディアさんの[ヒューマンインターフェースガイドライン](https://www.sociomedia.co.jp/category/shig)を見ておくとたくさんの気づきが得られます。
[やるひゃく][link1] でその全てを遵守できている自信はありませんが、大事なところは押さえていると思っています。

とにかく触って、身近な友達や家族にも恥ずかしがらずに見てもらいましょう。

## 4. この辺りでプロジェクトの構成をちゃんとしておく

私はFlutterのプロジェクトを `flutter create` してからこんな勢いで走り抜けてしまったので**整理整頓**をすっかり忘れていました。

- libの下にたくさんファイルが並んでしまっていませんか？
- ひとつのDartファイルに大量のWidgetが並んでいませんか？
- ひとつのWidgetに大量のchildがぶら下がっていませんか？

Flutterのフォルダ構成はあまり日本語の良い記事が見つかりませんでした。私はこちらの英語のMedium記事を参考にしました。

- [File and Folder structure in Flutter](https://medium.com/flutter-community/file-and-folder-structure-in-flutter-967b8be3155e)

ここまでは UIに関するWidgetしか作っていないと想定します。それなら話は早いです。まず、**View** とか **UI** みたいな名前のフォルダをlibの下に作ってください。
そして、main.dart 以外をすべて投げ込んでください。

次に、1つのdartファイルが巨大になっている場合、**複数のStatelessWidgetに分離できないか** チャレンジしてみてください。再利用性とかそういう難しいことは後からでもなんとかなるので、一覧性の良いようにしてみてください。

もし、複数のWidgetから共通して参照されるような dartファイルができていたら、**widgets** といった名前のフォルダを作成し、その中に投げ込んでください。

```
/lib
  |- main.dart
  |- /view
      |- cool.dart
      |- fabulous.dart
      |- /widgets
          |- good.dart
```

あと、なんとなく色々な人のサンプルをみて気づいたのでそうしているのですが、**dartファイルには英大文字を入れないようにしています**。camelCaseやPascalCaseにはせず、settings_view.dart のようにアンダースコアで繋いでいます。
公式かどこかで案内されているのかなぁ・・・見落としているかもしれません。


## 5. サンプルのデータをアプリ全体に流す(状態管理を実装する)

まず、アプリで使用するデータを書き出します。書き出したデータの親子関係を考えたり、後の機能追加を考慮して設計します。ここはエンジニアのセンスというか、個性が出るところかなぁと思います。Flutterに限らず必要な工程ですね。

次がポイントとなる **状態管理** です。
アプリにデータを流していくためには状態管理の仕組みを考える必要があります。状態管理を正しく詳しく説明することは自信がないので、**大まかに**説明します。

Flutterの状態管理は大まかに言えば、おおきなツリー状になっているWidgetの関係において、色々な場所で使用される共通のメモリデータ(状態)を上手く参照できるようにするための仕組みです。巨大なツリーの各ノードに対してバケツリレーでデータを流すよりも、どこかで一元管理して上手い仕組みで特定のノードに流すのが便利だよね、というものです。

これを理解するにはinheritedWidgetから始めるといいと思っています。Widgetツリーとcontextを意識することができるからです。

![image.png](https://flutter.dev/assets/development/data-and-backend/state-mgmt/state-management-explainer-5495afe6c3d6162f145107fe45794583bc4f2b55be377c76a92ab210be74c033.gif)
  ↑ [Flutter公式](https://flutter.dev/docs/development/data-and-backend/state-mgmt/intro)から借用


さて、Flutterでは**状態管理の仕組みがいくつもあります**。私は何かをおすすめできるほど網羅して使った経験がないので、単に私が使ったものを紹介します。状態管理の比較はたくさんの良記事があるのでぜひそちらをご覧ください。私が参考にしたものです。

- [Flutter公式](https://flutter.dev/docs/development/data-and-backend/state-mgmt/intro)
- [monoさんのMedium](https://medium.com/flutter-jp/state-1daa7fd66b94)

私は[Riverpod](https://riverpod.dev/)を使いました。Riverpodは[Provider](https://pub.dev/packages/provider)と呼ばれる状態管理パッケージを開発した方が、もっといいものをという事でまた新しく作られたものです。こんなものをどんどん作れるってほんとすごいと思います。ちなみに、Riverpodの文字を並び替えるとProviderになるのもシャレが効いていていいですね！

加えて、Riverpod公式がおすすめしていたので私は [hooks_riverpod](https://pub.dev/packages/hooks_riverpod) を組み合わせて使用しました。本業の方でReact.jsを使っていて、そこでもReact Hooksをよく使うので、手に馴染みがあったからです。

Riverpodの実装方法については、Riverpod公式のGettingStartedとmonoさんの記事がオススメです。monoさんの記事ではHooksを使われていないですが、Riverpodのイメージは同じなのでとても参考になりました。

最初のステップで**全てStatelessWidget**で作成していたと思います。RiverpodとHooksをつかえば、データを受け取りたいStatelessWidgetをどんどん**HookWidgetに変えていき、useProvider()でデータを受け取る**だけです。


## 6. データを永続化する

データの永続化とはつまり**保存**です。保存先は大きく分けて2つです。

- iPhoneの中に保存する
- インターネットを介してどこかのサーバーに置く

個人開発の場合は後者はFirebaseなどのクラウドサービスを使うことが一般的ですね。自前でサーバーを立てている人には出会ったことがありません。

私は前者を選びました。やるひゃくはSNS的な要素や、ユーザー間のデータのやり取りなどは想定していないからです。もちろん、個人利用でもクラウドを使う事で、スマホを買い換えたり、iPhoneとiPadで連携させたりということができるようになります。この目的であればいつか対応してもいいなぁと考えています。もしクラウドを使うなら Flutter は Firebase がベストな選択肢だと思っています。[Firebase公式もそう言っています](https://firebase.google.com/docs/flutter/setup?hl=ja)。まぁ、どちらもGoogle様ですので・・・。

さて、iPhoneに保存する方法にもいくつかあります。[Flutterの公式](https://flutter.dev/docs/cookbook/persistence)では2つ案内されていますね。

- sqflite
- shared_preferences

私はsqfliteを選びました。やるひゃくはやりたいことリストを作るアプリです。たくさんの項目を登録することを想定しています。データベースではクエリを使って簡単にデータの取り出しができるので便利です。

とは言いましたが、世の中のアプリはだいたい何らかのデータベースを使っていると思いますのでそれに習っています。また先ほど書きましたが、いつかはクラウドにデータを載せてもいいと思っているのもひとつの理由です。なにかと移行しやすいですからね。

## 7. UIをブラッシュアップする

とにかくたくさん触って、試して、**親しみを持ってもらえるようなデザイン** を目指しました。正直、このデザインに関しては間違いないサクセスケースというものは無いのかなぁと思う一方で、ここを押さえておいたら割といい感じにまとまるぞ、というものもあると思っています。

私はたくさんのアプリをインストールしてみて、これから良いデザインを考えるぞ〜というモチベーションで見ることにしました。普段何気なく触っていては気づかないようなことに気づくことができました。

例えばiOSの最近のアプリのほとんどに共通することがあります。

- ベタ塗りカラーの直線的なAppBarはあまり使われていない
- AppBarの色はだいたい背景色と合わせてある
- elevation(立体感のあるシャドウ)もかなり控えめ

などです。この辺りの私の気づきについては、Zennの方で記事を書いてみましたので参考にしてみてください。

- [Flutterでこなれた感じのUIを作るコツ](https://zenn.dev/sugitlab/articles/d49a056941d511)


## 8. アイコンを作る

デザインがまとまったらあとはリリースに向かってまっしぐらです。アイコンを作りましょう。
Flutterでアイコンを作る場合、**ひとつのサイズを作っておけば一括で色々なサイズの画像に変換してくれるパッケージ** があります。
これ、とっっっってもありがたいですよね。以前Unityで遊んでいた時のアプリは全てのサイズのアイコンを自前で用意していたので本当にありがたい。結構な種類が必要ですから・・・。

そのパッケージがこちらです。

- [flutter_launcher_icons](https://pub.dev/packages/flutter_launcher_icons)
- [参考のQiita記事: 【Flutter】iOSとAndroidのアプリアイコンを一括生成する](https://qiita.com/rkowase/items/e0f3f8aec207ed8567aa)

私はアイコンの作成はiPadを使っています。VectornaterとProcreateを活用しました。Procreateでラフ絵を描き、Vectornaterでトレースしてvectorに変換し、出力しています。4〜5年前に趣味でWebページとか作っていたときはAdobeのイラレとペンタブなんかを使っていたのですが、ApplePencilの登場によって不要になりました。もちろん素人レベルのことしかしないからです。ちょっとしたアイコン作りにはイラレは贅沢すぎでしたね。


## 9. ライセンス情報やポリシーの用意

ライセンス情報ではアプリで使っているパッケージを掲載するのですが、なんとFlutterには **利用したパッケージ情報の一覧ページを自動で作ってくれる機能** があります。それがこちらです。

- [showLicensePage](https://api.flutter.dev/flutter/material/showLicensePage.html)
- [showAboutPage](https://pub.flutter-io.cn/documentation/about/latest/about/showAboutPage.html)

どちらも同じような機能です。AboutDialogを出した上で、ライセンスページに飛ぶか。ライセンスページをいきなり出すか。それくらいの違いです。


次に、AppStoreに用意しなければならないポリシー情報ですが、**Notion**を使うと簡単です。ポリシーに限らずちょっとしたWebページの代わりに使えるのでかなり便利です。

- [Notion](https://www.notion.so/product)

私は普段から情報はNotionに集約しているので、Notionは私の第二の脳みそです。超有能な外部ストレージです。

Notionでページを作った後、**Shere to Web**の機能を使って公開リンクを作成すればそれでおしまいです。なんとまぁ素晴らしいエコシステムですこと。そしてまたこれも無料です。Notionが出た当初は無料枠には保存の上限があったのですが、最近無くなりました。信じられません。私はAPIを使用していろいろ遊んでいるので、引き続きサブスク契約していますが、それでも安く感じます。

ちなみに、やるひゃくのページは[こんなふう](https://www.notion.so/918e77e68a264a0e8b57410392fbeee2)に作ってあります。
 ↓ こんな感じ
![キャプチャ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266496/ae994360-8de8-14c4-6223-fa821b36fd48.png)

ここで使用しているなんだかおしゃれ風な画像ですが、フリーです。商用フリーです。こういうちょっとしたところに使いやすいフリー素材も紹介しておきます。

- [many pixels](https://www.manypixels.co/gallery/)
- [loose drawiing](https://loosedrawing.com/)




## 10. 申請する

申請方法はいろいろな説明があるので割愛します。

ここでは、**アプリストアの紹介画像にこだわったぞ**という紹介をさせていただきます。AppStoreですが、検索すると各アプリの画面キャプチャを含んだ説明画像が並ぶと思います。多くの場合、三枚の縦長画像か、一枚の横長画像が使用されていますね。

- 縦３枚の例: GoogleMap

<img width=300 alt="googlemap" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266496/77168278-ae63-9edc-863e-80c1c38091cc.png">

- 横１枚の例: Google スプレッドシート

<img width=300 alt="spread" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266496/0327dbcc-7bdb-22ad-874e-ab7360d8688c.png">

そんな中、いくつかのアプリでは**複数枚の縦長画像を組み合わせて1つの画像のように見せるテクニック**が使われていることに気づきました。

- 複数枚を組み合わせた例: Google Chrome

<img width=300 alt="chrome" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266496/608c74b6-92d6-4a2b-2421-a096542896b3.png">


そこで私も三枚全部は難しかったので、二枚で1つの画像になるようにつくりました。
これも、iPadでVectornaterとProcreateを使いました。本当に便利です。

<img width=500 alt="やるひゃくStore" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266496/5ff1b248-3923-6d39-3df3-997e94e23be0.png">


以上の工程を経て、無事アプリをリリースできました。
今後、Flutterでアプリを作ってみたいな、という方の参考になれば幸いです。

長文にお付き合いいただきありがとうございました。

例として(?)紹介させていただいた個人開発したアプリ [やるひゃく][link1] を使って、2021年のやりたいことリストの1つに「Flutterアプリを作る」を登録して、チャレンジしてみてはいかがでしょうか？

（最後の最後で宣伝、失礼しました）


ありがとうございました。

すぎっと٩( ᐛ )و