本書では、データモデル設計とNoSQLデータベースとFirestoreについて学んだのち、レストランにレビューを追加するアプリを例にFirebaseとFlutterを使ったスマホアプリ開発する一連の流れをハンズオンします。
最近はコロナの影響もあり、在宅勤務が推奨され、学校の授業がオンライン化され、買い物はeコマースの利用が増加し、食事の際は出前アプリの利用が流行っています。コロナ以前から社会生活におけるデジタル化の必要性がうたわれていましたが、コロナ渦となり、あらゆる生活シーンでオンライン利用が半強制化され、便利で使いやすいオンラインサービスに対する需要が増しています。
また最近のシステム開発の動きを見ると、いままで「使いづらい」の代名詞であった行政や金融のオンラインサービスに「ユーザ中心設計」を採用し、使いやすくセキュリティ上も安全なサービスに作り替えようとする取組が進められています。またIT化が比較的遅れていた中小企業においてもDX(デジタルトランスフォーメーション)を促進しようとする動きがあります。
このように、あらゆる業界や組織において、あらゆる生活シーンでそれぞれの立場の要望にあった使いやすいオンラインサービスが安価に構築・提供されることが求められています。
一方これらの要望を担う人材育成の観点では、子供から大人までプログラミング教育がはやっており、例えば文科省は小学校でのプログラミング授業を必須化しています。しかしシステム開発や人材育成においては下記のような課題があると感じています。
以上のことから、エンジニアだけでなく発注側顧客企業の担当者やプロジェクトマネージャもユースケースからデータモデルに落とす考え方やデータモデルから画面設計を行うための考え方、さらにはこれらのパターンを知っていることが望まれます。
そこで本書の前半部分ではデータモデリングの具体的な方法と考え方についても詳しく説明するよう心がけました。またエンジニア育成という観点では、アイデアが浮かんだらすぐに完成品までもっていけるよう効率的で汎用的なツールを選び、それらのハンズオンを掲載しています。
様々な立場の人が本書を読みハンズオンを試してもらうことで「こんなに簡単に作れるんだ」とものづくりに興味をもってもらい、自分たちが実現したいシステムを自らが構築するマインドをもっていただく一助になればと思っています。また本書をとおしてIT技術の活用が民主化されることで、自らの会社や組織、業界においてより価値を産むアイデア考察に積極的になり、それらを自ら実現した便利で使いやすいオンラインサービスが世に増えることを願っています。
データモデルの設計はユースケースからデータモデル候補を抜き出すことからはじめます。これによってデータの大きな単位とそこに含まれるフィールドを整理できます。また、ここで抽出されたデータ単位ごとにテーブルを作り、具体的なデータサンプルを作っておくと次ステップのデータ間の関係を考察する際に役立ちます。
データ候補が抽出できたら、まとめたデータサンプルを参照しながらそれらの関係を整理します。本書ではドキュメント型データベースを採用するため、ドキュメントとコレクションの関係を整理します(ドキュメント型データベースについては、「NoSQLとFirestoreについて」を参考にしてください)。前ステップで行ったデータ候補の抽出は比較的簡単な作業ですが、ここで行うデータ間の関係整理は経験が必要な難しい作業です。前ページ「はじめに」で挙げた課題である画面設計に走りがちな理由もこの作業の難しさにあると考えられます。次ページ「ユースケース、データモデルと画面設計のパターン」では、よくあるユースケースとデータモデルと画面設計のパターンをまとめていますので、興味がある方はそちらを参照ください。
コレクションとドキュメントの分離方針がきまったら、サンプルデータをJSONの形式にまとめます。JSONスキーマにそった正しいJSON形式にまとめることでドキュメント型データベースで扱えるサンプルデータが作成されます。JSONとはWebアプリケーションやREST APIで扱われるプログラムが処理しやすく人も見やすいデータ形式ですが、初めてJSONを扱う人は正しいJSON形式とは何かわからないと思います。そのため本書では本文中にJSONエディタというツールを組み込んでJSONデータを表示し、JSON形式でのサンプルデータの作り方をステップバイステップで解説します。
顧客がレストランに対して星の数とコメントでレビューを記入し公開するアプリを例にしたユースケース記述を以下にまとめます。アプリ名は「レストランレビューアプリ」とします。
このユースケース記述から得られるデータ候補を「ドキュメント(フィールド1、フィールド2、‥)」と表現すると下記のように整理できます。
ここで具体的なサンプルデータを表形式にまとめておくとイメージしやすくなり今後の設計もしやすくなり、開発メンバーへの説明もしやすく開発やテスト等にも生かせます。
表1. レストラン
ID | 店名 | 種類 | 住所 | 星数 | ロゴ |
0 | ガスト東岡崎店 | 洋食 | 愛知県岡崎市大西1丁目1−10 | 0 | https://www.skylark.co.jp/site_resource/gusto/images/logo.svg |
1 | デニーズ東岡崎店 | 洋食 | 愛知県岡崎市美合町 字五反田25-1 | 0 | https://sozainavi.com/wp-content/uploads/2019/10/dennys.jpg |
2 | 大戸屋ごはん処岡崎店 | 和食 | 愛知県岡崎市井田西町1−11 | 0 | https://sozainavi.com/wp-content/uploads/2019/10/ootoya.jpg |
3 | 和食さと岡崎店 | 和食 | 愛知県岡崎市上里2丁目1−1 | 0 | https://sato-res.com/assets/tile/sato.png |
4 | カレーハウスCoCo壱番屋岡崎上地店 | カレー | 愛知県岡崎市上地3丁目51−6 | 0 | https://www.ichibanya.co.jp/assets/images/common/ogp.png |
5 | スシロー岡崎上和田店 | 寿司 | 愛知県岡崎市天白町東池15−1 | 0 | https://www.akindo-sushiro.co.jp/shared/images/ogp.png |
6 | くら寿司北岡崎店 | 寿司 | 愛知県岡崎市錦町2−12 | 0 | https://www.watch.impress.co.jp/img/ipw/docs/1230/499/kura1_s.jpg |
7 | モスバーガー岡崎大西店 | ハンバーガ | 愛知県岡崎市大西1丁目16−7 | 0 | http://www.wing-net.ne.jp/image/kamiooka/store/storage/w250/mos.png |
8 | マクドナルド岡崎インター店 | ハンバーガ | 愛知県岡崎市大平町石丸60−1 | 0 | https://sozainavi.com/wp-content/uploads/2019/10/mcdonalds.png |
9 | かつや愛知岡崎インター店 | とんかつ | 愛知県岡崎市大平町新寺25 | 0 | https://www.arclandservice.co.jp/katsuya/wp-content/themes/arclandservice-group/assets/img/katsuya/common/logo.svg |
表2. レビュー
レストラン「ガスト東岡崎店」に対するレビュー
ID | 顧客名 | 星数 | コメント |
0 | 鈴木一郎 | 2 | 値段の割に合わない気がする。 チーズハンバーグを頼んだが、レトルトな感じでした。 さらに、スープセットにしたが、スープは一種類。 |
1 | 佐藤二郎 | 3 | タブレットによる注文に変わったが、慣れが必要。 メニューを広げて、料理を比べたい。 この方式で価格が下がればよいが、,, |
2 | 北島三郎 | 5 | ドリンクバーが99円(単品で注文してもOK)。 パソコンの持ち込みOK。 コンセントで充電できる。持ち帰り容器は無料。 食べきれない料理の持ち帰りOK。 トイレは新しくてキレイ |
表3. 顧客
ID | メールアドレス | 氏名 | アイコン |
0 | ichiro@test.com | 鈴木一郎 | https://meikyu-kai.org/wp-content/uploads/2020/01/51_Ichiro.jpg |
1 | jiro@test.com | 佐藤二郎 | http://www.from1-pro.jp/images/t_10/img_l.jpg?1597426029 |
2 | saburo@test.com | 北島三郎 | https://cdn.asagei.com/asagei/uploads/2016/08/20160810kitajima.jpg |
1で抽出したデータをドキュメントとコレクションの関係についての方針を整理します。
これらの関係を表現したツリー構造を下図に示します。
ドキュメントとコレクションの関係について方針が決まったら、1のサンプルデータをJSON形式であらわすことでより具体化します。
また、ここではJSONエディタを使い、段階的にサンプルのJSONデータを作成します。
上記表1のIDが0のレコードを参考にレストラン・ドキュメントを作成します。
JSONではデータのひとつの塊であるドキュメントを { } で囲み、その中に "属性名": "属性値" をカンマ( , )で区切ってリストします。
上記表1の通りレストランは10個存在するため、配列をあらわすコレクションとして表現します。
JSONではこのコレクションを { "配列名": [(ドキュメントをカンマ( , )で区切ってリスト)] } で表現します。
レストランと同じく顧客コレクションもルートレベルに追加します。
JSONでは二つのコレクションを { "users": [(...)], "restaurants": [(...)] } と表現します。
レビュー・ドキュメントの配列であるコレクションをレストラン・ドキュメント配下に追加します。この時、レビュー・ドキュメントには書き込みを行った顧客がわかるように顧客IDと顧客名と顧客写真を追加します。また、レビュー・ドキュメントを参照するだけで書き込みを行ったレビュー対象のレストランがわかるよう、レストランID、レストラン名、レストランロゴといった項目も追加します。以上の作業の必要性については「NoSQLとFirestoreについて」を参照してください。
データモデルができあがったので次のステップとして画面設計を行います。
画面設計では、「画面」と「画面遷移」をデザインします。
「画面」はユーザが一時点で対面する一画面のことで、テキストフィールドやボタンなどのコンポーネントを組み合わせて作成されます。一方、「画面遷移」は複数の「画面」が違いに移動していく振る舞いのことを表します。「画面」はユースケースごとに様々な種類が考えられ、単純なパターン化は難しいです。一方、「画面遷移」は以下の4パターンにまとめることが可能です。
アプリケーションは複数の機能から成り立つので、ホーム画面にメニューやナビゲーションバーを用意して、そこからユーザがメニューを選択することで機能を切り替えます。下図は時計アプリを例に挙げて「時計」表示と「アラーム」設定、時計「設定」の3機能をボトムナビゲーションバーを使って切り替える画面遷移を表しています。
このパターンの画面遷移では機能が切り替わるため、機能間でデータの引き継ぎが必要なケースは少なく、機能内でのデータ保存のみを考えればよいでしょう。
スマートフォンは画面が小さいため、多くの入力を必要とするアンケートや申請書は下図のとおり画面を複数にわけてウィザード形式で入力を促し、最後に確認してSubmitするといった画面遷移になります。
このパターンは途中で枝分かれすることなく一本線でゴールに向かう場合に利用し、分岐が発生する複雑な場合は次の「③状態遷移」パターンを考えるとよいでしょう。
状態によって表示する画面が変化する画面遷移のパターンです。下図はログイン・ログアウトの画面遷移の例で、ログインとログアウトのどちらかをあらわす状態変数の値に従って、表示される画面を切り替えるしくみになっています。
スマホアプリの開発では、この状態の保持という概念がとても重要です。ユーザがアプリを通して何かデータの入力や変更を行うときのデータだけでなく、ログインしているかログアウトしたか、どの画面に遷移したかなど、画面を動かすために必要な「状態」というデータもあります。この状態遷移としてよく現れる画面遷移のパターンは次の「④リストのCRUD」です。
ビジネスアプリケーションの多くは、複数データを表示・追加・編集・削除しながら整理整頓するものです。そのため、データの概要をリスト表示して、そのうち一つをクリックすると詳細を表示し、編集・削除する、といった機能を持ちます。このタイプのアプリケーションの画面遷移は大概下記のようなフローになります。各画面の説明カッコ内の「Create」「Read」「Update」「Delete」はあわせてCRUD(クラッド)と呼び、データ操作の基本になります。ここでは複数データを扱っていますので、「Read」には複数データのリスト表示と一つのデータの詳細表示の二種類があるので、画面が二つ存在します。
上図の時計アプリの例では、リスト表示をする対象がアラーム設定した時刻といったシンプルなデータですが、本書で開発する「レストランレビューアプリ」のようにレストランデータが複数あり、さらにそのレストランのレビューデータが複数あるといった構造を持つ場合、レストランの詳細画面の下にレビューリストを表示するといった構造を持ちます。このように、複数データを整理整頓する形式のアプリ画面はリスト画面を中心にCRUDを担う機能に画面が分かれることをパターンとして覚えておくとよいでしょう。
本書で開発する「レストランレビューアプリ」の画面設計を「①ログイン、メニュー選択、ログアウト」と「②レストランリストとレビュー表示」、「③全レビューリストとレビュー表示」、「④アカウント表示と自分が投稿したレビューの管理」の4種類にわけて説明します。
①ログイン、メニュー選択、ログアウト
ログイン・ログアウトとメニュー表示する画面とそれらの画面遷移を下図に示します。ここでは「アカウント画面」からのみログアウトできるよう設計していますが、どこからでもログアウトできるように設計してもよいでしょう。またログイン画面はユーザ登録画面と同一画面上で切り替えられるように設計しています。
本設計は「1. 画面設計の基本的な考え方」で述べた「③状態遷移」のログイン・ログアウトと「①メニュー遷移」のパターンを利用しています。
②レストランリストとレストラン詳細、レビュー表示とレビュー追加
レストランをリスト表示し、そこから一つを選択するとレストラン詳細が表示されるとともに選択したレストランに関するレビューがリストされます。さらに、そこからレビューを一つ選択するとレビューの詳細が表示されます。また、レストラン詳細画面からはレビューを追加することができますが、リストされるレビューは自分以外のものも含まれるので、編集や削除はできないよう設計しています。
本設計は「1. 画面設計の基本的な考え方」で述べた「④リストのCRUD」のパターンを利用しています。
③全レビューリストとレビュー表示
レビュー画面では、全レストランを対象に全ユーザのレビューの一覧をリストします。また、星数で検索することが可能です。
本設計も「1. 画面設計の基本的な考え方」で述べた「④リストのCRUD」のパターンを利用していますが、ここではレビューの一覧が目的で、参照(Read)のみで追加(Create)、更新(Update)、削除(Delete)の機能は有していません。
④アカウント表示と自分が投稿したレビューの管理
アカウント画面では、自分の名前を直接編集できるように設計しました。また自分が投稿したレビューをリストし、編集と削除が可能です。
本設計も「1. 画面設計の基本的な考え方」で述べた「④リストのCRUD」のパターンを利用しており、自分が投稿したレビューを対象に更新(Update)と削除(Delete)ができるよう設計しています。
ここでは様々なユースケースの画面設計を紹介します。「1. 特徴的な機能の画面」では主に一つの画面に特徴的な機能を有しているもので、よく使う例を紹介します。2以降は、世にあるアプリのユースケースをいくつか例に挙げ、それらのデータモデルと画面設計のパターンを紹介します。
当初はユースケースパターンという観点からアプリをまとめましたが、下記の記事にも同じようなユースケースがまとまっています。求められているアプリアイデアという観点からも正解だったようです。
これらを理解し参考にすることで、経験の少ない方でもみずからのユースケースから適切な設計を行うことが可能になるでしょう。
上で紹介したとおり世の中の主要なアプリケーション、特にビジネス用途のアプリケーションはログインしたユーザの権限で複数データを整理整頓を行うことを目的としており、1で紹介した画面遷移パターンの組み合わせで事足ります。一方、下記に挙げる機能はよく使われると共に特殊な画面を持ちますので、パターンとして覚えておくとよいでしょう。
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
ゲームやAR、スマートスピーカーといったアプリケーションは複雑なUIからなりたっていたり、音声によるUIからなりたつため一般化は難しく、本書の対象外とします。
ユースケース
ユーザは何か思いついたときや覚えておきたい内容を記入するためにメモを使いますが、それをスマホアプリにした場合のユースケースを以下に記述します。ユーザがメモを友達にシェアするとか手書きメモをテキストにおとすといった高度な機能は持たず、ここでは基本的なメモ機能のみを考えます。
このユースケース記述から得られるデータ候補を「ドキュメント(フィールド1、フィールド2、‥)」と表現すると下記のように整理できます。
ここで具体的なサンプルデータを表形式にまとめておくとイメージしやすくなり今後の設計もしやすくなり、開発メンバーへの説明もしやすく開発やテスト等にも生かせます。
表1. メモ
ID | |||||
0 | |||||
1 | |||||
2 | |||||
3 | |||||
4 | |||||
5 | |||||
6 | |||||
7 | |||||
8 | |||||
9 |
データモデル
画面設計
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
(TODO)
ここでは画面設計に関連して、スマホアプリのUIデザインについて記述します。世の中のスマホといえば、iPhoneとAndroidの2種類ですが、それぞれAppleとGoogleがUIデザインのガイドラインを出しています。iPhoneはヒューマン・インタフェース・ガイドライン、Androidではマテリアル・デザインです。各ガイドラインの紹介をする前に、スマホアプリ特有の問題からデザインの歴史を説明してから、ヒューマン・インタフェース・ガイドラインとマテリアルデザインの紹介をします。
スマホには、パソコンとは異なる2つの特徴があります。
画面が小さく操作手段が限定的
スマホは画面が小さくさらにマウスやキーボードがないため操作手段が限定されるため、画面レイアウトや操作方法をどのようにデザインするかでユーザ満足度が大きく変化します。
画面サイズがばらばら
AndroidとiPhoneという違いだけでなく、同種のスマホでも画面サイズが様々で、アプリのレイアウト幅や高さを固定すると画面表示が崩れて使い物にならなくなります。例えば、歴代のiPhoneを比べても下図のとおり様々な画面サイズが存在しますし、Androidは各デバイス開発会社が様々なものを出しているのでiPhone以上に画面サイズはバラバラです。
Google PlayやApp Storeに登録された実際のアプリの評判からまとめたスマホアプリに対するよくある悪評は主に以下の4つに集約されます。
これらの悪評のうち、1以外の2から4まではUIデザインの問題といえます。
それではスマホにおけるデザインはどのような歴史を歩んできたのでしょうか。
まず、2000年前半のWebやアプリケーションのUIはそれまでのWindowsパソコンが機械的で使いづらく利用者のことを考えて作られていないという反省から、現実世界を模した装飾的なデザインからなるUIを取り入れるようになりました。この考え方によるデザインをスキューモーフィズムと呼びます。ただし、この装飾に凝ったデザインでは、デザイナーの主張が先行し、見た目や操作性に統一感がないといった弊害を生むことになりました。
そのため、20世紀初頭にモダニズムが建築や工業製品のデザインに取り入れられたように、パソコンやWebやスマホのデザインにも極力装飾を廃したフラットデザインが採用されることになりました。このフラットデザインを採用しているのがAppleです。AppleはiPhone 7のデザインにフラットデザインを取り入れ、ヒューマン・インタフェース・ガイドラインもそれにのっとったガイドラインを発表しています。
iPhone6のスキューモーフィズム | iPhone7のフラットデザイン |
しかし、フラットデザインはシンプルすぎて、システムの構造やしくみが理解しづらいといったデメリットが挙げられます。そこで、Googleはフラットデザインとスキューモフィズムの中間をとり、さらに発展させた「マテリアル・デザイン」を定義し、2014年にだれもが参照して利用できるオープンソースな形でデザインガイドラインを発表しました。
本書では、ヒューマン・インタフェース・ガイドラインの概要を述べるものの、マテリアル・デザインをメインに説明します。また、本書で利用している開発プラットフォームであるFlutterもマテリアルデザインを全面的に採用しており、AndroidはもとよりiPhoneでもマテリアルデザインを実装することができるツールであるため、本書においては開発全般でマテリアルデザインを採用しています。
ヒューマン・インタフェース・ガイドラインでは3つのデザインテーマと6つのデザイン原則とその他設計の詳細が記載されていますが、ここでは3つのデザインテーマと6つのデザイン原則を紹介します。
iOSアプリは「明確であること」「コンテンツにフォーカスすること」「奥行きがあること」の3つのデザインテーマにのっとってデザインすることを推奨しています。
1. 明確であること
あらゆる画面サイズのスマホデバイスで、テキストは読みやすく、アイコンは明快で、装飾は繊細で適度であり、かつ機能的であることが必要です。空間、色、フォント、グラフィック、インターフェイスに関する各要素は、重要なコンテンツは適度に強調されインタラクティブ性を向上します。
2. コンテンツにフォーカスすること
滑らかな動きと鮮明で美しいインターフェースは、ユーザが操作に迷うことなく、コンテンツを理解して操作するのに役立ちます。通常、コンテンツは背景の上に表示されますが、背景を半透明にするなどの工夫で、より多くのことを示唆します。ベゼル、グラデーション、ドロップシャドウの使用を最小限に抑えながら、コンテンツを最優先に表示することで、インターフェイスを明るくわかりやすくします。
3. 奥行きがあること
明確な視覚的レイヤーとリアルな動きが階層を伝え、画面に活力を与え、ユーザに理解を促進します。接触の間隔と発見のしやすさはユーザに喜びを高め、コンテキストを失うことなく機能と追加コンテンツへのアクセスを可能にします。トランジションは、コンテンツをナビゲートするときに奥行き感を提供します。
iOSアプリは、ユーザへのインパクトとリーチを最大化するため、アプリのアイデンティティを強化するため、次の原則に従うことを推奨しています。
1. 美的完全性
美的完全性は、アプリの外観と動作がその機能とどれだけうまく統合されているかを表します。たとえば、ユーザが慎重を期す作業をするアプリでは、微妙で目立たないグラフィックと標準的な操作、予測可能な動作を提示することで集中力を維持できます。一方、ゲームなどの没入型アプリでは、発見を促しながら楽しさと興奮を約束する魅力的な外観を提供するとよいでしょう。
2. 一貫性
一貫性のあるアプリはシステムが提供するインターフェース要素とよく知られたアイコン、標準のテキストスタイル、統一された用語を使用することで、使い慣れた標準的な印象を与えます。このようなアプリはユーザが期待する方法で機能と動作を組み込んでいます。
3. 直接的な操作
画面上のコンテンツを操作することで、ユーザを引き付け、理解を促進します。ユーザは、デバイスを回転させたり、ジェスチャーを使用して画面上のコンテンツに影響を与えたりすると、直接的な操作の体験をします。直接的な操作を通じて、自らの行動が即時にアプリに影響を与えることを体験するわけです。
4. フィードバック
フィードバックは行動を認め、結果を示して人々に情報を提供し続けます。組み込みのiOSアプリは、すべてのユーザーアクションに応答して知覚可能なフィードバックを提供します。インタラクティブな要素をタップすると簡単に強調表示され、進行状況インジケーターが長時間実行されている操作のステータスを伝え、アニメーションとサウンドがアクションの結果を明確にするのに役立ちます。
5. メタファー
アプリの仮想オブジェクトとアクションが、現実の世界に根ざしているのかデジタルの世界に根ざしているのかにかかわらず、身近な体験のメタファーである場合、ユーザはより迅速に学習します。ユーザが画面を物理的に操作するため、メタファーはiOSでうまく機能します。ビューを邪魔にならないように移動して、下のコンテンツを公開します。コンテンツをドラッグしてスワイプします。スイッチを切り替えたり、スライダーを移動したり、ピッカーの値をスクロールしたりします。ユーザは本や雑誌のページをめくることさえします。
6. ユーザーコントロール
iOS全体を通して、アプリではなくユーザが制御します。アプリは一連の行動を提案したり、危険な結果について警告したりできますが、アプリは意思決定をしません。最高のアプリは、ユーザーを有効にすることと望ましくない結果を回避することの間の正しいバランスを見つけます。アプリは、インタラクティブな要素を使い慣れた予測可能な状態に保ち、破壊的なアクションを確認し、すでに進行中の場合でも操作を簡単にキャンセルできるようにすることで、ユーザーがアプリをコントロールしているように感じさせることができます。
一方、マテリアルデザインとは、Googleが2014年に発表したUIのデザインガイドラインです。Googleは、Gmailなどの数多くのアプリを世に出していましたが、それらがパソコン、Android、iPhone、タブレットなど異なるデバイスで利用されるとき、画面サイズもOSも異なるため、異なった見た目と操作性を持ってしまったことを問題視していました。そこで、デバイスが異なっていても、統一したデザインで操作できるようなUIデザインのガイドラインを作る目的で開発されたのがマテリアルデザインです。例えば、スマホのようなデバイス用には、片手で操作することを想定し、直感的で意味のある物理的(マテリアル)な概念をデザインに取り入れてガイドラインが策定されています。また、明示はされていませんが、ユーザ インタフェースという意味では、ヒューマン・インタフェース・ガイドラインで挙げた3つのデザインテーマと6つのデザイン原則は、マテリアルデザインでも目指すところは同じです。
マテリアルデザインは、あくまでもガイドラインであるため、必ず準拠すべきものではありませんが、デザインの部品がどのような形でどのように動くかが細かく規定されており、Java、Kotlin、Swift、JavaScript、Flutterといったさまざまな言語に対応した無料で利用できるプログラムライブラリとともに提供されているため、まるまる採用することで一定レベル以上のUIデザインのアプリを最小のコストで開発することができます。そのため、多くのプロジェクトで採用されるに至っています。
マテリアルデザインは以下の4つのコンセプトからなりたっています。
1. 形をもった面
形をもったうすい紙のような面が重なって画面を表示するしくみを採用しています。この考え方によって、一枚の紙を切って分割して記事を表示したり、メニューやフッターを表示するための紙と中心のコンテンツをのせるための紙が重なって表示されることを影を使って表現することができます。これによって実際は奥行きがなくフラットなスマホの画面を立体的に表現することによって、表示されているものの種類が区別しやすくなったり、高さを使うことで手前にあるものほど重要といったユーザが3次元の世界で感じるのと同じくどこに注意をむけるべきかを直感的に知らせることができるようになっています。
2. 印刷物のようなデザイン
文字や絵が紙に載っているという概念からなり、ベースラインやキーラインといった印刷物のコンセプトを踏襲しています。ベースラインを決めることでコンテンツが収まる範囲を決めたり、本文やタイトルなどはキーラインを基準に配置するなど、印刷用にワードなどに設定する値などを応用することで、見た目に統一感を持たせて画面を美しくみせます。また見やすいフォントが無料で提供されています。
3. 意味のあるアニメーション
ユーザになんらかの意味を伝えるためにはアニメーションが効果的な場合がありますが、コンポーネントにアニメーション表示を簡単にほどこせるようなしくみを実装しています。
4. アダプティブデザイン
パソコン、Androidスマホ、iPhone、タブレットなどデバイスが異なり画面サイズが異なっていても形を崩さず表示するしくみを実装しています。レスポンシブデザインも同じコンセプトですが、アダプティブデザインは横幅の伸び縮みだけでなく、メニューの表示非表示などを含む、より画面サイズに応じた(アダプティブな)形でデザインを変化させるしくみをそなえています。
マテリアルデザインを使うと下記のようなリッチなUI画面を実装することができます。下記リンクを開くとFlutterを使って実装したUI画面のサンプルギャラリーが参照できます。
またFlutterでは、下記のようなマテリアルデザインの部品が提供されています。このように画面にのせる部品はたくさん存在しているので、それぞれ何ができるかだけ覚えておいて、画面パターンに従って使う部品を選別してから詳細の実装方法を調べるのがよいでしょう。
※「レストランレビューアプリ」で利用している部品には「◯」印をつけています。
アップバー ◯ | ボタン ◯ | |
カード ◯ | チップ ◯ | |
ダイアログ ◯ | ||
リスト ◯ | ||
グリッド ◯ | アイコン ◯ | アバター ◯ |
マテリアルデザインではありませんが、「レストランレビューアプリ」で使っている部品を紹介します。
マテリアルデザインはアプリのブランディングを統一的に進めるためにカラーテーマというしくみを採用し、アプリケーション全体で部品の色やフォントカラーを統一させることが可能です。逆に言うと、同じアプリでもカラーテーマを変えてイメージの異なるアプリとして仕上げることが可能ですので、アプリケーションのテンプレートを先に作っておいて様々なプロジェクトに応用する場合にもこのようなしくみは役に立つでしょう。
カラーテーマ:https://material.io/design/color/the-color-system.html
カラーツール:https://material.io/resources/color/
マテリアルデザインは単なる画面と画面遷移にとどまらず、音声や機械学習に関するUIについても拡張し、ガイドラインを提供しています。
機械学習機能に関するマテリアルデザイン:https://material.io/design/machine-learning/understanding-ml-patterns.html
音声に関するマテリアルデザイン:https://material.io/design/sound/about-sound.html
これらはスマホにかかわらず、スマートウォッチ、スマートスピーカーやスマートイヤフォン、スマートグラスなどにもマテリアルデザインを適用していこうという取り組みのあらわれであり、次章で紹介するFlutterという開発環境も同じ目的で開発が進められています。
2021年5月のGoogle I/OにおいてAndroid12のリリースにあわせてマテリアルデザインの更新がなされています。ここでは、スキューモフィズム的なモノを表現する普遍的なロゴデザインを継承しつつ、高度にパーソナライズすることができ、仕事とプライベートの切り替えが用意でモダニズム的な要素を加えたデザインを目指したとのことです。具体的には、新しいテクノロジーを快適に使えること、デバイスやアプリとの境界線を無くすような慣習に挑戦すること(イコノクラスム)、入力に反応する有機的な動きなど自然界のスピリットを取り入れることを目指しています。また、壁紙の色使いにあわせてパーソナライズされたカラーパレットを適切に自動設定してくれるダイナミックカラーという機能を有しています。今後カラーツール等にもこれらの自動化機能が付与されてくるかもしれません。
このようにマテリアルデザインは、いま世界で最も適切にデザインされ、オープンで誰にでも利用可能で、いまだ活発に進化を続けている生きているデザインシステムといえます。
通常、アプリケーションの開発プロジェクトでは、事前にマテリアルデザインのようなデザインフレームワークからプロジェクトで活用するレイアウトや部品を選択してカラーテーマやフォントを決めて、アプリのブランドに統一感を持たせることをします。このようなデザイン原則とデザインフレームワークの組み合わせを「デザインシステム」と呼びます。
最近日本国内でも「みんなの銀行」や「デジタル庁」でもデザイン原則やシステムデザインを整備してWebサイトを作るといったアプリ開発を行うことがはじまっています。Webサイトといった単純なUIだけならデザインシステムもロゴやカラーデザインや画面テンプレート程度を決めるだけですみますが、スマホアプリのデザインシステムというとより複雑な機能を含みますので、これを一から作るのは大変な労力がかかります。
そのため、本書ではマテリアルデザインをまるまるデザインシステムとして採用してスマホアプリを開発します。サービスのブランディングに関わるロゴやイラスト、カラーデザインをカスタマイズする機能もマテリアルデザインを支えるFlutterのようなツールにも開発が進められていますので、これを使わない手はないでしょう。
本書では、開発環境にGoogleがオープンソースとして開発しているクロスプラットフォーム開発環境であるFlutterを、インフラ環境にはGoogle Cloud Platform上のモバイル開発用クラウドプラットフォームであるFirebaseを利用します。本章ではこれらを選定した根拠を記載し、ベンダーロックインを排除するための代替案をまとめます。
最近のスマホアプリ開発では「あらゆるデバイス向けにネイティブアプリを開発しメンテナンスをする」か「特定のクロスプラットフォームフレームワークを選択して一つのコードをメンテナンスする」かを選択する必要があると言われています。
「あらゆるデバイス向けにネイティブアプリを開発しメンテナンスをする」を選択しているのは現在スマホアプリをリリースする企業の80%に上り、ネイティブアプリ開発により得られる操作性のよさと引き換えにiPhoneとAndroid用に2つのソースコードで開発し、両プラットフォーム用に卓越したプログラマーを雇い、コストをかけて運用しています。一方「特定のクロスプラットフォームフレームワークを選択して一つのコードをメンテナンスする」を選択する場合、開発されるアプリケーションはネイティブアプリより操作性や機能は劣るが、両方をネイティブ開発するより半分程度のコストですむことになります。
参考)Why choose Flutter for your company?
しかし、Flutterは第三の選択肢として機能します。FlutterはDart言語でプログラミングすると完全にネイティブなiOS用コードとAndroid用コードを生成してくれます。さらにはこれをリアルタイムで行うため、開発したスマホアプリを動かしながらDart言語によるソースコードを修正し、画面を修正することができます(この機能をホットリロードといいます)。このようにFlutterを使うことで、ワンコードで同じUIのiPhoneとAndroid両方のネイティブアプリを開発することが可能になります。
またFlutterを選択することで得られるビジネス上のメリットを以下に示します。
なお、Flutterと比較されるクロスプラットフォーム開発環境と特徴を以下に紹介します。
以上のことを総合的に判断し、Flutterをベストな選択であると考え採用しました。
Flutterのよさだけを強調すると初めての方は少ない知識で何も考慮することなくアプリが開発できると勘違いするとよくないので、Flutterを正しく学ぶためのマインドセットを最後に紹介します。
※ 本記事は下記の動画を参考に作成しました。
https://www.youtube.com/watch?v=kD-1dZBmRPU
本書で開発するレストランレビューアプリもそうですが、世の中のスマホアプリはインターネット上のサーバと連携してサービスを提供します。通常インターネット上のサーバはアプリに入力したデータを保存したり、アプリに表示するデータを提供するデータベースが主要な機能になりますが、スマホアプリからのアクセスに通常のWebアプリが採用しているODBCやJDBCのようなしくみを選択するのはセキュリティ上の理由からも推奨されません。そのため、アプリからのアクセスの際、テーブルへのCRUDのどの機能が必要かを考えてAPIを開発し、さらに認証認可のしくみを実装することが必要になります。
一方、Firebaseは、アプリにログインする認証のしくみ(Firebase Auth)とアプリと安全にコネクションを繋いだままストリーム処理が可能なデータベース(Firestore)、写真などの大きめのバイナリファイルを保存するためのストレージ(Firestorage)といったサービスを提供します。またクライアント用には、Firebase Authで認証されたユーザIDと紐づけてFirestoreデータベースにデータを保存したり、FirestorageのURIをFirestoreデータベースに保存して利用する機能を、様々なプログラミング言語向けに標準的なSDKとして提供されています。そのため、一からバックエンドのサービスを開発するのに比べ、基本的な機能をほとんど開発する必要がないため、開発工数を劇的に削減することができます。
※ Firebase Authにおけるブルートフォース攻撃対策については下記記事を参照してください。
https://medium.com/swlh/google-firebase-authentication-vulnerability-245050cb7ceb
さらには、FirebaseはA/Bテストを行う機能やスマホアプリのクラッシュログを収集する機能、ユーザがどの画面のどのボタンをクリックしたかといった動線を集計する機能、スマホアプリをテストユーザに配布する機能などモバイルアプリ用に豊富な機能を安価に提供しているため、多くのモバイルアプリ開発者に採用されています。
以上の理由から本書ではバックエンドの機能にFirebaseを採用することにしました。
企業におけるITツールの選定で気をつけるべき点にベンダーロックインの排除が挙げられます。というのも、採用技術の開発が止まったり急に利用コストが値上げされ別の方法を模索しなければならなくなったり、アプリを横展開して他社に提供する場合先方のプラットフォームにあわせなければならない、といったことがありうるため、採用時のメリデメだけでなく他に乗り換える可能性も考慮しなければなりません。
まず開発環境としてのFlutterについては、100%オープンソースであり、プロジェクトがストップしたりいきなり有料サービスになることは、いまとなってはまずあり得ないため、Flutterを採用することによるベンダーロックインはありえないと考えてよいでしょう。
一方、Firebaseについてはアプリの横展開をする際にクラウドサービスを使いたくないケースが考えられるので、なんらかの代替案を考える必要があります。Firebaseのサービスごとに代替案を以下に紹介します。
Googleが公開している12コマの動画シリーズをベースにNoSQLデータベースとFirestoreを解説します。また最後に実践を通して得られたNoSQLの限界について記載します。
ここで紹介する動画シリーズは、GoogleのTodd Kerpelman氏による「Get to know Cloud Firestore」という動画シリーズで、Firestoreを使い始めるにあたって知っておくべきことを12回にわたって解説しているものです。本書に添付された動画は全画面表示が可能ですし、英語の字幕を自動表示する設定になっていますが、英語が苦手な人はYoutube画面の右下の設定で字幕をONにし、さらに字幕の自動翻訳を選び日本語を選択して日本語字幕を表示することも可能です。
Firestoreにおけるルール
Firestoreでは、JSONの構造における配列([]で表す)とマップ({}で表す)をドキュメント型リアルタイム・データベースとしてうまく操作できるようコレクション(JSON内の配列に相当)とドキュメント(JSON内のマップに相当)にうまく分離して保存管理するよう設計します。この設計をうまく行うためにはFirestoreにおけるいくつかのルールを理解しておく必要があります。
ルール1: ドキュメントには制約がある
ルール2: 検索結果はドキュメント全体が出力される
ルール3: 検索対象は浅い
ルール4: 課金はドキュメントの読み書き回数でカウントされる
ルール5: 配列操作は奇妙である
参考)https://firebase.googleblog.com/2014/04/best-practices-arrays-in-firebase.html
ドキュメント型データベースにおいて、ユースケースに適したデータ構造を設計するとき主に以下の3種類の構造から適切なものを選びます。
タイプ1. ドキュメントの一部として配列もしくはマップとして含める
タイプ2. オブジェクトをサブコレクションにおさめる
タイプ3. オブジェクトをルートレベルのコレクションにおさめる
https://cloud.google.com/firestore/docs/concepts/structure-data?hl=ja
https://cloud.google.com/firestore/docs/security/get-started
https://firebase.google.com/docs/reference/security/database
参考)
リアルタイムデータベースの操作について下記の迷信がありますが、Firestoreはこれらの迷信を覆します。そのためFirestoreにおいてはデフォルトでリアルタイム機能を使うよう設計し開発をすすめることをおすすめします。
いいえ。Firestore用SDKがストリーム処理をしてくれるので使い方を覚えれば実装は簡単です。
いいえ。更新された分だけが読み込み数にカウントされ、むしろ全部を読み込む方式の方が課金されます。リアルタイム処理の方が一般的に課金が少なくなります。
いいえ。リスナーを常駐させることは電力消費を増やしません。
https://www.youtube.com/watch?v=DYfP-UIKxH0
Cloud Functionsをどのような場合に利用すればよいか、5つのパターンにまとめると以下の通りです。ただし繰り返しになりますがリアルタイムデータベースのメリットが享受できなくなる欠点があることは考慮すべきでしょう。
最後にいままでのオフィシャル動画とは直接関係ありませんが、Cloud Firestoreが提供するNoSQLのしくみは安全で使い勝手が良くスケーラブルでいいことづくめに見えますが、やはりリレーショナルデータベースにしか表現できないデータモデルというものがあり、NoSQLの限界というものが存在します。例えば、プロジェクトと要員を紐付けたい場合にマッチング値を計算して中間テーブルに持ち、特定のプロジェクトに適した要員をマッチング値でソートしたり、特定の要員に適したプロジェクトをマッチング値でソートしたりすることはリレーショナルデータベースでは可能ですが、NoSQLには構造上できません。NoSQLでこれを実現するには、プロジェクト・コレクションに要員マッチング値という配列項目を用意して、逆に要員コレクションにプロジェクト・マッチング値という配列項目を用意してそれらを使って検索を行う必要があります。この方式の難点は重複してデータを持つことになることとプロジェクトや要員が新規に追加される際にマッチング値を再計算してデータ更新が必要になることです。
NoSQLの利用はプロトタイプ開発などすぐにアプリを開発する必要がある場合にはよいでしょうが、データモデルの機能によっては適さない場合があるので、気をつけるとよいでしょう。
FlutterはDartと呼ばれる新しいプログラミング言語で実装されており、Dart言語を使ってプログラミングを行います。DartはJavaScriptに似た言語で、Webアプリケーションにおいて必要な非同期処理やストリーミング処理にたけたオブジェクト指向言語です。JavaやJavaScriptなどの言語をマスターした読者であればすぐに学習して使えるでしょう。
ここまでの説明から、アプリケーション開発の基本はデータモデルとアプリ画面のマッピングであることは理解してもらえたかと思います。そのため、アプリ開発においてDart言語が効果的に機能する重要なしくみとしては「1. 認証サービスやデータベースとネットワークごしに非同期でやりとりするデータをうまく処理すること」と「2. 得られたデータ(特にリスト型)を画面表示用にうまく変換すること」といえます。そのため本書では、2のリスト型のデータ変換理解のため「コレクション型」と1の非同期で流れてくるストリームの瞬間をとらえるスナップショット理解のため「非同期処理」を、Dart言語の特徴的で使える機能としてピックアップして解説します。
Dartで定義できるコレクション型(同じ型やクラスであるエレメントをまとめてあつかう集合型)には以下の4種類があります。
Flutterでのデータモデルとアプリ画面間の変換にはListとMapをよく使うので、それらの使い方と変換方法をステップバイステップで説明します。
1. レストランクラスの作成
ID、名前、タイプ、住所、ロゴ画像URL、星の数といった属性をもつクラスを実装し、そのインスタンスを作成しprint表示します。
2. JSONからのレストランクラスの作成
'dart:convert'パッケージに含まれる jsonDecodeメソッドを使うと、一つのドキュメントをあらわす「{ }」から始まるJSON文字列からはマップを作成することができるので、このマップからレストランクラスのインスタンスを作成しprint表示します。
3. マップからレストランクラスを生成するファクトリ
マップからレストランクラスのインスタンスを作成する部分を、レストランクラスにファクトリ関数「fromMap(String id, Map<String, dynamic> data)」を作成して実装します。
4. JSONからのレストランクラスの配列作成
jsonDecodeメソッドを使うと「[ ]」から始まるJSON文字列からはリストを作成することができるので、このリストからレストランクラスの配列を作成します。JSON文字列をよく見ると「[ ]」の中の配列要素はドキュメントをあらわす「{ }」から成り立つのでマップに変換されます。そのマップを3でつくった「fromMap」関数でレストランクラスに変換して、レストランクラスの配列を生成しています。
5. リストからレストランクラスの配列作成
JSON文字列から得られたマップのリストを「.map((val)=>function(val)).toList()」という関数を使って、Restoインスタンスのリストに変換します。このしくみはデータベース上のドキュメントデータをマップとして取得するFirestoreの場合でも利用します。変数名や型が不明なマップからRestoクラスに変換することで、画面表示がプログラミングしやすくなります。
一般的に、プログラム言語における関数処理においては入力と出力から成り立ちます。関数の引数が入力となり、戻り値が出力となるわけです。ここで例えばネット上の画像データを関数の引数にとり、その画像を画面に表示することを考えます。するとこの関数やその戻り値を画面表示するプログラムは、ネットから画像をダウンロードする時間入力値の到着を待つ必要があります。
また複数人によるグループチャットアプリを想像してみてください。だれかがグループあてにメッセージを打ち込むとします。そのメッセージはデータベースに追加されると同時に送信者以外のグループメンバーにブロードキャストされますが、このようなしくみを実装するにはグループチャット用データベースとチャットアプリがリアルタイムでつながっていて、チャットアプリがデータベース上の変化を検知する必要があります。このしくみを支援するプログラム上のしくみがストリームです。今回はデータベースとしてfirestoreを使うので、firestoreとアプリの常時接続や変化の検知、キャッシュや差分更新といったしくみはfirestoreがアプリ用に提供するSDKを使うことができます。Dart言語の方ではチャット内容を表すチャットクラスの配列を用意し、上記のSDKと連携して変化検知のたびにマップからチャットクラスへのデータ変換を行う必要がありますし、こちらがメッセージを送る際はチャットオブジェクトをマップ変換してデータベースに送信する必要があります。
上記に挙げた処理のことを非同期処理と呼びますが、取得する値が一つの値(ネット上の画像ダウンロードのケース)か、コレクション(グループチャットのケース)かによって、Dartでは扱うクラスが異なります。
FutureとStreamの実装例を以下に示します。実際には、FutureもStreamもURLを指定してHTTP経由でデータを取得するクラスやfirestoreのライブラリから取得するので、それらを扱う方法を理解することに集中すれば実装はできるのですが、ここではあえてFutureとStreamのしくみから説明しています。
1. Futureの実装
JSON文字列からレストランオブジェクトを5秒遅れて取得してprint表示するFutureの例です。
2. Streamの実装
JSON文字列から取得したマップのリストから1秒おきにレストランオブジェクトを取得してStreamにsinkしてlistenでprint表示するStreamの例です。
Dart言語が理解できたらFlutterを使ってさまざまな画面と画面遷移を実装します。ここではFirebaseを使わず、JSONの静的データから「レストランレビューアプリ」の一部の画面と画面遷移を実装します。
Flutterの開発を行うには、画面の部品を構成するWidget(ウィジット)の理解が必要です。「3. 画面設計」でも述べたとおり、画面と画面遷移を構成するにはその裏で状態(ステート)の管理が必要です。例えば、ウィザード遷移を行いアンケートに答える場合には画面間を入力済みのアンケート回答情報をステートに保存し、確認画面表示に利用するとともにデータベースへの送信情報として利用します。FlutterにおけるWidgetにはStatefulWidget(ステートフル・ウィジット)とStatelessWidget(ステートレス・ウィジット)の2種類のWidgetが提供されており、用途によって下記のような使い分けをします。
StatefulWidget: | 一画面の中で状態変数を保持し、変数に変更があったら画面をリロードして変更された変数にあわせて画面表示の変更が必要な画面を実装する際に利用します。「レストランレビューアプリ」では、一画面の中でログインとユーザ登録を切り替える「ログイン画面」とボトムナビゲーションバーを制御する「ホーム画面」、アニメーションで検索用のスライドアップを表示する「レビュー画面」、レビューの編集後に画面リロードが必要な「レビュー詳細画面」、レビューの編集・削除後に画面リロードが必要な「アカウント画面」、レビュー追加後に画面リロードが必要な「レストラン詳細画面」と「レストラン画面」をStatefulWidgetで実装します。 ※(著者メモ)「レビュー詳細画面」「アカウント画面」「レストラン詳細画面」「レストラン画面」はChangeNotifierProviderを使えばStatelessWidgetで実装できるかもしれない。 |
StatelessWidget: | 静的な画面。ChangeNotifierとConsumerを使ってウィジット部品を管理することで、状態変化のたびリロードされる箇所をウィジット部品に限定することができます |
ここではこの両Widgetを理解するために、ブラウザ上でFlutterをプログラミングし実行できるDartpadを使って説明しています。各説明文の下に記載されたリンクをクリックするとDartpadがソースコードと一緒に開きますので、プログラムを実行したい場合は右上のRUNボタンをクリックすると右側の画面にアプリが表示されます。前章と同様にDartpadを本画面上に直接iframeで表示することができるのですが、ブラウザに大量の負荷をかけるため、ワンステップずつDartpad画面を別タブで開いて学習してもらう形式にしています。
StatelessWidgetを使ってシンプルなログイン画面を実装します。
ログインボタンを押すとメールアドレスやパスワードが入力されているか?ルールにのっとって入力されているかをチェックして、条件を満たさない場合エラーメッセージを表示してくれるよう実装します。
FocusNodeクラスを使い、フィールド入力が完了し、エンターを押したら次のフィールドにキー操作が移動し、最後のフィールド入力後エンター押下でログインボタンをクリックしたこととなり、ホーム画面に遷移するよう実装します。
キー入力でフォーカスが移動しホーム画面に遷移するログイン画面
ログイン画面とホーム画面をログイン/ログアウト状態を管理するステートを持つランディング画面でつなげるよう実装します。この実装はのちに利用するProviderの簡易実装です。
ログインボタンと登録ボタンをクリックすることでログイン画面と登録画面を切り替えられるようログイン画面を修正します。一つのWidgetで切り替えを行うにはStatelessWidgetから状態(State)を持つStatefulWidgetに変更が必要なのでLoginクラスにリファクタリングを行います。
ホーム画面にメニューとボトムナビゲーションバーを追加して、3画面切り替えられるように実装します。そのために、StatefulWidgetにリファクタリングして、PageViewを使った実装を行います。
ホーム画面のボトムナビゲーションバーで切り替える画面の一つにJSON文字列から取得したレストランのリストをGridViewで表示する画面を実装します。
RestoPageのGridViewページを追加したホーム画面
前の2つの章では、Dartpadを使ってブラウザ上でプログラムを試しましたが、ここからはパソコン上のFlutterの開発環境とVSCodeエディタを使った開発を開始してもらいます。開発対象がスマホアプリなので、Android用の開発環境(Android Studio)やiPhone用の開発環境Xcodeが必要と思われがちなのですが、これらは大量のディスクスペースと実行時にハイスペックなCPUと大規模メモリを要求しますので、これを極力回避するためFlutter Webをつかって開発を行うこととします。
Flutter WebというWebアプリを開発するとなるとスマホのネイティブアプリを開発する本書の目的とは違うと思われそうですが、これが真のクロスプラットフォームフレームワークであるFlutterのよいところで、Flutter Webを開発すればそのソースコードがiPhoneアプリとAndroidアプリ用にもほぼそのまま転用できるのです。いくつかスマホ用には対応していないFlutterプラグインがありますが、それらの機能はスマホアプリをコンパイルする際に追加することとして、ここではFlutter Webとしての「レストランレビューアプリ」を開発します。
また、Flutter FlowというWebアプリを使うと、Firebaseとの接続をWeb上の設定で行うことができ、UI開発をGUI形式で行うことが可能です。Flutter Flowのフル機能を使うには月額数千円必要ですが、非常に協力なしくみなので使用を検討するとよいでしょう。サンプルのFlutterMetアプリの構築動画を観るとこのツールの強力さがわかるでしょう。
いままで整理してきたユースケースやデータモデルや画面設計のとおり、顧客がレストランのレビューを書き込むアプリケーションを開発します。できあがったFlutter Webアプリのイメージは下記のとおりです。この画面は裏でFirebaseにつながっており実際のWebアプリケーションとして利用できます。顧客としてユーザ登録を行いレストランを参照しレビューを書き込むことができます。
下記サンプルアプリに Email: ichiro@test.com, Password: password でログインしてみてください。
本書のハンズオンではGithubからソースコードをダウンロードしてきて、Firebaseとの接続設定を行うことでこのアプリケーションをローカル環境で動かすことができるようにします。その後、ソースコードを眺めながら、実装方法について説明をしていきます。
そのために、まずはお手持ちのパソコンにFlutterと開発用エディタであるVSCodeのインストールを行います。
まずはじめに開発に必要なFlutterとエディタであるVS Codeのインストールを行います。
- Flutterのインストール
下記リンクから自らの開発PCのOSを選択してFlutter SDKをインストールします。
https://flutter.dev/docs/get-started/install
本書ではFlutter Webのみを利用しますので、上記リンクに記載のあるAndrlid SetupやiOS Setupの作業は必要ありません。
- VS Codeのインストール
下記リンクに従ってVS Codeをインストールします。
https://flutter.dev/docs/get-started/editor?tab=vscode
- Chromeのインストール
開発したWebアプリをデバッグするためにブラウザが必要ですが、Flutterで推奨されているChromeをインストールします。
https://www.google.com/intl/ja_jp/chrome/
- PowerShellのインストール(Windowsの場合)
Windows環境の場合ターミナルが使いやすくなるように下記リンクからPowerShellをインストールしておくと良いです。
https://github.com/PowerShell/PowerShell
- インストールの確認
上記のインストールが完了しているかを確認するため「flutter doctor」コマンドを実行します。下記の通り、FlutterとVS CodeとChromeにチェック「✔︎」が入っていますが、AndroidとXcodeにはインストールがされていないことを表すバツ「✖︎」が表示されます。
$ flutter doctor Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel beta, 1.20.0, on Mac OS X 10.15.6 19G2021, locale ja-JP) [✗] Android toolchain - develop for Android devices ✗ Unable to locate Android SDK. Install Android Studio from: https://developer.android.com/studio/index.html On first launch it will assist you in installing the Android SDK components. (or visit https://flutter.dev/docs/get-started/install/macos#android-setup for detailed instructions). If the Android SDK has been installed to a custom location, set ANDROID_SDK_ROOT to that location. You may also want to add it to your PATH environment variable. [✗] Xcode - develop for iOS and macOS ✗ Xcode installation is incomplete; a full installation is necessary for iOS development. Download at: https://developer.apple.com/xcode/download/ Or install Xcode via the App Store. Once installed, run: sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer sudo xcodebuild -runFirstLaunch ✗ CocoaPods not installed. CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your plugin usage on the Dart side. Without CocoaPods, plugins will not work on iOS or macOS. For more info, see https://flutter.dev/platform-plugins To install: sudo gem install cocoapods [✓] Chrome - develop for the web [!] Android Studio (not installed) [✓] VS Code (version 1.45.1) [✓] Connected device (2 available) ! Doctor found issues in 3 categories.
Flutterで開発を始めるにあたってflutterコマンドを使ってFlutter Webプロジェクトの初期フォルダを作成します。
- Flutter Webの設定
FlutterによるWebアプリ開発のためには、Flutterコマンド「flutter config --enable-web」を実行してWebアプリ開発を有効化します。
$ flutter --version Flutter 1.21.0-10.0.pre.114 • channel master • https://github.com/flutter/flutter.git Framework • revision b2c0970aa7 (8 hours ago) • 2020-08-16 06:11:02 -0400 Engine • revision b300be3df3 Tools • Dart 2.10.0 (build 2.10.0-34.0.dev) $ flutter config --enable-web Setting "enable-web" value to "true". You may need to restart any open editors for them to read new settings.
- Flutterプロジェクトの作成と実行
Flutterで開発を始めるには"flutter create [プロジェクト名]"というコマンドを使ってFlutterプロジェクトの初期フォルダを作成します。また、コマンドオプション「--org [パッケージ名]」を追加すると、Dartコードから生成されるiOS用SwiftコードやAndroid用Kotlinコードのパッケージ名を指定できます。私はパッケージ名はドメイン名を反対にした形式をとるので、例えば弊社ドメイン名"sonrisa.co.jp"にアプリのグループ名"samples"を追加した"samples.sonrisa.co.jp"を反対にした"jp.co.sonrisa.samples"を指定しましたが、みなさんの環境では自分にあったパッケージ名を決めて実行してください。このパッケージ名はアプリをAppStoreやGoogle Playに登録する際やFirestoreプロジェクトにアクセスできるアプリを登録する際に使われる重要なものなので、あらかじめルールを決めて指定するように心がけましょう。また、ここでのプロジェクト名は、レストランレビューを登録するアプリであるため「RestoReview」にしてFlutter Webプロジェクトを作成します。
$ flutter create --org jp.co.sonrisa.samples RestoReview Creating project RestoReview... RestoReview/ios/Runner.xcworkspace/contents.xcworkspacedata (created) ... Running "flutter pub get" in RestoReview... 2.5s Wrote 76 files. All done! [✓] Flutter: is fully installed. (Channel master, 1.21.0-10.0.pre.114, on Mac OS X 10.15.6 19G2021, locale ja-JP) [✗] Android toolchain - develop for Android devices: is not installed. [✗] Xcode - develop for iOS and macOS: is not installed. [✓] Chrome - develop for the web: is fully installed. [!] Android Studio: is not available. (not installed) [✓] VS Code: is fully installed. (version 1.45.1) [✓] Connected device: is fully installed. (2 available) Run "flutter doctor" for information about installing additional components. In order to run your application, type: $ cd RestoReview $ flutter run Your application code is in RestoReview/lib/main.dart.
プロジェクトを作成するとプロジェクト名のフォルダが作成されます。その中身は下記のとおり、プロジェクトの設定を行う"pubspec.yaml"ファイル、Dart言語で書かれたソースコードを置く"lib"フォルダ、プラットフォームごとに自動生成されたソースコードを置く"ios"、"android"、"web"フォルダなどから構成されます。
RestoReview $ tree . . ├── pubspec.yaml ├── lib │ └── main.dart ├── ios ... ├── android ... └── web ├── favicon.png ├── icons │ ├── Icon-192.png │ └── Icon-512.png ├── index.html └── manifest.json 50 directories, 80 files
また、初期状態としてカウンターアプリが生成されますので、それを実行します。
$ cd RestoReview $ flutter run -d chrome
するとChromeブラウザが開き、カウンターアプリが表示されます。
本Flutterプロジェクトは、インストール確認テスト用に作成したものなのでまるまる削除してください。
Flutter開発の初期設定が完了したのでインフラ部分を担うFirebaseの設定を行います。また途中でGithubリポジトリから「RestoReview」プロジェクトをダウンロードして、Firebaseから利用できるよう設定を行います。
Firebase Consoleにログインして「プロジェクトを追加」をクリックし、名前を「RestoReview」と命名し、「続行」ボタンをクリック、Google Analyticsの設定後、「プロジェクトを作成」ボタンをクリックしてFirebaseプロジェクトを作成します。
Firebaseには様々な認証をサポートする機能としてFirebase Authenticationがあります。画面左メニューから「Authentication」を選択し、「Sign-in method」タブを選択します。
「メール/パスワード」を有効化するため、「Authentication」画面のログインプロバイダのリストの最上部「メール/パスワード」行右端の鉛筆アイコンを選択し表示される下記ダイアログで「有効にする」を選択し、保存ボタンをクリックします。
さらに「Authentication」画面の「Users」タブを選んで、「ユーザ追加」から下記の3つのメールアドレスを追加しておいてください。パスワードは任意です。これらのユーザは「RestoReview」アプリから利用します。
アプリケーションが利用するデータベースとしてCloud Firestoreを有効化します。
以上によりRestoReviewプロジェクトでCloud Firestoreが使えるようになります。
下記コマンドを実行して、GithubからFlutterプロジェクト「RestoReview」をダウンロードします。
$ git clone https://github.com/david3080/RestoReview.git ... $ cd RestoReview/
Firebaseのコマンドラインインタフェース(CLI)を使うと、開発したWebアプリや設定ファイルをFirebaseに直接デプロイすることができます。本作業は前回ダウンロードした「RestoReview」フォルダ下で実行します。
$ npm -g install firebase-tools
$ firebase --version
$ $ firebase login i Firebase optionally collects CLI usage and error reporting information to help improve our products. Data is collected in accordance with Google's privacy policy (https://policies.google.com/privacy) and is not used to identify you. ? Allow Firebase to collect CLI usage and error reporting information? Yes
ChromeブラウザがオープンしてFirebaseにログインするアカウントの選択を促してくるので、該当アカウントを選択してログインします。
ログインが成功するとFirebase CLIに許可を求められるので「許可」ボタンをクリックして許可すると...
ログインが成功します。
$ firebase projects:list
$ firebase use restoreview-xxxxx
$ firebase init
######## #### ######## ######## ######## ### ###### ######## ## ## ## ## ## ## ## ## ## ## ## ###### ## ######## ###### ######## ######### ###### ###### ## ## ## ## ## ## ## ## ## ## ## ## #### ## ## ######## ######## ## ## ###### ######## You're about to initialize a Firebase project in this directory: /.../RestoReview ? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices. ? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices. ◯ Database: Deploy Firebase Realtime Database Rules ◉ Firestore: Deploy rules and create indexes for Firestore ◉ Functions: Configure and deploy Cloud Functions ◉ Hosting: Configure and deploy Firebase Hosting sites ❯◉ Storage: Deploy Cloud Storage security rules ◯ Emulators: Set up local emulators for Firebase features
=== Firestore Setup Firestore Security Rules allow you to define how and when to allow requests. You can keep these rules in your project directory and publish them with firebase deploy. ? What file should be used for Firestore Rules? (firestore.rules)
Firestore indexes allow you to perform complex queries while maintaining performance that scales with the size of the result set. You can keep index definitions in your project directory and publish them with firebase deploy. ? What file should be used for Firestore indexes? (firestore.indexes.json)
=== Functions Setup A functions directory will be created in your project with a Node.js package pre-configured. Functions can be deployed with firebase deploy. ? What language would you like to use to write Cloud Functions? TypeScript ? Do you want to use TSLint to catch probable bugs and enforce style? Yes ✔ Wrote functions/package.json ✔ Wrote functions/tslint.json ✔ Wrote functions/tsconfig.json ✔ Wrote functions/src/index.ts ✔ Wrote functions/.gitignore ? Do you want to install dependencies with npm now? (Y/n)
=== Hosting Setup Your public directory is the folder (relative to your project directory) that will contain Hosting assets to be uploaded with firebase deploy. If you have a build process for your assets, use your build's output directory. ? What do you want to use as your public directory? build/web ? Configure as a single-page app (rewrite all urls to /index.html)? No ✔ Wrote build/web/404.html ✔ Wrote build/web/index.html
=== Storage Setup Firebase Storage Security Rules allow you to define how and when to allow uploads and downloads. You can keep these rules in your project directory and publish them with firebase deploy. ? What file should be used for Storage Rules? storage.rules i Writing configuration info to firebase.json... i Writing project information to .firebaserc... ✔ Firebase initialization complete!
画面灰色部分を先ほどダウンロードした「RestoReview」フォルダ配下の「web/index.html」ファイル内の下記箇所に上書き保存します。
// ここを上書きする const firebaseConfig = { apiKey: "...", authDomain: "...", databaseURL: "...", projectId: "...", storageBucket: "...", messagingSenderId: "...", appId: "...", measurementId: "..." }; // ここまで
$ pwd .../RestReview $ flutter build web
$ firebase deploy --only hosting === Deploying to 'restoreview-824b4'... i deploying hosting i hosting[restoreview-824b4]: beginning deploy... i hosting[restoreview-824b4]: found 15 files in build/web ✔ hosting[restoreview-824b4]: file upload complete i hosting[restoreview-824b4]: finalizing version... ✔ hosting[restoreview-824b4]: version finalized i hosting[restoreview-824b4]: releasing new version... ✔ hosting[restoreview-824b4]: release complete ✔ Deploy complete! Project Console: https://console.firebase.google.com/project/restoreview-824b4/overview Hosting URL: https://restoreview-824b4.web.app
ここでは
「レストランレビューアプリ」の画面設計で説明した「①ログイン、メニュー選択、ログアウト」の各画面は図のようなクラスで実装されています。
1. ログイン画面(LoginPage)の実装
(TODO)
2. LandigPageの実装
ログイン状態を随時チェックしてログイン画面(LoginPage)とホーム画面(HomePage)を切り替えるためにLandingPageが配置されています。
(TODO)
3. ホーム画面(HomePage)の実装
(TODO)
「レストランレビューアプリ」の画面設計で説明した「②レストランリストとレストラン詳細、レビュー表示とレビュー追加」の各画面は図のようなクラスで実装されています。
1. レストラン画面(RestoPage)の実装
(TODO)
2. レストラン詳細画面(RestoDetail)の実装
(TODO)
3. レビュー詳細(ユーザ画面付)画面(UserReviewDetail)の実装
(TODO)
4. レビュー追加画面(ReviewEdit)の実装
(TODO)
「レストランレビューアプリ」の画面設計で説明した「③全レビューリストとレビュー表示」の各画面は図のようなクラスで実装されています。
1. レビュー画面(ReviewPage)の実装
(TODO)
2. レビュー詳細(ユーザ画面付)画面(UserReviewDetail)の実装
(TODO)
「レストランレビューアプリ」の画面設計で説明した「④アカウント表示と自分が投稿したレビューの管理」の各画面は図のようなクラスで実装されています。
1. アカウント画面(AccountPage)の実装
(TODO)
2. レビュー詳細(ReviewDetail)の実装
(TODO)
3. レビュー編集(ReviewEdit)の実装
(TODO)
(TODO) StatefulWidgetとStatelessWidgetの違い、Stateの意味、Providerパッケージ、RiverPodパッケージ、GetXパッケージ、RxDartパッケージなどを説明します。
#1 - Why BLoC? | BLoC - from Zero to Hero
(TODO)
(TODO)
https://codelabs.developers.google.com/codelabs/flutter-app-testing/#0
https://qiita.com/yujikawa/items/fe509b160df5ab9eb74e
(TODO) Flutter WebプロジェクトにAndroidとiOS用フォルダを追加する方法(flutter create . --org [パッケージ名])。XcodeインストールとAndroid SDK(コマンドのみhttps://qiita.com/undrthemt/items/1861d5fe39be46a2b776)をインストールしてビルドする方法。Firebaseにアプリを追加して必要ファイルをダウンロードして配置する方法。)
(TODO) Firebase App Distributionの方法を解説
https://firebase.google.com/docs/app-distribution/ios/distribute-console?hl=ja
(TODO) 本書での構成はフロントのネイティブアプリ開発はFlutterにまかせ、バックエンドもFirebaseにまかせるので、APIやWebアプリの開発は発生せず、それらの脆弱性は考える必要がない。そのため、Flutterで構築するネイティブアプリの脆弱性回避とFirebaseの主要機能であるFirestoreとFirestorageとCloud Functionsで妥当なセキュリティルールの設定をすることに注力するだけでよい。あとは脆弱性診断など第三者によるチェックの仕方を考えることくらい。
APIキーについて、Firebase HostingでもAPIキーファイルはJSとして提供されて誰でもダウンロードできるようになっているので、誰にもアクセスできないようにすべきものではない。大事なのはセキュリティルール。
参考)Flutterアプリ開発七つの大罪:https://qiita.com/__naoya__/items/98ac66157a1578be2798
参考)Flutter Security:https://flutter.dev/security
参考)How to make a flutter app with high security? :https://medium.com/flutter-community/how-to-make-a-flutter-app-with-high-security-880ef0aa54da
参考)Mobile App Security:https://www.linkedin.com/pulse/mobile-app-security-native-vs-flutter-rick-wu
参考)Storing your secret keys in Flutter:
https://medium.com/@sokrato/storing-your-secret-keys-in-flutter-c0b9af1c0f69
参考)Do you need to hide your Firebase API keys for Ionic apps?
https://javebratt.com/hide-firebase-api/
参考)多くのモバイルアプリに「バックドアシークレット」、オハイオ州立大などが発表
https://www.atmarkit.co.jp/ait/articles/2004/19/news014.html
(TODO)
Firebase参考:https://www.youtube.com/watch?v=iWEgpdVSZyg
せっかくFirebaseに一本化したのでサービスの稼働状況をスマホアプリから見れるようにするとよいかも https://status.firebase.google.com/
(TODO)