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

【データ設計】MovieItemクラスで作品情報をひとまとまりにする

7NETFLIX風動画アプリを作りながらUI・検索・動画再生・共有機能を学ぶFlutter(iOS・Android)アプリ開発
FlutteriOSAndroidMacOSWindows基礎から学ぶ開発アプリ開発

この節で学ぶこと

前の節では、黒背景・赤アクセント・白文字を使って、動画アプリ風の世界観を作る方法を学びました。

今回の節では、アプリに表示する 作品情報の持ち方 を見ていきます。

アプリの画面には、作品タイトル、説明文、ポスター画像、背景画像、YouTube動画ID、年齢制限、カテゴリなど、いろいろな情報が表示されています。

たとえば、Home画面では作品の画像が表示されます。

作品詳細画面では、タイトルや説明文が表示されます。

YouTube再生画面では、動画IDを使ってトレーラーを再生します。

Search画面では、タイトルやカテゴリを使って検索します。

このように、1つの作品にはたくさんの情報が入っています。

その情報をバラバラに持つのではなく、MovieItemクラスでひとまとまりにして管理する のが今回のポイントです。


まず完成コードを見てみよう

今回の作品データは、MovieItem というクラスで作られています。

class MovieItem {
  const MovieItem({
    required this.title,
    required this.subtitle,
    required this.description,
    required this.posterUrl,
    required this.backdropUrl,
    required this.youtubeVideoId,
    required this.year,
    required this.rating,
    required this.seasonLabel,
    required this.category,
    required this.matchRate,
    required this.homeCategory,
  });

  final String title;
  final String subtitle;
  final String description;
  final String posterUrl;
  final String backdropUrl;
  final String youtubeVideoId;
  final String year;
  final String rating;
  final String seasonLabel;
  final String category;
  final int matchRate;
  final HomeCategory homeCategory;
}

最初は少し長く見えるかもしれません。

でも、やっていることはシンプルです。

1つの作品に必要な情報をまとめるための設計図を作っている

ということです。


MovieItemは「作品カードの設計図」

MovieItem は、作品情報を入れるための箱のようなものです。

たとえば、1つの作品には次のような情報があります。

情報
タイトルSquid Game
サブタイトルSurvival thriller series
説明文Hundreds of cash-strapped players…
ポスター画像縦長の作品画像URL
背景画像横長の背景画像URL
YouTube動画IDoqxAJKy0ii4
公開年2021
年齢制限18+
シーズンSeason 1
カテゴリTV Drama
マッチ率98
HomeカテゴリShows

これらを毎回バラバラに書くと、かなり管理しづらくなります。

そこで、MovieItem という形でまとめています。

作品1つ分の情報 = MovieItem

と考えると分かりやすいです。


クラスとは?

ここで出てくる class という言葉も確認しておきましょう。

class MovieItem {
  ...
}

class は、データや処理をまとめるための設計図です。

今回の場合、MovieItem は「作品情報の設計図」です。

たとえるなら、映画館の作品紹介カードを作るための入力用紙のようなものです。

タイトルを書く欄
説明文を書く欄
画像URLを書く欄
年齢制限を書く欄
カテゴリを書く欄

このような欄をあらかじめ決めておくことで、作品情報を同じ形でそろえることができます。


final String title とは?

MovieItem の中には、次のようなコードがあります。

final String title;

これは、

titleという名前の文字データを持ちます

という意味です。

分けて見ると、こうなります。

コード意味
final一度入れたら基本的に変更しない
String文字データ
titleデータの名前

つまり、title には作品タイトルが入ります。

たとえば、

title: 'Squid Game',

のように使います。


Stringとintの違い

MovieItem には、Stringint が出てきます。

final String title;
final int matchRate;

String は文字です。

'Squid Game'
'2021'
'18+'

このように ' で囲まれているものは、基本的に文字として扱われます。

一方で、int は整数です。

98
96
91

今回の matchRate は、98% Match のように表示するための数字なので、int にしています。

使うもの
String文字'Squid Game'
int整数98

最初は、「文字はString、数字はint」と覚えておくと十分です。


required this.title とは?

次に、この部分を見てみましょう。

const MovieItem({
  required this.title,
  required this.subtitle,
  required this.description,
  ...
});

これは、MovieItem を作るときに必要な情報を指定しています。

required は、

これは必ず入力してください

という意味です。

たとえば、title がない作品データを作ろうとすると困ります。

タイトルがないと、画面に作品名を表示できません。

そのため、

required this.title,

として、必ずタイトルを入れるようにしています。


const MovieItemとは?

MovieItem の最初に、次のように書かれています。

const MovieItem({
  ...
});

const は、簡単に言うと「中身が固定されているデータ」に使います。

今回の作品データは、アプリの中に最初から書いてある固定データです。

ユーザーが自由に追加・編集するものではありません。

そのため、const を使っています。

最初は深く考えすぎなくて大丈夫です。

最初から決まっているデータにはconstを使うことがある

くらいでOKです。


実際の作品データを見てみよう

次に、MovieItem を使って作品データを作っている部分を見てみます。

const movies = [
  MovieItem(
    title: 'Squid Game',
    subtitle: 'Survival thriller series',
    description:
        'Hundreds of cash-strapped players accept a strange invitation to compete in children’s games. Inside, a tempting prize awaits — with deadly high stakes.',
    posterUrl:
        'https://image.tmdb.org/t/p/w500/dDlEmu3EZ0Pgg93K2SVNLCjCSvE.jpg',
    backdropUrl:
        'https://image.tmdb.org/t/p/w1280/oaGvjB0DvdhXhOAuADfHb261ZHa.jpg',
    youtubeVideoId: 'oqxAJKy0ii4',
    year: '2021',
    rating: '18+',
    seasonLabel: 'Season 1',
    category: 'TV Drama',
    matchRate: 98,
    homeCategory: HomeCategory.shows,
  ),
];

これは、Squid Game という作品1つ分のデータです。

MovieItem の中で決めた項目に沿って、実際の値を入れています。

つまり、次のような関係です。

MovieItemクラス
↓
作品情報の設計図

MovieItem(...)
↓
設計図を使って作った作品データ

moviesリストとは?

作品データは、movies というリストにまとめています。

const movies = [
  MovieItem(...),
  MovieItem(...),
  MovieItem(...),
];

リストとは、複数のデータを順番に並べたものです。

今回の場合は、複数の作品をまとめて管理しています。

movies
├── Squid Game
├── Stranger Things
├── Money Heist
├── Wednesday
├── Dark
└── The Witcher

Home画面やSearch画面では、この movies リストを使って作品を表示しています。


なぜリストにするの?

アプリには、作品が1つだけ表示されるわけではありません。

横スクロールの一覧にも使いますし、検索結果にも使います。

Clips画面では縦にスワイプして作品を切り替えます。

My Netaflix画面では、Continue Watchingとして並べています。

そのため、作品データをまとめておく必要があります。

movies にまとめておくと、いろいろな画面で同じ作品データを使い回せます。

作品データを1か所にまとめる
↓
Home画面でも使える
↓
Search画面でも使える
↓
Clips画面でも使える
↓
詳細画面でも使える

これがデータ設計の大事なところです。


各項目の役割を確認しよう

ここで、MovieItem の各項目がどこで使われるのかを整理します。

項目役割
title作品タイトルとして表示する
subtitle作品の短い説明として使う
description詳細画面やClips画面の説明文に使う
posterUrl縦長のポスター画像に使う
backdropUrl横長の背景画像に使う
youtubeVideoIdYouTube再生に使う
year公開年として表示する
rating年齢制限として表示する
seasonLabelシーズン情報として表示する
categoryジャンル表示や検索に使う
matchRate98% Match のような表示に使う
homeCategoryHome画面のカテゴリ切り替えに使う

このように、作品データは画面のあちこちで使われています。

1つのデータが、いろいろな画面に反映されるのが面白いところです。


posterUrlとbackdropUrlの違い

作品画像には、posterUrlbackdropUrl の2つがあります。

posterUrl:
    'https://image.tmdb.org/t/p/w500/dDlEmu3EZ0Pgg93K2SVNLCjCSvE.jpg',
backdropUrl:
    'https://image.tmdb.org/t/p/w1280/oaGvjB0DvdhXhOAuADfHb261ZHa.jpg',

どちらも画像URLですが、使われる場所が違います。

項目画像の形使う場所
posterUrl縦長画像作品カード、横スクロール一覧
backdropUrl横長画像Homeの背景、詳細画面の背景、Clips背景

動画アプリでは、縦長のポスター画像と横長の背景画像を使い分けることが多いです。

縦長画像は、作品一覧に並べやすいです。

横長画像は、画面いっぱいの背景に使いやすいです。


youtubeVideoIdは動画再生に使う

youtubeVideoId は、YouTube動画を再生するために使います。

youtubeVideoId: 'oqxAJKy0ii4',

YouTubeのURLがこちらだとします。

https://www.youtube.com/watch?v=oqxAJKy0ii4

この中の動画IDは、

oqxAJKy0ii4

です。

アプリでは、この動画IDを使ってYouTubeプレイヤーを表示しています。

controller = YoutubePlayerController.fromVideoId(
  videoId: widget.movie.youtubeVideoId,
  autoPlay: true,
  params: const YoutubePlayerParams(
    showControls: true,
    showFullscreenButton: true,
    playsInline: true,
    strictRelatedVideos: true,
    enableCaption: true,
  ),
);

つまり、作品ごとに youtubeVideoId を変えると、再生される動画も変わります。


homeCategoryとは?

homeCategory は、Home画面のカテゴリ切り替えに使います。

homeCategory: HomeCategory.shows,

今回のアプリには、次のカテゴリがあります。

enum HomeCategory {
  all,
  shows,
  movies,
  games,
  newHot,
}

この homeCategory によって、作品がどのカテゴリに属しているかを決めています。

たとえば、Squid Gameshows です。

homeCategory: HomeCategory.shows,

WednesdaynewHot です。

homeCategory: HomeCategory.newHot,

Home画面で Shows タブを押すと、HomeCategory.shows の作品だけが表示されます。


enumとは?

ここで出てくる enum も確認しておきましょう。

enum HomeCategory {
  all,
  shows,
  movies,
  games,
  newHot,
}

enum は、選択肢をあらかじめ決めておくためのものです。

今回の場合は、Home画面のカテゴリとして使えるものを決めています。

All
Shows
Movies
Games
New & Hot

文字で毎回 'shows''movies' と書く方法もありますが、スペルミスが起きやすくなります。

enum にしておくと、決まった選択肢の中から選べるので、コードが安全になります。


labelで表示名を作る

HomeCategory には、画面に表示するためのラベルも用意しています。

extension HomeCategoryLabel on HomeCategory {
  String get label {
    switch (this) {
      case HomeCategory.all:
        return 'All';
      case HomeCategory.shows:
        return 'Shows';
      case HomeCategory.movies:
        return 'Movies';
      case HomeCategory.games:
        return 'Games';
      case HomeCategory.newHot:
        return 'New & Hot';
    }
  }
}

これは、HomeCategory.shows を画面上では Shows と表示するための仕組みです。

たとえば、コードではこのように使えます。

tab.label

すると、カテゴリに応じて、

Shows
Movies
Games
New & Hot

のように表示されます。


データが画面に表示される流れ

ここまで見た内容を、画面表示の流れとして整理してみます。

moviesリストに作品データを書く
↓
Home画面がmoviesを読む
↓
作品カードにposterUrlを表示する
↓
作品カードをタップする
↓
MovieDetailPageにmovieを渡す
↓
詳細画面でtitleやdescriptionを表示する
↓
PlayボタンでyoutubeVideoIdを使って動画を再生する

つまり、1つの MovieItem データが、いろいろな画面につながっています。

データ設計がきれいにできていると、画面づくりも楽になります。


まずカスタマイズしてみよう

今回は、作品タイトルを変えてみましょう。

movies の中にある、次の部分を探してください。

title: 'Squid Game',

これを、たとえば次のように変えてみます。

title: 'Survival Game',

保存して、アプリを確認してください。

Home画面や詳細画面のタイトルが変わっていれば成功です。

このように、movies のデータを変えるだけで、画面の表示も変わります。


説明文もカスタマイズしてみよう

次に、作品の説明文を変えてみます。

description:
    'Hundreds of cash-strapped players accept a strange invitation to compete in children’s games. Inside, a tempting prize awaits — with deadly high stakes.',

たとえば、教材用に分かりやすくするなら、次のようにできます。

description:
    '参加者たちが不思議なゲームに挑戦する、緊張感のあるサバイバル作品です。',

保存して、作品詳細画面やClips画面を確認してください。

説明文が変わっていればOKです。


matchRateをカスタマイズしてみよう

次は、マッチ率を変えてみます。

matchRate: 98,

これを次のように変えてみます。

matchRate: 87,

保存して、詳細画面やClips画面を確認してください。

87% Match

のように表示が変わります。

数字を変えるだけで、画面の情報が変わることが分かります。


よくあるつまずきポイント

Q. 作品データを追加したらエラーが出ました。

MovieItem では、すべての項目が required になっています。

そのため、作品を追加するときは、次の項目をすべて入れる必要があります。

MovieItem(
  title: '',
  subtitle: '',
  description: '',
  posterUrl: '',
  backdropUrl: '',
  youtubeVideoId: '',
  year: '',
  rating: '',
  seasonLabel: '',
  category: '',
  matchRate: 0,
  homeCategory: HomeCategory.shows,
),

1つでも足りないと、エラーになることがあります。


Q. カンマは必要ですか?

必要です。

Dartでは、項目の区切りにカンマを使います。

title: 'Squid Game',
subtitle: 'Survival thriller series',
description: '...',

カンマがないと、次の項目との区切りが分からなくなってエラーになります。

特に、作品データを追加するときはカンマ忘れが起きやすいです。


Q. 画像が表示されません。

posterUrlbackdropUrl のURLが正しいか確認してください。

また、URLが途中で切れていないかも見てください。

posterUrl:
    'https://image.tmdb.org/t/p/w500/dDlEmu3EZ0Pgg93K2SVNLCjCSvE.jpg',

URLは長いので、コピーするときに一部が抜けることがあります。


Q. YouTubeが再生されません。

youtubeVideoId に、URL全体ではなく動画IDだけを入れているか確認してください。

OKな例:

youtubeVideoId: 'oqxAJKy0ii4',

間違いやすい例:

youtubeVideoId: 'https://www.youtube.com/watch?v=oqxAJKy0ii4',

今回のコードでは、動画IDだけを使います。


チャレンジ

チャレンジ1:作品タイトルを変えてみよう

次のコードを探してください。

title: 'Squid Game',

これを好きな作品名に変えてみましょう。

例:

title: 'Survival Game',

チャレンジ2:説明文を日本語にしてみよう

次のような説明文を探してください。

description:
    'Hundreds of cash-strapped players accept a strange invitation to compete in children’s games. Inside, a tempting prize awaits — with deadly high stakes.',

これを日本語の説明文に変えてみましょう。

例:

description:
    '参加者たちが不思議なゲームに挑戦する、緊張感のあるサバイバル作品です。',

チャレンジ3:マッチ率を変えてみよう

次のコードを探してください。

matchRate: 98,

これを次のように変えてみましょう。

matchRate: 87,

チャレンジ4:カテゴリを変えてみよう

次のコードを探してください。

homeCategory: HomeCategory.shows,

これを次のように変えてみましょう。

homeCategory: HomeCategory.movies,

Home画面でカテゴリタブを押したとき、表示される場所が変わるか確認してください。


チャレンジの答え

チャレンジ1の答え

変更前:

title: 'Squid Game',

変更後の例:

title: 'Survival Game',

Home画面や詳細画面に表示される作品タイトルが変わります。


チャレンジ2の答え

変更前:

description:
    'Hundreds of cash-strapped players accept a strange invitation to compete in children’s games. Inside, a tempting prize awaits — with deadly high stakes.',

変更後の例:

description:
    '参加者たちが不思議なゲームに挑戦する、緊張感のあるサバイバル作品です。',

詳細画面やClips画面に表示される説明文が変わります。


チャレンジ3の答え

変更前:

matchRate: 98,

変更後:

matchRate: 87,

画面上の表示が 87% Match に変わります。


チャレンジ4の答え

変更前:

homeCategory: HomeCategory.shows,

変更後:

homeCategory: HomeCategory.movies,

Home画面で Movies タブを押したときに、その作品が表示されるようになります。


この節のまとめ

この節では、MovieItem クラスを使って作品情報をひとまとまりにする方法を学びました。

大切なポイントは次の通りです。

  • MovieItem は、作品情報をまとめるためのクラス。
  • titledescriptionposterUrlyoutubeVideoId などを1つの作品データとして持てる。
  • String は文字、int は整数を表す。
  • required がついている項目は、作品データを作るときに必ず必要。
  • movies リストに複数の MovieItem を入れることで、作品一覧を作れる。
  • posterUrl は縦長画像、backdropUrl は横長背景画像に使う。
  • youtubeVideoId は、YouTube再生に使う。
  • homeCategory は、Home画面のカテゴリ切り替えに使う。
  • データを1か所にまとめると、いろいろな画面で使い回しやすい。

次のステップ

次の節では、movies リストに作品データを追加する方法を学びます。

今回学んだ MovieItem の設計図を使って、新しい作品を1つ追加してみます。

作品タイトル、画像URL、説明文、YouTube動画IDを入れて、自分だけの作品データを画面に表示していきましょう。

教材トップへ戻る