
classでVideoデータを設計する
この節で学ぶこと
前回の 3-2 では、List と Map を使って、複数の動画データをまとめて扱う方法を学びました。
動画一覧は、次のように考えられました。
List
├─ Map 動画1本目
├─ Map 動画2本目
└─ Map 動画3本目
Mapを使うと、動画1本分の情報をまとめることができます。
final video = {
'title': 'FlutterでYouTube風UIを作る',
'channelName': 'Code Studio',
'views': 128000,
'publishedAt': '2日前',
'duration': '12:34',
};
ただし、Mapには弱点もありました。
キーの名前を間違えやすいこと。
型が分かりにくくなりやすいこと。
アプリが大きくなると、どんな情報を持つデータなのか見えにくくなること。
そこで今回学ぶのが、class です。
この節では、YouTube風アプリで使う Video データを、Mapではなく class として設計する方法を学びます。
この節のゴール
この節のゴールは、class と instance の基本を理解し、動画1本分のデータを Video classで表せるようになることです。
最終的には、次のようなコードの意味が分かるようになることを目指します。
final video = Video(
title: 'FlutterでYouTube風UIを作る|ListViewとCardの実践入門',
channelName: 'Code Studio',
views: 128000,
publishedAt: '2日前',
duration: '12:34',
);
これは、次のように読めます。
Videoという設計図を使って、
動画1本分のデータを作っている。
この節で大切なのは、次の一文です。
classは、データの形を決める設計図である。
なぜMapだけでは足りないのか
まず、Mapで動画1本分のデータを作った例を見てみます。
void main() {
final video = {
'title': 'FlutterでYouTube風UIを作る',
'channelName': 'Code Studio',
'views': 128000,
'publishedAt': '2日前',
'duration': '12:34',
};
print(video['title']);
print(video['channelName']);
print(video['views']);
}
このコードは動きます。
動画1本分の情報も、まとまっています。
ただし、次のような問題があります。
| Mapで困りやすいこと | 例 |
|---|---|
| キーを打ち間違えやすい | title を titel と書いてしまう |
| どんな情報を持つべきか見えにくい | duration が必要なのか、あとから分かりにくい |
| 型が分かりにくい | views が int なのか String なのか見えにくい |
| 同じ形を何度も使いにくい | 動画データを毎回Mapで書くと統一しづらい |
たとえば、次のコードを見てください。
void main() {
final video = {
'title': 'FlutterでYouTube風UIを作る',
'views': 128000,
};
print(video['titel']);
}
title と書くべきところを、titel と間違えています。
Mapではキーを文字で指定するため、このようなミスが起きやすくなります。
小さな練習ならMapでも問題ありません。
しかし、YouTube風アプリのように動画データを何件も扱う場合は、データの形をしっかり決めておいたほうが安全です。
そこで使うのが class です。
classとは何か
class は、データの形を決める設計図です。
YouTube風アプリでは、動画1本分のデータに、次のような情報が必要です。
動画1本分の情報:
- 動画タイトル
- チャンネル名
- 再生回数
- 投稿日
- 動画時間
これをDartのclassで表すと、次のようになります。
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
required this.publishedAt,
required this.duration,
});
final String title;
final String channelName;
final int views;
final String publishedAt;
final String duration;
}
このコードは、次の意味です。
Videoというデータの形を作る。
Videoは、title、channelName、views、publishedAt、durationを持つ。
つまり、Video classは、動画1本分の設計図です。
classの中身を分解して見る
先ほどのコードを、少しずつ分解します。
class Video {
これは、Video というclassを作るという意味です。
Videoという名前の設計図を作る
次に、classの中にある項目を見ます。
final String title;
final String channelName;
final int views;
final String publishedAt;
final String duration;
これは、Video が持つデータです。
| コード | 意味 |
|---|---|
final String title; | 動画タイトルを文字列として持つ |
final String channelName; | チャンネル名を文字列として持つ |
final int views; | 再生回数を整数として持つ |
final String publishedAt; | 投稿日を文字列として持つ |
final String duration; | 動画時間を文字列として持つ |
このような、classが持つデータの項目を property と呼びます。
propertyとは何か
property は、データが持っている情報です。
YouTube風アプリで考えると、動画1本が持っている情報がpropertyです。
| 現実の意味 | property名 | 型 |
|---|---|---|
| 動画タイトル | title | String |
| チャンネル名 | channelName | String |
| 再生回数 | views | int |
| 投稿日 | publishedAt | String |
| 動画時間 | duration | String |
たとえば、次のように考えます。
Video
├─ title
├─ channelName
├─ views
├─ publishedAt
└─ duration
動画カードを作るために必要な情報を、Video の中に入れておく。
それがpropertyの役割です。
constructorとは何か
classの中には、次の部分もありました。
const Video({
required this.title,
required this.channelName,
required this.views,
required this.publishedAt,
required this.duration,
});
これは constructor です。
constructorは、データを作るときの入口です。
たとえば、実際に動画データを作るときは、次のように書きます。
final video = Video(
title: 'FlutterでYouTube風UIを作る',
channelName: 'Code Studio',
views: 128000,
publishedAt: '2日前',
duration: '12:34',
);
このとき、Video(...) の中に渡した値が、classのpropertyに入ります。
titleに入るもの:
'FlutterでYouTube風UIを作る'
channelNameに入るもの:
'Code Studio'
viewsに入るもの:
128000
つまり、constructorは、Video データを作るときに必要な値を受け取る場所です。
instanceとは何か
class は設計図です。
しかし、設計図だけでは実際のデータはありません。
実際に作られた1つ1つのデータを instance と呼びます。
YouTube風アプリで考えると、次のようになります。
class:
Videoという動画データの設計図
instance:
実際の動画1本分のデータ
コードで見ると、これがclassです。
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
required this.publishedAt,
required this.duration,
});
final String title;
final String channelName;
final int views;
final String publishedAt;
final String duration;
}
そして、これがinstanceです。
final video = Video(
title: 'FlutterでYouTube風UIを作る',
channelName: 'Code Studio',
views: 128000,
publishedAt: '2日前',
duration: '12:34',
);
Video という設計図から、video という実際の動画データを作っています。
classとinstanceの違い
classとinstanceの違いを整理します。
| 用語 | 意味 | YouTube風UIでの例 |
|---|---|---|
| class | データの設計図 | Video |
| instance | 設計図から作った実際のデータ | video |
| property | データが持つ情報 | title、views |
| constructor | データを作る入口 | Video(...) |
たとえるなら、classは「たい焼きの型」です。
instanceは「実際に焼き上がったたい焼き」です。
YouTube風アプリなら、classは Video という設計図。
instanceは、実際の動画1本分のデータです。
Video class
↓
FlutterでYouTube風UIを作る
DartのListとMapを解説
UIデザインの基本
まずは最小のVideo classを作る
いきなり完成形のclassを書くと長くなります。
まずは、動画タイトルだけを持つclassから始めます。
void main() {
final video = Video(
title: 'FlutterでYouTube風UIを作る',
);
print(video.title);
}
class Video {
const Video({
required this.title,
});
final String title;
}
このコードでは、Video は title だけを持っています。
Video
└─ title
まずはこれで十分です。
「classを作る」
「instanceを作る」
「propertyを取り出す」
この3つを確認します。
propertyを増やす
次に、チャンネル名を追加します。
void main() {
final video = Video(
title: 'FlutterでYouTube風UIを作る',
channelName: 'Code Studio',
);
print(video.title);
print(video.channelName);
}
class Video {
const Video({
required this.title,
required this.channelName,
});
final String title;
final String channelName;
}
Video が持つ情報が増えました。
Video
├─ title
└─ channelName
このように、必要な情報を少しずつpropertyとして追加していきます。
再生回数を追加する
次に、再生回数を追加します。
再生回数は数字なので、int を使います。
void main() {
final video = Video(
title: 'FlutterでYouTube風UIを作る',
channelName: 'Code Studio',
views: 128000,
);
print(video.title);
print(video.channelName);
print(video.views);
}
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
});
final String title;
final String channelName;
final int views;
}
ここで大切なのは、title と channelName は String、views は int であることです。
| property | 型 | 理由 |
|---|---|---|
| title | String | 文字だから |
| channelName | String | 文字だから |
| views | int | 数字だから |
classを使うと、このように型をはっきり書けます。
投稿日と動画時間を追加する
YouTube風の動画カードでは、投稿日と動画時間も必要です。
void main() {
final video = Video(
title: 'FlutterでYouTube風UIを作る',
channelName: 'Code Studio',
views: 128000,
publishedAt: '2日前',
duration: '12:34',
);
print(video.title);
print(video.channelName);
print(video.views);
print(video.publishedAt);
print(video.duration);
}
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
required this.publishedAt,
required this.duration,
});
final String title;
final String channelName;
final int views;
final String publishedAt;
final String duration;
}
これで、基本的な動画カードに必要な情報がそろいました。
Video
├─ title
├─ channelName
├─ views
├─ publishedAt
└─ duration
この形が、YouTube風UIの動画カードの土台になります。
Mapで書いた場合と比べる
Mapで書くと、次のようになります。
final video = {
'title': 'FlutterでYouTube風UIを作る',
'channelName': 'Code Studio',
'views': 128000,
'publishedAt': '2日前',
'duration': '12:34',
};
classで書くと、次のようになります。
final video = Video(
title: 'FlutterでYouTube風UIを作る',
channelName: 'Code Studio',
views: 128000,
publishedAt: '2日前',
duration: '12:34',
);
取り出し方も違います。
| Map | class |
|---|---|
video['title'] | video.title |
video['channelName'] | video.channelName |
video['views'] | video.views |
| キーを文字で指定する | propertyとして指定する |
| 打ち間違いに気づきにくい | 型と名前が見えやすい |
classを使うと、コードが「動画データらしく」読めるようになります。
完成アプリのVideo classを見てみる
完成アプリでは、さらに多くのpropertyを持つ 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.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;
}
このclassは、動画カードに必要な情報をまとめています。
| property | 型 | 画面での使い道 |
|---|---|---|
| title | String | 動画タイトル |
| channelName | String | チャンネル名 |
| views | int | 再生回数 |
| publishedAt | String | 投稿日 |
| duration | String | 動画時間 |
| category | String | サムネイル中央の文字 |
| thumbnailColor | Color | サムネイル背景色 |
| channelColor | Color | チャンネルアイコン色 |
| isLive | bool | ライブ表示の切り替え |
今は、Color や bool が少し難しく見えても大丈夫です。
大切なのは、Video classの中に、動画カードを作るための情報が集まっていることです。
Colorをpropertyとして持つ理由
完成アプリでは、サムネイルやチャンネルアイコンに色を使っています。
そのため、Video の中に色の情報も入れています。
final Color thumbnailColor;
final Color channelColor;
これは、次のように使われます。
| property | 使われる場所 |
|---|---|
| thumbnailColor | サムネイルの背景色 |
| channelColor | チャンネルアイコンの背景色 |
たとえば、動画ごとにサムネイルの色を変えたい場合、動画データの中に色を持たせると便利です。
Video(
title: 'DartのListとMapを解説',
thumbnailColor: Color(0xFF1E3A8A),
channelColor: Color(0xFF1565C0),
// 他の値は省略
)
このように、画面の見た目に関わる情報も、データとして持つことがあります。
boolをpropertyとして持つ理由
完成アプリでは、ライブ配信中かどうかを isLive で持っています。
final bool isLive;
bool は、true または false のどちらかを持つ型です。
| 値 | 意味 |
|---|---|
| true | ライブ配信中 |
| false | 通常動画 |
たとえば、ライブ中なら「ライブ配信中」と表示したい。
通常動画なら「12.8万回視聴・2日前」と表示したい。
このような表示切り替えに bool が使えます。
isLiveがtrue
↓
ライブ配信中と表示
isLiveがfalse
↓
再生回数と投稿日を表示
この処理は、3-5のmethodやgetterで詳しく扱います。
完成アプリではVideoCardにVideoを渡す
完成アプリには、次のコードがあります。
VideoCard(video: videos[index])
これは、動画一覧の中から1本分の Video を取り出して、VideoCard に渡しています。
VideoCard の中では、次のように受け取っています。
class VideoCard extends StatelessWidget {
const VideoCard({
super.key,
required this.video,
});
final Video video;
}
この final Video video; は、次の意味です。
VideoCardは、Video型のデータを受け取る。
つまり、画面に表示される動画カードは、Video データをもとに作られています。
ここが、DartのclassとFlutterのUIがつながる場所です。
DartPadで試すコード1:最小のclass
まずは、titleだけを持つ最小のclassを試します。
void main() {
final video = Video(
title: 'FlutterでYouTube風UIを作る',
);
print(video.title);
}
class Video {
const Video({
required this.title,
});
final String title;
}
確認することは、次の3つです。
1. class Videoを作っている
2. Videoからvideoというデータを作っている
3. video.titleで値を取り出している
DartPadで試すコード2:動画カードに近づける
次に、動画カードに必要な情報を増やします。
void main() {
final video = Video(
title: 'FlutterでYouTube風UIを作る',
channelName: 'Code Studio',
views: 128000,
publishedAt: '2日前',
duration: '12:34',
);
print('タイトル: ${video.title}');
print('チャンネル: ${video.channelName}');
print('再生回数: ${video.views}');
print('投稿日: ${video.publishedAt}');
print('動画時間: ${video.duration}');
}
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
required this.publishedAt,
required this.duration,
});
final String title;
final String channelName;
final int views;
final String publishedAt;
final String duration;
}
出力結果は次のようになります。
タイトル: FlutterでYouTube風UIを作る
チャンネル: Code Studio
再生回数: 128000
投稿日: 2日前
動画時間: 12:34
この時点で、動画1本分の基本データをclassで表せています。
DartPadで試すコード3:複数のVideoを作る
動画アプリでは、動画は1本だけではありません。
複数の Video instanceを作ってみます。
void main() {
final video1 = Video(
title: 'FlutterでYouTube風UIを作る',
channelName: 'Code Studio',
views: 128000,
publishedAt: '2日前',
duration: '12:34',
);
final video2 = Video(
title: 'DartのListとMapを解説',
channelName: 'App School',
views: 8500,
publishedAt: '5日前',
duration: '08:21',
);
print(video1.title);
print(video2.title);
}
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
required this.publishedAt,
required this.duration,
});
final String title;
final String channelName;
final int views;
final String publishedAt;
final String duration;
}
ここで大切なのは、同じ Video classから、違う動画データを作れることです。
Video class
├─ video1
└─ video2
classは設計図です。
instanceは、その設計図から作った実際のデータです。
DartPadで試すコード4:Listにする
3-2で学んだListと組み合わせると、複数の動画を一覧として持てます。
void main() {
final videos = [
Video(
title: 'FlutterでYouTube風UIを作る',
channelName: 'Code Studio',
views: 128000,
publishedAt: '2日前',
duration: '12:34',
),
Video(
title: 'DartのListとMapを解説',
channelName: 'App School',
views: 8500,
publishedAt: '5日前',
duration: '08:21',
),
Video(
title: 'UIデザインの基本',
channelName: 'Design Lab',
views: 24000,
publishedAt: '1週間前',
duration: '15:10',
),
];
for (final video in videos) {
print('${video.title} / ${video.channelName}');
}
}
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
required this.publishedAt,
required this.duration,
});
final String title;
final String channelName;
final int views;
final String publishedAt;
final String duration;
}
出力結果です。
FlutterでYouTube風UIを作る / Code Studio
DartのListとMapを解説 / App School
UIデザインの基本 / Design Lab
これで、List<Video> の考え方に近づきました。
完成アプリでは、この videos を使って動画カードを一覧表示します。
classを使うと何がうれしいのか
classを使うメリットを整理します。
| メリット | 内容 |
|---|---|
| データの形が分かる | Video が何を持つか見える |
| 型が分かる | views は int、title は String と分かる |
| 取り出しやすい | video.title のように書ける |
| 同じ形で複数作れる | 動画1本目、2本目、3本目を同じ形で作れる |
| UIに渡しやすい | VideoCard(video: video) のように渡せる |
Mapと比べると、classは少し準備が必要です。
しかし、アプリ開発ではその準備が後から効いてきます。
特に、YouTube風アプリのように同じ形の動画カードを何度も表示する場合、classはとても重要です。
よくあるつまずき1:classとfinal videoの違いが分からない
次の2つは、役割が違います。
class Video {
// 設計図
}
final video = Video(
// 実際のデータ
);
整理すると、次の通りです。
| コード | 意味 |
|---|---|
class Video | Videoという設計図を作る |
final video = Video(...) | 設計図から実際の動画データを作る |
classは設計図。
video は実物。
この違いが大切です。
よくあるつまずき2:required this.title が分からない
constructorの中に、次のような書き方があります。
required this.title
これは、次の意味です。
Videoを作るとき、titleを必ず渡してください。
渡された値を、このVideoのtitleに入れます。
たとえば、次のように書く必要があります。
final video = Video(
title: 'FlutterでYouTube風UIを作る',
);
もし title を渡さなければ、必要な情報が足りないということになります。
required は「必須」という意味です。
よくあるつまずき3:final String title; が分からない
次のコードを見てください。
final String title;
これは、次の意味です。
このVideoは、titleという文字列データを持つ。
final は、一度入れたら基本的に変更しないという意味です。
動画タイトルは、データを作ったあとに勝手に変わらないほうが安全です。
そのため、まずは final を使うと考えてください。
| 部分 | 意味 |
|---|---|
| final | あとから変更しない |
| String | 文字列 |
| title | property名 |
よくあるつまずき4:classの外と中が分からない
classの中には、propertyやconstructorを書きます。
class Video {
const Video({
required this.title,
});
final String title;
}
一方で、実際にデータを作る処理は main の中に書きます。
void main() {
final video = Video(
title: 'FlutterでYouTube風UIを作る',
);
print(video.title);
}
役割を分けると、次のようになります。
| 場所 | 書くこと |
|---|---|
| classの中 | データの形 |
| mainの中 | 実際にデータを作って使う |
よくあるつまずき5:Colorが入ると難しく見える
完成アプリでは、Video に Color も入っています。
final Color thumbnailColor;
final Color channelColor;
これは、Flutterの見た目に使う色のデータです。
今は、細かい仕組みを覚える必要はありません。
次の理解で十分です。
thumbnailColor:
サムネイルの背景色
channelColor:
チャンネルアイコンの色
classには、文字や数字だけでなく、色のようなUIに使うデータも持たせることができます。
よくあるつまずき6:boolが入ると混乱する
完成アプリには、isLive というpropertyがあります。
final bool isLive;
これは、ライブ配信中かどうかを表す値です。
| 値 | 意味 |
|---|---|
| true | ライブ中 |
| false | 通常動画 |
このような true / false の値を bool と呼びます。
完成アプリでは、isLive を使って表示を切り替えます。
isLiveがtrue
↓
ライブ配信中と表示
isLiveがfalse
↓
再生回数・投稿日を表示
手を動かす練習1:最小のVideo classを書く
動画タイトルだけを持つ Video classを書いてください。
解答例
void main() {
final video = Video(
title: 'Dartのclass入門',
);
print(video.title);
}
class Video {
const Video({
required this.title,
});
final String title;
}
手を動かす練習2:channelNameを追加する
先ほどの Video classに、チャンネル名を追加してください。
解答例
void main() {
final video = Video(
title: 'Dartのclass入門',
channelName: 'App School',
);
print(video.title);
print(video.channelName);
}
class Video {
const Video({
required this.title,
required this.channelName,
});
final String title;
final String channelName;
}
手を動かす練習3:viewsを追加する
Video classに、再生回数 views を追加してください。
解答例
void main() {
final video = Video(
title: 'Dartのclass入門',
channelName: 'App School',
views: 8500,
);
print(video.title);
print(video.channelName);
print(video.views);
}
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
});
final String title;
final String channelName;
final int views;
}
手を動かす練習4:動画カード用の基本データを完成させる
title、channelName、views、publishedAt、duration を持つ Video classを書いてください。
解答例
void main() {
final video = Video(
title: 'FlutterでYouTube風UIを作る',
channelName: 'Code Studio',
views: 128000,
publishedAt: '2日前',
duration: '12:34',
);
print(video.title);
print(video.channelName);
print(video.views);
print(video.publishedAt);
print(video.duration);
}
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
required this.publishedAt,
required this.duration,
});
final String title;
final String channelName;
final int views;
final String publishedAt;
final String duration;
}
手を動かす練習5:複数のVideoをListに入れる
3本の動画を List に入れて、タイトルを順番に表示してください。
解答例
void main() {
final videos = [
Video(
title: 'FlutterでYouTube風UIを作る',
channelName: 'Code Studio',
views: 128000,
publishedAt: '2日前',
duration: '12:34',
),
Video(
title: 'DartのListとMapを解説',
channelName: 'App School',
views: 8500,
publishedAt: '5日前',
duration: '08:21',
),
Video(
title: 'UIデザインの基本',
channelName: 'Design Lab',
views: 24000,
publishedAt: '1週間前',
duration: '15:10',
),
];
for (final video in videos) {
print(video.title);
}
}
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
required this.publishedAt,
required this.duration,
});
final String title;
final String channelName;
final int views;
final String publishedAt;
final String duration;
}
この節で覚える対応表
| 考え方 | Dartでの表現 | YouTube風UIでの例 |
|---|---|---|
| データの設計図 | class | Video |
| 実際のデータ | instance | 1本分の動画 |
| データの項目 | property | title、views |
| データを作る入口 | constructor | Video(...) |
| 必須の値 | required | titleを必ず渡す |
| 変更しない値 | final | 動画タイトル、再生回数 |
| 複数の動画 | List<Video> | 動画一覧 |
確認問題1
class Video は何を表していますか。
答え
動画1本分のデータの設計図です。
確認問題2
次のコードは、classとinstanceのどちらですか。
final video = Video(
title: 'Flutter入門',
channelName: 'Code Studio',
views: 12000,
publishedAt: '2日前',
duration: '12:34',
);
答え
instanceです。
Video classから作られた、実際の動画1本分のデータです。
確認問題3
final String title; は何を意味しますか。
答え
Video が title という文字列データを持つ、という意味です。
確認問題4
Mapではなくclassを使うメリットを1つ答えてください。
答え
例です。
データの形と型が分かりやすくなり、`video.title` のように安全に取り出しやすくなります。
確認問題5
YouTube風アプリで、List<Video> は何を表しますか。
答え
複数の動画データをまとめた一覧です。
動画一覧画面の元データになります。
この節のまとめ
この節では、Mapでまとめていた動画1本分の情報を、Video classとして設計する方法を学びました。
classは、データの設計図です。
instanceは、その設計図から作った実際のデータです。
YouTube風アプリでは、動画カード1件分の情報を Video classで表します。
class Video {
const Video({
required this.title,
required this.channelName,
required this.views,
required this.publishedAt,
required this.duration,
});
final String title;
final String channelName;
final int views;
final String publishedAt;
final String duration;
}
このclassを使うと、動画データを次のように作れます。
final video = Video(
title: 'FlutterでYouTube風UIを作る',
channelName: 'Code Studio',
views: 128000,
publishedAt: '2日前',
duration: '12:34',
);
この節で一番大切なのは、次の一文です。
YouTube風アプリの動画カードは、Video classから作ったデータをもとに表示される。
次の 3-4 では、constructor と property をさらに丁寧に見ていきます。
動画データの初期値と持ち物を、どのように決めるのかを学びます。