
YouTube風ミニアプリ用のクラスを設計する
この節で学ぶこと
前回の 3-7 では、List<Video> を使って、動画一覧を操作する方法を学びました。
たとえば、次のような操作です。
動画を1件ずつ取り出す
人気動画だけ取り出す
Dartカテゴリだけ取り出す
ライブ動画だけ取り出す
タイトルに特定の文字を含む動画を検索する
再生回数が多い順に並び替える
ここまでで、YouTube風ミニアプリの「動画一覧データ」をかなり扱えるようになってきました。
今回の 3-8 では、さらに一歩進んで、YouTube風ミニアプリに必要なデータを自分で設計する練習をします。
これまで中心に扱ってきたのは Video classでした。
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
required this.publishedAt,
required this.duration,
required this.category,
required this.isLive,
});
final String title;
final String channelName;
final int views;
final String publishedAt;
final String duration;
final String category;
final bool isLive;
}
しかし、実際のアプリでは、動画だけでなく、チャンネルやカテゴリもデータとして考えることができます。
動画データ
チャンネルデータ
カテゴリデータ
この節では、Video、Channel、Category のようなclassを考えながら、アプリ全体のデータ設計に近づいていきます。
この節のゴール
この節のゴールは、YouTube風ミニアプリに必要な情報を見つけ、それをclassとして設計できるようになることです。
最終的には、次のような考え方ができる状態を目指します。
画面を見る
↓
表示されている情報を見つける
↓
どの情報をデータとして持つべきか考える
↓
classにする
↓
Listとして複数持つ
↓
UIに渡す
この節で大切なのは、次の一文です。
クラス設計とは、アプリに登場する情報を、使いやすいデータの形に整理することである。
まず「設計」とは何か
プログラミング初心者にとって、「設計」という言葉は少し難しく感じるかもしれません。
でも、考え方はとてもシンプルです。
何を作りたいのかを考える。
そのために、どんなデータが必要かを考える。
そのデータを、どんな形で持つかを決める。
たとえば、YouTube風アプリを作りたいとします。
画面には、次のような情報が表示されます。
動画タイトル
チャンネル名
再生回数
投稿日
動画時間
サムネイル
チャンネルアイコン
カテゴリ
ライブ中かどうか
これらを何も考えずにバラバラの変数で持つと、すぐに管理が大変になります。
そこで、意味のある単位ごとにまとめます。
動画1本分の情報 → Video class
チャンネルの情報 → Channel class
カテゴリの情報 → Category class
これが、クラス設計の入口です。
画面からデータを見つける
YouTube風の動画カードを想像してください。
┌──────────────────────────┐
│ サムネイル画像 │
│ 12:34 │
└──────────────────────────┘
● FlutterでYouTube風UIを作る
Code Studio
12.8万回視聴・2日前
この画面から、データを見つけます。
| 画面に見えるもの | データとして考えるもの |
|---|---|
| FlutterでYouTube風UIを作る | 動画タイトル |
| Code Studio | チャンネル名 |
| 12.8万回視聴 | 再生回数 |
| 2日前 | 投稿日 |
| 12:34 | 動画時間 |
| サムネイル | サムネイル色や画像 |
| ● チャンネルアイコン | チャンネルアイコン色 |
| ライブ配信中 | ライブ状態 |
このように、画面に表示されるものの多くは、裏側のデータから作られています。
まずはVideo classを設計する
動画カードの中心になるのは、やはり Video classです。
動画1本分に必要な情報を整理すると、次のようになります。
| 必要な情報 | property名 | 型 |
|---|---|---|
| 動画タイトル | title | String |
| チャンネル名 | channelName | String |
| 再生回数 | views | int |
| 投稿日 | publishedAt | String |
| 動画時間 | duration | String |
| カテゴリ | category | String |
| ライブ中かどうか | isLive | bool |
これをclassにすると、次のようになります。
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
required this.publishedAt,
required this.duration,
required this.category,
required this.isLive,
});
final String title;
final String channelName;
final int views;
final String publishedAt;
final String duration;
final String category;
final bool isLive;
}
これは、これまで作ってきた Video classとほぼ同じです。
このclassがあることで、動画1本分の情報を安全にまとめて扱えます。
Video classだけで足りるのか
ここで、少し考えてみます。
YouTube風アプリを作るだけなら、Video classだけでもある程度は作れます。
Video(
title: 'FlutterでYouTube風UIを作る',
channelName: 'Code Studio',
views: 128000,
publishedAt: '2日前',
duration: '12:34',
category: 'Flutter',
isLive: false,
)
しかし、もしチャンネルの情報をもっと詳しく扱いたくなったらどうでしょうか。
たとえば、次のような情報です。
チャンネル名
チャンネルアイコン色
登録者数
チャンネル説明
公式チャンネルかどうか
このような情報が増えてくると、Video classの中に全部入れるより、Channel classとして分けたほうが整理しやすくなります。
Video:
動画そのものの情報
Channel:
チャンネルそのものの情報
Channel classを考える
チャンネル情報をclassにするなら、たとえば次のように考えられます。
| 必要な情報 | property名 | 型 |
|---|---|---|
| チャンネル名 | name | String |
| アイコン色 | avatarColorName | String |
| 登録者数 | subscribers | int |
| 公式チャンネルかどうか | isOfficial | bool |
まず、Dartだけで試しやすいように、Color は使わず、色名を文字で持つ形にします。
class Channel {
const Channel({
required this.name,
required this.avatarColorName,
required this.subscribers,
required this.isOfficial,
});
final String name;
final String avatarColorName;
final int subscribers;
final bool isOfficial;
}
この Channel classは、チャンネル1つ分の情報を表します。
たとえば、次のように使えます。
final channel = Channel(
name: 'Code Studio',
avatarColorName: 'red',
subscribers: 24000,
isOfficial: true,
);
Channel classを使うメリット
チャンネル情報を Channel classに分けると、次のようなメリットがあります。
| 分ける前 | 分けた後 |
|---|---|
| Videoの中にチャンネル情報が増え続ける | Channelとして整理できる |
| チャンネル名だけしか扱いにくい | 登録者数や公式判定も持てる |
| 同じチャンネル情報を何度も書く | Channelデータとして再利用しやすい |
| 動画とチャンネルの役割が混ざる | 役割が分かれる |
たとえば、同じチャンネルが複数の動画を投稿している場合、チャンネル情報を別で持つと整理しやすくなります。
Code Studio
├─ FlutterでYouTube風UIを作る
├─ Flutterレイアウト入門
└─ ListViewの使い方
このように、チャンネルと動画は関係しています。
しかし、同じものではありません。
Videoの中にChannelを持たせる
Video classの中で、チャンネル名を String として持つ代わりに、Channel を持たせることもできます。
これまでの形です。
class Video {
const Video({
required this.title,
required this.channelName,
});
final String title;
final String channelName;
}
Channel classを使う形です。
class Video {
const Video({
required this.title,
required this.channel,
});
final String title;
final Channel channel;
}
この場合、Video は Channel データを持つことになります。
Video
├─ title
└─ channel
├─ name
├─ avatarColorName
├─ subscribers
└─ isOfficial
このように、classの中に別のclassを持たせることもできます。
Channelを持つVideoをDartPadで試す
void main() {
final channel = Channel(
name: 'Code Studio',
avatarColorName: 'red',
subscribers: 24000,
isOfficial: true,
);
final video = Video(
title: 'FlutterでYouTube風UIを作る',
channel: channel,
views: 128000,
);
print(video.title);
print(video.channel.name);
print(video.channel.subscribers);
}
class Video {
const Video({
required this.title,
required this.channel,
required this.views,
});
final String title;
final Channel channel;
final int views;
}
class Channel {
const Channel({
required this.name,
required this.avatarColorName,
required this.subscribers,
required this.isOfficial,
});
final String name;
final String avatarColorName;
final int subscribers;
final bool isOfficial;
}
出力結果です。
FlutterでYouTube風UIを作る
Code Studio
24000
ここで注目するのは、次の書き方です。
video.channel.name
これは、次のように読めます。
videoの中にあるchannelの中にあるnameを取り出す。
少し長く見えますが、意味はそのままです。
video.channel.nameの読み方
video.channel.name は、3段階で読みます。
| コード | 意味 |
|---|---|
video | 動画1本分のデータ |
video.channel | その動画のチャンネルデータ |
video.channel.name | そのチャンネルの名前 |
図にすると、次のようになります。
video
├─ title: FlutterでYouTube風UIを作る
├─ views: 128000
└─ channel
├─ name: Code Studio
├─ avatarColorName: red
├─ subscribers: 24000
└─ isOfficial: true
Video の中に Channel が入っているため、このように取り出します。
ただし初心者のうちはシンプルでもよい
ここで大切なことがあります。
実際の完成アプリでは、学習の分かりやすさを優先して、Video classの中に channelName と channelColor を直接持たせています。
final String channelName;
final Color channelColor;
これは、初心者には分かりやすい形です。
video.channelName
video.channelColor
一方、Channel classに分けると、より設計らしくなります。
video.channel.name
video.channel.avatarColor
どちらが絶対に正しいということではありません。
学習段階では、まずシンプルな Video classで理解する。
慣れてきたら、Channel classに分ける。
この順番で大丈夫です。
| 設計 | メリット | 向いている場面 |
|---|---|---|
Video に channelName を直接持つ | 初心者に分かりやすい | 小さなモックアプリ |
Channel classを分ける | 拡張しやすい | 本格的なアプリ設計 |
Category classを考える
次に、カテゴリをclassとして考えます。
YouTube風アプリには、上部にカテゴリバーがあります。
すべて
Flutter
Dart
UI
ライブ
最近アップロード
視聴済み
このカテゴリも、ただの文字列Listとして持つことができます。
final categories = [
'すべて',
'Flutter',
'Dart',
'UI',
'ライブ',
];
これでも十分です。
しかし、カテゴリに追加情報を持たせたい場合は、Category classにできます。
たとえば、次のような情報です。
| 必要な情報 | property名 | 型 |
|---|---|---|
| 表示名 | label | String |
| 選択中かどうか | isSelected | bool |
| 並び順 | order | int |
これをclassにすると、次のようになります。
class Category {
const Category({
required this.label,
required this.isSelected,
required this.order,
});
final String label;
final bool isSelected;
final int order;
}
CategoryをDartPadで試す
void main() {
final categories = [
Category(
label: 'すべて',
isSelected: true,
order: 0,
),
Category(
label: 'Flutter',
isSelected: false,
order: 1,
),
Category(
label: 'Dart',
isSelected: false,
order: 2,
),
];
for (final category in categories) {
print('${category.order}: ${category.label}');
}
}
class Category {
const Category({
required this.label,
required this.isSelected,
required this.order,
});
final String label;
final bool isSelected;
final int order;
}
出力結果です。
0: すべて
1: Flutter
2: Dart
このように、カテゴリもclassとして設計できます。
StringのListとCategory classの違い
カテゴリを文字だけで持つ場合です。
final categories = [
'すべて',
'Flutter',
'Dart',
];
カテゴリをclassで持つ場合です。
final categories = [
Category(label: 'すべて', isSelected: true, order: 0),
Category(label: 'Flutter', isSelected: false, order: 1),
Category(label: 'Dart', isSelected: false, order: 2),
];
違いを整理します。
| 比較 | StringのList | Category class |
|---|---|---|
| 書きやすさ | 簡単 | 少し長い |
| 表示名 | 持てる | 持てる |
| 選択状態 | 別で管理が必要 | isSelected として持てる |
| 並び順 | Listの順番で分かる | order として明示できる |
| 拡張性 | 低め | 高め |
初心者のうちは、まずStringのListで十分です。
ただし、「カテゴリにも情報を持たせたい」と思ったら、Category classにできます。
ここまでのまとめ
前半では、YouTube風ミニアプリに出てくる情報を、classとして設計する考え方を学びました。
大切なポイントは、次の通りです。
- クラス設計とは、アプリに登場する情報を使いやすい形に整理すること
- 動画1本分の情報はVideo classにできる
- チャンネル情報はChannel classにできる
- カテゴリ情報はCategory classにできる
- classの中に別のclassを持たせることもできる
- 小さなアプリではシンプルな設計でよい
- 本格的にするなら、役割ごとにclassを分ける
前半で特に大切なこと
画面に表示されている情報を見つけ、意味のある単位ごとにclassへ整理する。
小さなアプリと本格的なアプリで設計は変わる
前半では、YouTube風ミニアプリに登場する情報を、Video、Channel、Category のようなclassとして整理する考え方を学びました。
ここで大切なのは、最初からすべてを細かく分けすぎなくてもよい、ということです。
小さな学習用アプリでは、次のように Video classだけで作るほうが分かりやすい場合があります。
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
required this.publishedAt,
required this.duration,
required this.category,
required this.isLive,
});
final String title;
final String channelName;
final int views;
final String publishedAt;
final String duration;
final String category;
final bool isLive;
}
一方、本格的なアプリに近づけるなら、チャンネル情報やカテゴリ情報を別classに分ける設計も考えられます。
Video
├─ title
├─ views
├─ duration
├─ channel
└─ category
このように、アプリの規模や目的によって、設計の細かさは変わります。
設計は「正解を当てる」ものではない
プログラミング初心者は、class設計を学ぶときに、次のように考えてしまうことがあります。
正しいclass設計を一発で作らなければいけない。
しかし、実際にはそうではありません。
設計は、作りながら見直していくものです。
最初はシンプルに作り、必要になったら分ける。
これで大丈夫です。
たとえば、最初は次のように作ります。
final String channelName;
あとから、チャンネル情報が増えてきたら、Channel classに分けます。
final Channel channel;
つまり、設計は一度決めたら終わりではありません。
アプリの成長に合わせて、少しずつ整理していくものです。
設計の判断基準
classを分けるべきか迷ったときは、次のように考えると分かりやすいです。
| 判断すること | classを分けなくてもよい場合 | classを分けたほうがよい場合 |
|---|---|---|
| 情報量 | 情報が少ない | 情報が多い |
| 再利用 | 1か所でしか使わない | 複数の場所で使う |
| 意味のまとまり | ただの補足情報 | 独立した存在として扱いたい |
| 変更の可能性 | ほとんど変わらない | 今後増えそう |
| 初心者の理解 | シンプルにしたい | 設計練習をしたい |
たとえば、チャンネル名だけなら、Video に channelName を持たせても十分です。
final String channelName;
しかし、チャンネル名、アイコン、登録者数、公式判定、説明文などを扱いたくなったら、Channel classに分けたほうが自然です。
final Channel channel;
今回のYouTube風ミニアプリのおすすめ設計
今回の教材では、初心者がFlutter UIに進みやすいように、完成アプリでは次のようなシンプルな設計を採用します。
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
required this.publishedAt,
required this.duration,
required this.category,
required this.thumbnailColor,
required this.channelColor,
required this.isLive,
});
final String title;
final String channelName;
final int views;
final String publishedAt;
final String duration;
final String category;
final Color thumbnailColor;
final Color channelColor;
final bool isLive;
String get viewLabel {
if (views >= 100000000) {
return '${(views / 100000000).toStringAsFixed(1)}億回視聴';
}
if (views >= 10000) {
return '${(views / 10000).toStringAsFixed(1)}万回視聴';
}
return '$views回視聴';
}
String get metaLabel {
if (isLive) {
return 'ライブ配信中';
}
return '$viewLabel・$publishedAt';
}
}
この設計では、Video classの中に、動画カードを表示するために必要な情報をまとめています。
Video
├─ 動画の情報
│ ├─ title
│ ├─ views
│ ├─ publishedAt
│ └─ duration
│
├─ チャンネルの簡単な情報
│ ├─ channelName
│ └─ channelColor
│
├─ カテゴリ情報
│ └─ category
│
├─ サムネイル情報
│ └─ thumbnailColor
│
└─ 表示用のふるまい
├─ viewLabel
└─ metaLabel
小さなミニアプリとしては、このくらいの設計が分かりやすいです。
なぜ完成アプリではChannel classを分けないのか
前半では、Channel classを作る例を見ました。
class Channel {
const Channel({
required this.name,
required this.avatarColorName,
required this.subscribers,
required this.isOfficial,
});
final String name;
final String avatarColorName;
final int subscribers;
final bool isOfficial;
}
これは、設計としては良い考え方です。
ただし、今回の完成アプリでは、チャンネルの詳しいページや登録者数の表示までは作りません。
必要なのは、動画カードに表示するチャンネル名とアイコン色です。
そのため、次のように Video に直接持たせています。
final String channelName;
final Color channelColor;
このほうが、初心者には分かりやすくなります。
video.channelName
video.channelColor
もし Channel classに分けると、次のようになります。
video.channel.name
video.channel.avatarColor
こちらは本格的ですが、少し複雑になります。
今回の目標は、YouTube風の動画一覧UIを作ることなので、まずはシンプルな設計で進めます。
Categoryも最初はStringで十分
カテゴリも同じです。
本格的には、Category classにできます。
class Category {
const Category({
required this.label,
required this.isSelected,
required this.order,
});
final String label;
final bool isSelected;
final int order;
}
ただし、今回の完成アプリでは、カテゴリバーに文字を並べるだけで十分です。
そのため、カテゴリは次のような List<String> で扱えます。
const categories = [
'すべて',
'Flutter',
'Dart',
'UI',
'ライブ',
'最近アップロード',
'視聴済み',
];
この形なら、初心者でも理解しやすいです。
Category classを使うのは、たとえば次のような機能を追加したくなったときで大丈夫です。
選択中カテゴリをデータとして持ちたい
カテゴリごとに色を変えたい
カテゴリごとにアイコンを持たせたい
カテゴリの並び順を管理したい
設計を段階的に成長させる
アプリの設計は、段階的に成長させることができます。
第1段階:文字列だけで持つ
final String channelName;
final String category;
初心者には分かりやすいです。
小さなアプリなら、これで十分です。
第2段階:classとして分ける
final Channel channel;
final Category category;
情報が増えてきたら、classに分けます。
第3段階:データ同士の関係を設計する
Channel
├─ 複数のVideoを持つ
Category
├─ 複数のVideoを分類する
本格的なアプリでは、データ同士の関係も考えます。
ただし、今は第1段階からで十分です。
完成アプリに近いデータ設計
ここで、完成アプリに近いデータの形を見てみます。
const videos = [
Video(
title: 'FlutterでYouTube風UIを作る|ListViewとCardの実践入門',
channelName: 'Code Studio',
views: 128000,
publishedAt: '2日前',
duration: '12:34',
category: 'Flutter',
thumbnailColor: Color(0xFF121212),
channelColor: Color(0xFFE53935),
isLive: false,
),
Video(
title: 'DartのListとMapをアプリ画面で使う方法をやさしく解説',
channelName: 'App School',
views: 8500,
publishedAt: '5日前',
duration: '08:21',
category: 'Dart',
thumbnailColor: Color(0xFF1E3A8A),
channelColor: Color(0xFF1565C0),
isLive: false,
),
Video(
title: 'ライブ:Flutter質問会|Widget・ListView・classの疑問を解決',
channelName: 'Mobile Dev Live',
views: 5600,
publishedAt: '現在',
duration: 'LIVE',
category: 'LIVE',
thumbnailColor: Color(0xFFE62117),
channelColor: Color(0xFFD32F2F),
isLive: true,
),
];
この videos は、Video classのListです。
List<Video>
├─ Video 1本目
├─ Video 2本目
└─ Video 3本目
完成アプリでは、この videos をもとに、動画カードを作ります。
VideoCard(video: videos[index])
つまり、データ設計がそのままUI設計につながっています。
データ設計からUIを考える
class設計は、ただデータをきれいにするためだけではありません。
UIを作りやすくするためにも重要です。
たとえば、動画カードを作る VideoCard には、Video を1つ渡します。
VideoCard(video: video)
VideoCard の中では、次のように分解して表示できます。
VideoCard
├─ Thumbnail
│ ├─ video.thumbnailColor
│ ├─ video.category
│ └─ video.duration
│
├─ ChannelAvatar
│ └─ video.channelColor
│
└─ VideoInfo
├─ video.title
├─ video.channelName
└─ video.metaLabel
このように、Video classのpropertyが、UI部品にそのまま渡されます。
UI部品とVideo propertyの対応表
完成アプリで、どのpropertyがどのUIに使われるかを整理します。
| Videoのproperty / getter | 使われるUI | 表示内容 |
|---|---|---|
thumbnailColor | Thumbnail | サムネイル背景色 |
category | Thumbnail | サムネイル中央のカテゴリ文字 |
duration | Thumbnail | 右下の動画時間 |
channelColor | ChannelAvatar | チャンネルアイコンの色 |
title | VideoInfo | 動画タイトル |
channelName | VideoInfo | チャンネル名 |
viewLabel | metaLabelの中 | 再生回数表示 |
publishedAt | metaLabelの中 | 投稿日 |
isLive | metaLabel / 表示スタイル | ライブ表示の切り替え |
metaLabel | VideoInfo | 補足情報 |
この表を見ると、Video classが動画カードUIの材料になっていることが分かります。
設計演習1:画面から必要なデータを抜き出す
次の動画カードを作るとします。
┌──────────────────────────┐
│ DART │
│ 08:21 │
└──────────────────────────┘
● DartのListとMapをアプリ画面で使う方法
App School
8500回視聴・5日前
この画面から必要なデータを抜き出してください。
解答例
| 画面に見えるもの | property名 | 値 |
|---|---|---|
| DART | category | Dart |
| 08:21 | duration | 08:21 |
| DartのListとMapをアプリ画面で使う方法 | title | DartのListとMapをアプリ画面で使う方法 |
| App School | channelName | App School |
| 8500回視聴 | views | 8500 |
| 5日前 | publishedAt | 5日前 |
| 通常動画 | isLive | false |
画面に表示される情報を見れば、必要なpropertyが見えてきます。
設計演習2:Videoデータにする
先ほどの情報を Video データとして書いてみます。
Video(
title: 'DartのListとMapをアプリ画面で使う方法',
channelName: 'App School',
views: 8500,
publishedAt: '5日前',
duration: '08:21',
category: 'Dart',
thumbnailColor: Color(0xFF1E3A8A),
channelColor: Color(0xFF1565C0),
isLive: false,
)
このように、画面から抜き出した情報を、classのpropertyに当てはめます。
画面で見える情報
↓
propertyとして整理
↓
Video(...)として作る
これが、データ設計の実践です。
設計演習3:ライブ動画を設計する
次に、ライブ動画を考えます。
┌──────────────────────────┐
│ LIVE │
│ LIVE │
└──────────────────────────┘
● ライブ:Flutter質問会
Mobile Dev Live
ライブ配信中
この場合、必要なデータは次のようになります。
| 画面に見えるもの | property名 | 値 |
|---|---|---|
| LIVE | category | LIVE |
| LIVE | duration | LIVE |
| ライブ:Flutter質問会 | title | ライブ:Flutter質問会 |
| Mobile Dev Live | channelName | Mobile Dev Live |
| ライブ配信中 | isLive | true |
| 投稿日 | publishedAt | 現在 |
| 再生回数 | views | 5600 |
isLive が true の場合、metaLabel は ライブ配信中 になります。
String get metaLabel {
if (isLive) {
return 'ライブ配信中';
}
return '$viewLabel・$publishedAt';
}
つまり、画面に ライブ配信中 と直接持つのではなく、isLive というデータから表示を作ります。
ライブ動画をVideoデータにする
Video(
title: 'ライブ:Flutter質問会|Widget・ListView・classの疑問を解決',
channelName: 'Mobile Dev Live',
views: 5600,
publishedAt: '現在',
duration: 'LIVE',
category: 'LIVE',
thumbnailColor: Color(0xFFE62117),
channelColor: Color(0xFFD32F2F),
isLive: true,
)
このデータでは、isLive が true になっています。
そのため、UI側で video.metaLabel を使うと、次のように表示されます。
ライブ配信中
同じ Video classでも、isLive の値によって表示が変わります。
これが、データ設計と表示ルールを組み合わせる考え方です。
設計演習4:カテゴリバーを設計する
次に、カテゴリバーを考えます。
完成アプリでは、カテゴリを文字列のListで持つことができます。
const categories = [
'すべて',
'Flutter',
'Dart',
'UI',
'ライブ',
'最近アップロード',
'視聴済み',
];
この categories は、カテゴリバーに表示する文字の一覧です。
List<String>
├─ すべて
├─ Flutter
├─ Dart
├─ UI
├─ ライブ
├─ 最近アップロード
└─ 視聴済み
カテゴリバーのUIでは、これを1件ずつ取り出して表示します。
for (final category in categories) {
print(category);
}
出力結果です。
すべて
Flutter
Dart
UI
ライブ
最近アップロード
視聴済み
まずは、このようなシンプルな設計で十分です。
Category classにするならどうなるか
もしカテゴリにも選択状態を持たせたいなら、Category classを使えます。
class Category {
const Category({
required this.label,
required this.isSelected,
});
final String label;
final bool isSelected;
}
データは次のようになります。
final categories = [
Category(label: 'すべて', isSelected: true),
Category(label: 'Flutter', isSelected: false),
Category(label: 'Dart', isSelected: false),
Category(label: 'UI', isSelected: false),
];
この場合、UI側では isSelected を見て、選択中の見た目を変えることができます。
isSelectedがtrue
↓
黒背景・白文字
isSelectedがfalse
↓
グレー背景・黒文字
ただし、今回の完成アプリでは、まず List<String> で十分です。
設計演習5:Channel classにするならどうなるか
もしチャンネル情報を独立させるなら、次のように設計できます。
class Channel {
const Channel({
required this.name,
required this.avatarColor,
required this.subscribers,
required this.isOfficial,
});
final String name;
final Color avatarColor;
final int subscribers;
final bool isOfficial;
}
そして、Video は Channel を持ちます。
class Video {
const Video({
required this.title,
required this.channel,
required this.views,
});
final String title;
final Channel channel;
final int views;
}
データは次のようになります。
final codeStudio = Channel(
name: 'Code Studio',
avatarColor: Color(0xFFE53935),
subscribers: 24000,
isOfficial: true,
);
final video = Video(
title: 'FlutterでYouTube風UIを作る',
channel: codeStudio,
views: 128000,
);
この場合、チャンネル名は次のように取り出します。
video.channel.name
これは本格的な設計ですが、初心者には少し複雑になります。
今回の完成アプリでは、まず channelName と channelColor を Video に直接持たせます。
初心者におすすめの設計手順
アプリのclass設計に迷ったら、次の順番で考えると分かりやすいです。
1. 画面に表示されているものを書き出す
2. その情報が何の情報か分類する
3. 1つのまとまりにできるものを探す
4. まずはシンプルなclassにする
5. 情報が増えたらclassを分ける
YouTube風アプリなら、まずは動画カードを見ます。
動画タイトル
チャンネル名
再生回数
投稿日
動画時間
カテゴリ
ライブ状態
これらを Video classにまとめます。
慣れてきたら、Channel や Category に分けます。
設計で大切な考え方
class設計で大切なのは、難しい名前をつけることではありません。
大切なのは、役割を分けることです。
Video:
動画1本分の情報
Channel:
チャンネルの情報
Category:
分類の情報
名前を見たときに、何を表しているのか分かることが重要です。
悪い例です。
class Data {
final String name;
}
何のデータか分かりにくいです。
よい例です。
class Video {
final String title;
}
動画のデータであることが分かります。
class Channel {
final String name;
}
チャンネルのデータであることが分かります。
名前の付け方も設計の一部
class名やproperty名は、コードの読みやすさに大きく関わります。
| 表したいもの | よい名前 | 避けたい名前 |
|---|---|---|
| 動画 | Video | Data |
| チャンネル | Channel | Info |
| カテゴリ | Category | Item |
| 動画タイトル | title | text1 |
| チャンネル名 | channelName | name2 |
| 再生回数 | views | num |
| ライブ中かどうか | isLive | flag |
isLive のように、is から始まる名前は、true / false の値でよく使います。
final bool isLive;
これは、読んだときに「ライブ中かどうか」だと分かりやすいです。
完成アプリに向けた最終設計
この章の完成アプリでは、次のような設計で進めます。
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
required this.publishedAt,
required this.duration,
required this.category,
required this.thumbnailColor,
required this.channelColor,
required this.isLive,
});
final String title;
final String channelName;
final int views;
final String publishedAt;
final String duration;
final String category;
final Color thumbnailColor;
final Color channelColor;
final bool isLive;
String get viewLabel {
if (views >= 100000000) {
return '${(views / 100000000).toStringAsFixed(1)}億回視聴';
}
if (views >= 10000) {
return '${(views / 10000).toStringAsFixed(1)}万回視聴';
}
return '$views回視聴';
}
String get metaLabel {
if (isLive) {
return 'ライブ配信中';
}
return '$viewLabel・$publishedAt';
}
}
カテゴリは、まず文字列のListで持ちます。
const categories = [
'すべて',
'Flutter',
'Dart',
'UI',
'ライブ',
'最近アップロード',
'視聴済み',
];
動画一覧は、List<Video> で持ちます。
const videos = [
Video(
title: 'FlutterでYouTube風UIを作る|ListViewとCardの実践入門',
channelName: 'Code Studio',
views: 128000,
publishedAt: '2日前',
duration: '12:34',
category: 'Flutter',
thumbnailColor: Color(0xFF121212),
channelColor: Color(0xFFE53935),
isLive: false,
),
];
これで、YouTube風ミニアプリのデータ設計の土台ができます。
手を動かす練習1:画面からpropertyを考える
次の表示をもとに、必要なpropertyを考えてください。
Flutterレイアウト入門
Code Studio
3.2万回視聴・1週間前
10:05
解答例
| 表示 | property名 | 型 |
|---|---|---|
| Flutterレイアウト入門 | title | String |
| Code Studio | channelName | String |
| 32000 | views | int |
| 1週間前 | publishedAt | String |
| 10:05 | duration | String |
| Flutter | category | String |
| 通常動画 | isLive | bool |
手を動かす練習2:Videoデータを書く
練習1の情報をもとに、Video データを書いてください。
解答例
Video(
title: 'Flutterレイアウト入門',
channelName: 'Code Studio',
views: 32000,
publishedAt: '1週間前',
duration: '10:05',
category: 'Flutter',
thumbnailColor: Color(0xFF121212),
channelColor: Color(0xFFE53935),
isLive: false,
)
手を動かす練習3:Channel classを設計する
チャンネルに次の情報を持たせたいとします。
チャンネル名
登録者数
公式チャンネルかどうか
Channel classを書いてください。
解答例
class Channel {
const Channel({
required this.name,
required this.subscribers,
required this.isOfficial,
});
final String name;
final int subscribers;
final bool isOfficial;
}
手を動かす練習4:Category classを設計する
カテゴリに次の情報を持たせたいとします。
表示名
選択中かどうか
Category classを書いてください。
解答例
class Category {
const Category({
required this.label,
required this.isSelected,
});
final String label;
final bool isSelected;
}
手を動かす練習5:classを分けるべきか考える
次のうち、Channel classに分けたほうがよさそうな場面はどれですか。
A. チャンネル名だけを表示したい
B. チャンネル名、登録者数、公式マーク、説明文を表示したい
C. 学習用の小さな動画カードだけ作りたい
解答例
B です。
チャンネルに関する情報が増えているため、Channel classとして分けたほうが整理しやすくなります。
A や C のような小さな設計では、まずは channelName を Video に直接持たせても大丈夫です。
よくあるつまずき1:最初から完璧な設計をしようとする
初心者がよくつまずくのは、最初から完璧なclass設計を作ろうとして、手が止まってしまうことです。
しかし、最初から完璧である必要はありません。
まず動く形を作る。
必要になったら整理する。
情報が増えたらclassを分ける。
この順番で大丈夫です。
今回の完成アプリでも、まずは Video class中心のシンプルな設計にしています。
よくあるつまずき2:classを分けすぎる
classを学ぶと、何でもclassに分けたくなることがあります。
しかし、分けすぎると、初心者には読みにくくなります。
たとえば、小さなアプリで次のように分けると、少し複雑です。
Video
Channel
Category
Thumbnail
Duration
ViewCount
PublishedDate
本格的なアプリでは有効な場合もあります。
しかし、学習用のミニアプリでは、まずは次のくらいで十分です。
Video
categories
videos
必要になったら分ける、という考え方を持ちましょう。
よくあるつまずき3:Stringでよいものまでclassにしてしまう
カテゴリ名だけを表示するなら、String のListで十分です。
const categories = [
'すべて',
'Flutter',
'Dart',
];
カテゴリに選択状態やアイコンなどを持たせたくなったら、Category classを考えます。
class Category {
final String label;
final bool isSelected;
}
つまり、情報が少ないうちはシンプルに。
情報が増えたらclassにする。
これが分かりやすい進め方です。
よくあるつまずき4:property名があいまいになる
名前があいまいだと、あとから読みにくくなります。
避けたい例です。
final String text;
final String name;
final int number;
final bool flag;
何を表しているのか分かりにくいです。
分かりやすい例です。
final String title;
final String channelName;
final int views;
final bool isLive;
何のデータか分かります。
名前は、コードを読む人への説明です。
自分があとで見ても分かる名前をつけることが大切です。
よくあるつまずき5:UIから逆算しない
class設計で迷ったら、画面を見るのが一番分かりやすいです。
画面に何が表示されるか
↓
その表示にはどんなデータが必要か
↓
どのclassに持たせるか
たとえば、動画時間をサムネイル右下に表示したいなら、duration が必要です。
final String duration;
ライブ表示を切り替えたいなら、isLive が必要です。
final bool isLive;
このように、UIから逆算すると、必要なpropertyが見つけやすくなります。
この節で覚える対応表
| 考え方 | 例 | 説明 |
|---|---|---|
| 動画1本分 | Video | 動画カードの元データ |
| チャンネル情報 | Channel | チャンネル名、登録者数など |
| カテゴリ情報 | Category | 表示名、選択状態など |
| 小さなアプリ | Video中心 | シンプルで理解しやすい |
| 本格的なアプリ | classを分ける | 拡張しやすい |
| カテゴリ文字だけ | List<String> | まずはこれで十分 |
| 複数の動画 | List<Video> | 動画一覧の元データ |
| UIへの受け渡し | VideoCard(video: video) | データをカードに渡す |
確認問題1
クラス設計とは、どのような作業ですか。
答え
アプリに登場する情報を、使いやすいデータの形に整理することです。
YouTube風アプリでは、動画情報を Video、チャンネル情報を Channel、カテゴリ情報を Category のように整理できます。
確認問題2
今回の完成アプリで、まず Video class中心の設計にする理由は何ですか。
答え
学習用の小さなミニアプリでは、シンプルな設計のほうが理解しやすいからです。
チャンネル情報やカテゴリ情報が増えてきたら、あとから Channel classや Category classに分けることもできます。
確認問題3
Channel classに分けたほうがよいのは、どのような場合ですか。
答え
チャンネル名だけでなく、登録者数、アイコン、公式マーク、説明文など、チャンネルに関する情報が増えてきた場合です。
確認問題4
カテゴリ名を表示するだけなら、どのようなデータで十分ですか。
答え
List<String> で十分です。
const categories = [
'すべて',
'Flutter',
'Dart',
];
確認問題5
UIから逆算してpropertyを考えるとは、どういうことですか。
答え
画面に表示したいものを見て、その表示に必要なデータをpropertyとして考えることです。
たとえば、動画時間を表示したいなら duration、ライブ表示を切り替えたいなら isLive が必要になります。
この節のまとめ
この節では、YouTube風ミニアプリに必要なclassを設計する考え方を学びました。
クラス設計は、アプリに登場する情報を整理する作業です。
動画1本分の情報は Video classにできます。
チャンネル情報が増えてきたら Channel classにできます。
カテゴリ情報が増えてきたら Category classにできます。
ただし、最初から細かく分けすぎる必要はありません。
今回の完成アプリでは、初心者が理解しやすいように、まずは Video class中心のシンプルな設計にします。
この節のポイント:
- 画面に表示される情報から必要なデータを考える
- 動画1本分の情報はVideo classにまとめる
- 情報が増えたらChannelやCategoryに分ける
- 小さなアプリではシンプルな設計でよい
- 本格的にするなら役割ごとにclassを分ける
- class名やproperty名は、意味が分かる名前にする
大切なこと
クラス設計は、画面に必要な情報を、意味のある単位で整理する作業である。
次の 3-9 では、ここまで作ってきた Video データを、FlutterのUIにどのようにつなげるかを学びます。
List<Video> を ListView.builder に渡し、VideoCard として表示する考え方へ進みます。