Flutterアプリケーション開発概論

classでVideoデータを設計する

この節で学ぶこと

前回の 3-2 では、ListMap を使って、複数の動画データをまとめて扱う方法を学びました。

動画一覧は、次のように考えられました。

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 として設計する方法を学びます。

この節のゴール

この節のゴールは、classinstance の基本を理解し、動画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で困りやすいこと
キーを打ち間違えやすいtitletitel と書いてしまう
どんな情報を持つべきか見えにくいduration が必要なのか、あとから分かりにくい
型が分かりにくいviewsint なのか 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名
動画タイトルtitleString
チャンネル名channelNameString
再生回数viewsint
投稿日publishedAtString
動画時間durationString

たとえば、次のように考えます。

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データが持つ情報titleviews
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;
}

このコードでは、Videotitle だけを持っています。

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;
}

ここで大切なのは、titlechannelNameStringviewsint であることです。

property理由
titleString文字だから
channelNameString文字だから
viewsint数字だから

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',
);

取り出し方も違います。

Mapclass
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画面での使い道
titleString動画タイトル
channelNameStringチャンネル名
viewsint再生回数
publishedAtString投稿日
durationString動画時間
categoryStringサムネイル中央の文字
thumbnailColorColorサムネイル背景色
channelColorColorチャンネルアイコン色
isLiveboolライブ表示の切り替え

今は、Colorbool が少し難しく見えても大丈夫です。

大切なのは、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 が何を持つか見える
型が分かるviewsinttitleString と分かる
取り出しやすいvideo.title のように書ける
同じ形で複数作れる動画1本目、2本目、3本目を同じ形で作れる
UIに渡しやすいVideoCard(video: video) のように渡せる

Mapと比べると、classは少し準備が必要です。

しかし、アプリ開発ではその準備が後から効いてきます。

特に、YouTube風アプリのように同じ形の動画カードを何度も表示する場合、classはとても重要です。

よくあるつまずき1:classとfinal videoの違いが分からない

次の2つは、役割が違います。

class Video {
  // 設計図
}
final video = Video(
  // 実際のデータ
);

整理すると、次の通りです。

コード意味
class VideoVideoという設計図を作る
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文字列
titleproperty名

よくあるつまずき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が入ると難しく見える

完成アプリでは、VideoColor も入っています。

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:動画カード用の基本データを完成させる

titlechannelNameviewspublishedAtduration を持つ 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での例
データの設計図classVideo
実際のデータinstance1本分の動画
データの項目propertytitleviews
データを作る入口constructorVideo(...)
必須の値requiredtitleを必ず渡す
変更しない値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; は何を意味しますか。

答え

Videotitle という文字列データを持つ、という意味です。

確認問題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 では、constructorproperty をさらに丁寧に見ていきます。

動画データの初期値と持ち物を、どのように決めるのかを学びます。

教材トップへ戻る