
【まとめ】Discord風アプリで学んだUI構築を他のアプリに応用する
この節で学ぶこと
ここまでの章では、FlutterでDiscord風チャットアプリを作りながら、UI構築の基本を学んできました。
最初は、画面を観察するところから始めました。
Discord風の画面を観察する
↓
どんな領域があるか分ける
↓
必要なデータを考える
↓
小さなWidgetを作る
↓
状態を持たせる
↓
操作で状態を変える
↓
PC・タブレット・スマホに対応する
この流れは、Discord風アプリだけで使うものではありません。
ECアプリ、SNSアプリ、業務管理アプリ、予約アプリ、医療向けアプリ、教育アプリなど、ほとんどのアプリ開発に応用できます。
この節で一番大切なのは、次の一文です。
UI開発は、見た目をいきなり作るのではなく、画面を観察し、データと部品に分け、状態と操作をつなぐことで整理して作れる。
この章で作ってきたもの
この章では、Discord風チャットアプリを題材に、次のような部品を作りました。
ServerRail
ChannelSidebar
ChatArea
ChatInputBar
MemberPanel
ProfileCard
ProfileEditorDialog
MobileDiscordLayout
DesktopDiscordLayout
完成形の画面は、次のような構造でした。
PC表示
┌────────────┬────────────────┬──────────────────────┬──────────────┐
│ ServerRail │ ChannelSidebar │ ChatArea │ MemberPanel │
└────────────┴────────────────┴──────────────────────┴──────────────┘
スマホでは、横幅が足りないため、次のようにしました。
スマホ表示
┌──────────────────────────────┐
│ ChatTopBar │
├──────────────────────────────┤
│ ChatArea │
└──────────────────────────────┘
左Drawer
└─ ServerRail + ChannelSidebar
右Drawer
└─ MemberPanel
ここで大切なのは、PCとスマホでまったく別のアプリを作ったわけではないということです。
同じ部品を使い、配置を変えました。
同じ部品
↓
PCでは横に並べる
↓
スマホではDrawerに収納する
この考え方が、レスポンシブUI構築の基本です。
この章で学んだ流れ
この章全体で学んだ流れを、もう一度整理します。
1. 画面を観察する
2. 必要なデータを考える
3. 画面を領域に分ける
4. 領域をWidgetにする
5. データclassを作る
6. 状態を親Widgetで持つ
7. 子Widgetにデータを渡す
8. ユーザー操作で状態を変える
9. setStateで画面を更新する
10. PC・タブレット・スマホで配置を変える
11. 完成コードを役割ごとに読み解く
この流れは、最初は少し長く感じるかもしれません。
しかし、実務でも教材作成でも、基本的にはこの流れで考えると、複雑なアプリでも整理しやすくなります。
1. 画面を観察する
最初に行ったのは、Discord風の画面を観察することでした。
いきなりコードを書いたわけではありません。
まず、画面を見て、どんな領域があるかを分けました。
左端のサーバー一覧
左側のチャンネル一覧
中央のチャット画面
右側のメンバー一覧
下部の入力欄
プロフィールカード
プロフィール編集画面
これをFlutterの部品名に置き換えると、次のようになります。
| 画面の領域 | Flutterで作る部品 |
|---|---|
| サーバー一覧 | ServerRail |
| チャンネル一覧 | ChannelSidebar |
| チャット画面 | ChatArea |
| 入力欄 | ChatInputBar |
| メンバー一覧 | MemberPanel |
| プロフィールカード | ProfileCard |
| 編集画面 | ProfileEditorDialog |
このように、画面を観察して部品名に変換できると、コードを書く前に設計が見えてきます。
初心者がいきなりコードを書こうとすると、どこから始めればいいか分からなくなります。
でも、画面を領域に分けると、作るべきものが見えてきます。
画面を見る
↓
領域に分ける
↓
Widget名に変換する
これがUI開発の第一歩です。
2. 必要なデータを考える
次に考えたのは、画面に表示するデータです。
Discord風アプリでは、次のようなデータが必要でした。
サーバー情報
チャンネル情報
メッセージ情報
ユーザープロフィール情報
オンライン状態
それぞれをclassとして整理しました。
DiscordServer
DiscordChannel
ChatMessage
UserProfile
OnlineStatus
ChannelType
たとえば、チャットメッセージなら、次のような情報が必要です。
投稿者名
ハンドル名
アイコン色
アイコン画像URL
本文
投稿時刻
オンライン状態
システムメッセージかどうか
これを ChatMessage classにまとめました。
class ChatMessage {
const ChatMessage({
required this.userName,
required this.handle,
required this.avatarColor,
required this.avatarUrl,
required this.body,
required this.timestamp,
required this.status,
this.isSystem = false,
});
final String userName;
final String handle;
final Color avatarColor;
final String avatarUrl;
final String body;
final DateTime timestamp;
final OnlineStatus status;
final bool isSystem;
}
ここで大切なのは、画面に表示する情報を、バラバラの変数ではなく、意味のあるclassにまとめたことです。
バラバラの値
↓
意味のあるデータclass
この考え方は、どんなアプリでも使います。
3. 画面を部品に分ける
次に、画面を小さなWidgetに分けました。
たとえば、チャット画面は、次のように分けられます。
ChatArea
├─ ChannelIntro
├─ ChatMessageTile
└─ ChatInputBar
メンバー一覧は、次のように分けられます。
MemberPanel
├─ MemberSectionTitle
├─ MemberTile
└─ ProfileCard
プロフィール編集画面は、次のように分けられます。
ProfileEditorDialog
├─ UserAvatar
├─ DiscordTextField
├─ 色選択
├─ ステータス選択
└─ Save / Cancel ボタン
このように、複雑に見える画面でも、小さく分ければ作りやすくなります。
大きな画面
↓
領域に分ける
↓
小さなWidgetに分ける
↓
1つずつ作る
Flutterでは、この「小さく分ける力」がとても大切です。
4. StatelessWidgetとStatefulWidgetを使い分ける
この章では、StatelessWidget と StatefulWidget も使い分けました。
ざっくり言うと、次のように考えます。
| 種類 | 使う場面 |
|---|---|
StatelessWidget | 受け取ったデータを表示するだけ |
StatefulWidget | 中で状態が変わる |
たとえば、ProfileCard は、基本的に受け取った UserProfile を表示するだけです。
UserProfileを受け取る
↓
表示する
そのため、StatelessWidget で作れます。
一方、ProfileEditorDialog は、入力欄の内容や選択中の色が変わります。
名前を入力する
色を選ぶ
オンライン状態を選ぶ
このように、画面内で状態が変わる場合は、StatefulWidget を使います。
表示するだけ
↓
StatelessWidget
中で変わる
↓
StatefulWidget
この判断ができるようになると、Flutterの設計がかなり楽になります。
5. 状態を親Widgetで持つ
この章で特に重要だったのが、状態をどこで持つかです。
完成アプリでは、次のような状態を親Widgetで持ちました。
selectedServerIndex
selectedChannelIndex
currentUser
channelMessages
showMemberPanel
たとえば、currentUser は、いろいろな場所で使います。
ProfileCard
MemberPanel
ProfileEditorDialog
ChatMessage送信時
もしそれぞれのWidgetが別々に currentUser を持ってしまうと、情報がズレてしまいます。
そのため、親Widgetで1つだけ状態を持ち、子Widgetへ渡します。
親Widgetが状態を持つ
↓
子Widgetへ渡す
↓
子Widgetは表示する
↓
操作が起きたら親の関数を呼ぶ
↓
親が状態を更新する
これが、Flutterの基本的なデータの流れです。
6. ユーザー操作で状態を変える
この章では、ユーザー操作で状態を変える機能も作りました。
たとえば、メッセージ送信です。
入力欄に文字を書く
↓
Sendを押す
↓
ChatMessageを作る
↓
messagesに追加する
↓
setStateで画面を更新する
また、プロフィール編集も作りました。
編集Dialogを開く
↓
名前や画像URLを入力する
↓
Save Changesを押す
↓
新しいUserProfileを作る
↓
親Widgetに渡す
↓
setStateでcurrentUserを更新する
このように、アプリの操作は、基本的に次の流れで考えられます。
ユーザーが操作する
↓
データが変わる
↓
setStateで画面を更新する
これが、動くUIを作る基本です。
7. レスポンシブ対応を考える
この章の後半では、PC・タブレット・スマホで崩れないレイアウトを作りました。
PCでは、4カラム表示にしました。
ServerRail | ChannelSidebar | ChatArea | MemberPanel
タブレットでは、右側のメンバー欄を省略しました。
ServerRail | ChannelSidebar | ChatArea
スマホでは、チャット画面を中心にし、左右の情報はDrawerに収納しました。
ChatArea
+ drawer(ServerRail + ChannelSidebar)
+ endDrawer(MemberPanel)
ここで大切なのは、PCの画面をそのままスマホに押し込まなかったことです。
PC版をそのまま縮小
↓
見づらい
崩れる
操作しにくい
その代わりに、画面幅ごとに情報の優先順位を整理しました。
PC
↓
全部見せる
タブレット
↓
少し省略する
スマホ
↓
主役だけ見せる
補助情報は収納する
この考え方は、実務でもとても重要です。
8. 完成コードは役割ごとに読む
前回の 5-16 では、完成コードの読み方を整理しました。
完成コードは、上から1行ずつ理解しようとすると大変です。
そのため、役割ごとに分けて読みます。
アプリの入口
共通ルール
データclass
状態を持つ親Widget
レイアウト切り替え
PC用レイアウト
スマホ用レイアウト
左側パネル
中央チャット画面
右側メンバー欄
プロフィール編集Dialog
補助関数
このように見ると、長いコードも怖くなくなります。
大切なのは、コードを丸暗記することではありません。
どこが入口か
どこが状態を持つか
どのデータを扱うか
どのWidgetが何を担当するか
これが分かれば、コードは読めるようになります。
Discord風アプリで学んだことを他のアプリに応用する
ここからは、この章で学んだ考え方を、他のアプリにどう応用できるか考えます。
例1:ECアプリに応用する
ECアプリを作る場合も、同じ流れで考えられます。
まず画面を観察します。
商品カテゴリ
商品一覧
商品詳細
カート
ユーザープロフィール
購入ボタン
これをWidgetにすると、次のようになります。
CategorySidebar
ProductGrid
ProductCard
CartPanel
UserProfileCard
CheckoutButton
データclassは、次のように考えられます。
Product
Category
CartItem
UserProfile
Order
Discord風アプリでいう ChatMessage が、ECアプリでは Product や CartItem に変わるだけです。
Discord
ChatMessageを表示する
EC
Productを表示する
考え方は同じです。
データをclassにする
↓
Widgetに渡す
↓
一覧で表示する
↓
操作で状態を変える
例2:SNSアプリに応用する
SNSアプリも、同じ流れで作れます。
画面を観察すると、次のような領域があります。
投稿一覧
投稿カード
いいねボタン
コメント欄
プロフィール
投稿作成画面
Widgetにすると、次のようになります。
PostFeed
PostCard
LikeButton
CommentSheet
ProfileCard
PostEditorDialog
データclassは、次のようになります。
Post
Comment
UserProfile
LikeState
Discord風アプリで作った ChatInputBar は、SNSではコメント入力欄に応用できます。
ChatInputBar
↓
CommentInputBar
ProfileEditorDialog は、SNSのプロフィール編集画面にも応用できます。
ProfileEditorDialog
↓
SNSプロフィール編集画面
つまり、名前や見た目が変わるだけで、構造は似ています。
例3:業務管理アプリに応用する
業務管理アプリでも、同じ考え方が使えます。
たとえば、タスク管理アプリなら、画面は次のように分けられます。
プロジェクト一覧
タスクカテゴリ
タスク一覧
担当者一覧
タスク詳細
コメント欄
Discord風アプリとの対応は、次のように考えられます。
| Discord風アプリ | 業務管理アプリ |
|---|---|
ServerRail | プロジェクト一覧 |
ChannelSidebar | タスクカテゴリ |
ChatArea | タスク一覧・作業ログ |
MemberPanel | 担当者一覧 |
ProfileCard | 担当者プロフィール |
ChatInputBar | コメント入力欄 |
こう見ると、Discord風UIは、実は業務アプリの構造にもかなり近いです。
左で対象を選ぶ
中央で内容を見る
右で関連情報を見る
下で入力する
この型は、多くのアプリに応用できます。
例4:医療・クリニック向けアプリに応用する
医療向けのアプリでも、今回の考え方は使えます。
たとえば、クリニック内の患者説明アプリを作る場合を考えます。
画面を分けると、次のようになります。
診療カテゴリ一覧
説明動画一覧
説明本文
患者情報
同意書確認
質問入力欄
Widgetにすると、次のようになります。
CategorySidebar
VideoList
ExplanationArea
PatientPanel
ConsentCard
QuestionInputBar
データclassは、次のように考えられます。
MedicalCategory
ExplanationVideo
PatientProfile
ConsentItem
QuestionMessage
Discord風アプリで学んだ構造は、次のように置き換えられます。
| Discord風アプリ | 医療向け説明アプリ |
|---|---|
| チャンネル一覧 | 診療カテゴリ一覧 |
| チャット画面 | 説明本文・動画表示 |
| メッセージ入力 | 質問入力 |
| メンバー一覧 | 患者情報・スタッフ情報 |
| プロフィールカード | 患者プロフィールカード |
このように、UIの考え方は、業種が変わっても応用できます。
どんなアプリでも使える7つの質問
新しいアプリを作るときは、次の7つの質問を使うと整理しやすいです。
質問1:この画面にはどんな領域があるか
まず、画面を観察します。
左に何があるか
中央に何があるか
右に何があるか
上部に何があるか
下部に何があるか
Discord風アプリでは、次のように分けました。
左端:サーバー一覧
左側:チャンネル一覧
中央:チャット
右側:メンバー一覧
下部:入力欄
質問2:それぞれの領域は何のWidgetにできるか
領域をWidget名に変換します。
サーバー一覧
↓
ServerRail
チャンネル一覧
↓
ChannelSidebar
チャット画面
↓
ChatArea
この時点で、作るべき部品が見えてきます。
質問3:画面に表示するデータは何か
次に、データを考えます。
ユーザー情報
投稿情報
商品情報
予約情報
メッセージ情報
カテゴリ情報
これをclassとしてまとめます。
UserProfile
ChatMessage
Product
Reservation
Category
質問4:どのデータが変化するか
次に、状態を考えます。
選択中の項目
入力中の文字
ログイン中のユーザー
表示中の一覧
いいね状態
保存状態
変化するものは、どこかで状態として持つ必要があります。
変わらない
↓
constやfinalのデータ
変わる
↓
Stateで管理
質問5:その状態はどこで持つべきか
複数のWidgetで使う状態は、親Widgetで持つことが多いです。
複数の子で使う
↓
親で持つ
そのWidget内だけで使う
↓
そのWidget内で持つ
たとえば、Discord風アプリでは、currentUser は親で持ちました。
一方、入力欄の TextEditingController は、ChatArea や ProfileEditorDialog 内で持ちました。
質問6:ユーザー操作で何が変わるか
次に、操作を考えます。
ボタンを押す
入力する
選択する
保存する
送信する
Drawerを開く
そして、操作によって何の状態が変わるかを考えます。
Sendを押す
↓
messagesが増える
プロフィール保存
↓
currentUserが変わる
チャンネル選択
↓
selectedChannelIndexが変わる
質問7:スマホでは何を隠すか
最後に、レスポンシブ対応を考えます。
PCでは全部表示できる
↓
スマホでは全部は無理
↓
何を主役にするか
↓
何をDrawerや別画面に移すか
Discord風アプリでは、スマホの主役を ChatArea にしました。
スマホで常時表示
↓
ChatArea
必要なときだけ表示
↓
ServerRail
ChannelSidebar
MemberPanel
この判断が、スマホ対応ではとても重要です。
アプリ開発の共通テンプレート
この章で学んだ流れを、どんなアプリにも使えるテンプレートにすると、次のようになります。
アプリ名:
作りたい画面:
1. 画面を観察する
- 上部:
- 左側:
- 中央:
- 右側:
- 下部:
2. 必要なデータを考える
- データ1:
- データ2:
- データ3:
3. classを考える
- class名:
- property:
4. Widgetに分ける
- Widget1:
- Widget2:
- Widget3:
5. 状態を考える
- 変わる値:
- 親で持つ値:
- 子で持つ値:
6. 操作を考える
- 操作1:
- 操作2:
- 操作3:
7. レスポンシブ対応を考える
- PC:
- タブレット:
- スマホ:
新しいアプリを作るときは、いきなりコードを書く前に、このテンプレートを埋めるだけでもかなり整理できます。
FlutterでUIを作るときの基本姿勢
この章を通して、覚えてほしい基本姿勢があります。
それは、一気に完成させようとしない ことです。
完成形をいきなり作る
↓
難しい
詰まりやすい
エラーが増える
代わりに、小さく作ります。
まず色を決める
↓
小さな部品を作る
↓
データを渡す
↓
状態を追加する
↓
操作を追加する
↓
レスポンシブ対応する
このように積み上げると、複雑なアプリでも作りやすくなります。
初心者が次に意識するとよいこと
この章を終えた段階で、次に意識するとよいことは、次の5つです。
1. Widget名を自分でつける力
画面を見たときに、部品名を考える力です。
これは一覧だから ProductList
これはカードだから ProductCard
これは入力欄だから MessageInputBar
これは詳細欄だから DetailPanel
名前をつけられると、設計がかなり整理されます。
2. データclassを考える力
画面に表示されている情報を、classにまとめる力です。
商品
↓
Product
投稿
↓
Post
予約
↓
Reservation
患者情報
↓
PatientProfile
データが整理されると、Widgetも作りやすくなります。
3. 状態をどこで持つか考える力
Flutterでは、状態の置き場所が大切です。
そのWidgetだけで使う
↓
そのWidgetで持つ
複数のWidgetで使う
↓
親で持つ
この判断ができると、画面の動きが整理されます。
4. setStateの流れを理解する力
状態が変わったら、setState で画面を更新します。
状態を変える
↓
setState
↓
buildが再実行される
↓
画面が更新される
初心者のうちは、この流れを何度も確認するとよいです。
5. 画面幅で配置を変える力
PCとスマホでは、画面の使い方が違います。
PC
↓
横に広い
↓
情報を並べて表示できる
スマホ
↓
横に狭い
↓
情報を絞る・収納する
レスポンシブ対応では、単に縮小するのではなく、優先順位を決めることが重要です。
この章の到達点
この章を最後まで学ぶと、次のことができるようになります。
複雑な画面を部品に分解できる
データclassを考えられる
Listを使って一覧UIを作れる
TextFieldで入力を受け取れる
Dialogで編集画面を作れる
Drawerでスマホ対応できる
LayoutBuilderでレスポンシブ対応できる
完成コードを役割ごとに読める
これは、かなり大きな前進です。
「Discord風アプリを作った」というより、Flutterでアプリ画面を設計する流れを一通り経験した と考えるとよいです。
この章の学びを一言でまとめる
この章の学びを一言でまとめるなら、次のようになります。
アプリは、画面・データ・状態・操作・レスポンシブ設計を順番に整理すれば作れる。
最初は、UIが複雑に見えるかもしれません。
しかし、次のように分ければ、必ず小さくできます。
画面を見る
↓
領域に分ける
↓
Widgetにする
↓
データを作る
↓
状態を持つ
↓
操作で変える
↓
画面幅に合わせる
この流れを何度も繰り返すことで、別のアプリも作れるようになります。
手を動かす最終演習1:別アプリに置き換える
Discord風アプリの部品を、別のアプリに置き換えてみましょう。
たとえば、タスク管理アプリなら次のように置き換えられます。
| Discord風アプリ | タスク管理アプリ |
|---|---|
ServerRail | プロジェクト一覧 |
ChannelSidebar | タスクカテゴリ一覧 |
ChatArea | タスク一覧・作業ログ |
MemberPanel | 担当者一覧 |
ProfileCard | 担当者プロフィール |
ChatInputBar | コメント入力欄 |
このように置き換えるだけで、Discord風UIの構造が、別アプリの設計に変わります。
手を動かす最終演習2:自分のアプリ案を分解する
次のテンプレートを使って、自分の作りたいアプリを分解してみましょう。
作りたいアプリ:
画面の領域:
1.
2.
3.
4.
必要なデータ:
1.
2.
3.
作るWidget:
1.
2.
3.
4.
変化する状態:
1.
2.
3.
ユーザー操作:
1.
2.
3.
スマホ対応:
PCでは:
スマホでは:
このテンプレートを埋められれば、かなり設計が進んでいます。
手を動かす最終演習3:1つの部品だけ作ってみる
いきなり全部を作ろうとせず、まずは1つだけ作ってみてください。
たとえば、ECアプリなら ProductCard だけ。
SNSアプリなら PostCard だけ。
予約アプリなら ReservationCard だけ。
1つのカードを作る
↓
データclassを作る
↓
Listで複数表示する
↓
タップや編集を追加する
この小さな積み上げが、アプリ開発の基本です。
よくある不安1:全部覚えられない
全部覚える必要はありません。
大切なのは、思い出せる型を持つことです。
画面を分ける
データを作る
Widgetを作る
状態を持つ
操作で変える
レスポンシブ対応する
この型があれば、細かい書き方は調べながら進められます。
よくある不安2:コードが長くなると分からなくなる
コードが長くなったら、役割ごとに見出しをつけて考えます。
データclass
画面全体
左側パネル
中央コンテンツ
右側パネル
編集画面
補助関数
長いコードでも、役割に分ければ読めます。
よくある不安3:デザインを再現できるか不安
最初から完全再現を目指さなくて大丈夫です。
順番は次の通りです。
構造を作る
↓
余白を整える
↓
色を近づける
↓
角丸や影を整える
↓
アニメーションを足す
いきなり完璧な見た目を作ろうとすると難しくなります。
まずは構造です。
よくある不安4:スマホ対応が難しい
スマホ対応は、まず「何を隠すか」を決めると簡単になります。
全部表示しようとしない
↓
一番大事な画面を残す
↓
補助情報はDrawerや別画面へ移す
今回のDiscord風アプリでは、スマホの主役を ChatArea にしました。
このように、優先順位を決めることが重要です。
よくある不安5:自分でアプリを設計できる気がしない
最初は、既存アプリを観察するところからで大丈夫です。
既存アプリを見る
↓
画面を分解する
↓
Widget名をつける
↓
データclassを考える
↓
小さく再現する
今回のDiscord風アプリも、まさにこの流れでした。
この節の確認問題
確認問題1
この章で最初に行った大切な作業は何ですか。
答え
Discord風アプリの画面を観察し、領域に分けることです。
確認問題2
画面に表示する情報を整理するために使ったものは何ですか。
答え
データclassです。例として、UserProfile、ChatMessage、DiscordChannel などがあります。
確認問題3
複雑な画面を作りやすくするために行ったことは何ですか。
答え
画面を小さなWidgetに分けました。
確認問題4
ユーザー操作で画面を更新するために使ったものは何ですか。
答え
setState です。
確認問題5
スマホでサーバー一覧やチャンネル一覧を収納するために使ったものは何ですか。
答え
Drawer です。
確認問題6
PC・タブレット・スマホで表示を切り替えるために使ったものは何ですか。
答え
LayoutBuilder です。
確認問題7
この章で学んだ流れは、Discord風アプリ以外にも使えますか。
答え
はい。ECアプリ、SNSアプリ、業務管理アプリ、医療向けアプリなどにも応用できます。
この章のまとめ
この章では、Discord風チャットアプリを題材に、FlutterのUI構築を学びました。
作ったものはDiscord風アプリですが、本当に学んだことは、もっと広いアプリ開発の考え方です。
画面を観察する
↓
必要なデータを考える
↓
画面を部品に分ける
↓
部品をWidgetとして作る
↓
状態を持たせる
↓
ユーザー操作で状態を変える
↓
必要な機能を少しずつ追加する
↓
画面幅に応じてレイアウトを変える
この流れを理解できれば、次に別のアプリを作るときも、同じように考えられます。
どんなアプリでも
↓
画面がある
データがある
部品がある
状態がある
操作がある
画面幅への対応がある
つまり、アプリの種類が変わっても、基本の考え方は変わりません。
最後に、この章で一番覚えておきたい言葉を置いておきます。
複雑なアプリは、いきなり作るのではなく、小さな部品と状態の流れに分ければ作れる。
この考え方が身につけば、Flutterで作れるものは大きく広がっていきます。