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

【My Netaflix画面】プロフィール・通知・ダウンロード・視聴中リストを表示する

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

この節で学ぶこと

前の節では、検索結果UIを作りました。

検索結果があるときは作品一覧を表示し、見つからないときは EmptySearchResult を表示することで、検索画面として自然な動きを作りました。

今回の節では、BottomNavigationの最後のタブである My Netaflix画面 を作ります。

My Netaflix画面は、ユーザー自身の情報や、通知、ダウンロード、視聴中リスト、My Listなどをまとめて表示する画面です。

My Netaflix画面
↓
プロフィール画像を表示
↓
通知・ダウンロードのメニューを表示
↓
視聴中リストを表示
↓
My Listを表示

動画アプリでは、Home、Search、Clipsだけでなく、「自分のページ」があることで、アプリ全体の完成度が高く見えます。

この節では、プロフィール画面らしいUIを、MyNetflixPageMyNetflixCard を使って作っていきます。


My Netaflix画面の完成イメージ

今回作る画面は、次のような構成です。

┌────────────────────┐
│ My Netaflix         │
│              ✎     │
│                    │
│       [プロフィール画像] │
│          User       │
│                    │
│ ┌────────────────┐ │
│ │ 🔔 Notifications│ │
│ └────────────────┘ │
│ ┌────────────────┐ │
│ │ ↓ Downloads     │ │
│ └────────────────┘ │
│                    │
│ Continue Watching  │
│ [作品] [作品] [作品] │
│                    │
│ My List            │
│ [作品] [作品] [作品] │
└────────────────────┘

画面上部にはタイトルと編集アイコンを置きます。

中央にはプロフィール画像と名前を置きます。

その下に、通知やダウンロードなどのメニューカードを置きます。

さらに下に、視聴中の作品リストやMy Listを表示します。


今回見るコード

今回中心になるWidgetは、次の2つです。

MyNetflixPage
MyNetflixCard

役割は次の通りです。

Widget役割
MyNetflixPageMy Netaflix画面全体を作る
MyNetflixCard通知・ダウンロードなどのメニューカードを作る

まずは、MyNetflixPage 全体のコードを見ていきます。

class MyNetflixPage extends StatelessWidget {
  const MyNetflixPage({super.key});

  @override
  Widget build(BuildContext context) {
    final watchingMovies = movies.take(4).toList();
    final myListMovies = movies.skip(2).take(4).toList();

    return Scaffold(
      backgroundColor: NetflixColors.black,
      body: SafeArea(
        child: ListView(
          padding: const EdgeInsets.fromLTRB(18, 18, 18, 110),
          children: [
            Row(
              children: [
                const Text(
                  'My Netaflix',
                  style: TextStyle(
                    color: NetflixColors.white,
                    fontSize: 28,
                    fontWeight: FontWeight.w900,
                  ),
                ),
                const Spacer(),
                IconButton(
                  onPressed: () {},
                  icon: const Icon(
                    Icons.edit_outlined,
                    color: NetflixColors.white,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 24),
            Center(
              child: Column(
                children: [
                  CircleAvatar(
                    radius: 44,
                    backgroundImage: NetworkImage(profileImageUrl),
                  ),
                  const SizedBox(height: 10),
                  const Text(
                    'Netaflix User',
                    style: TextStyle(
                      color: NetflixColors.white,
                      fontSize: 17,
                      fontWeight: FontWeight.w800,
                    ),
                  ),
                ],
              ),
            ),
            const SizedBox(height: 28),
            const MyNetflixCard(
              icon: Icons.notifications_none_rounded,
              title: 'Notifications',
              subtitle: 'Check your latest updates',
            ),
            const SizedBox(height: 12),
            const MyNetflixCard(
              icon: Icons.file_download_outlined,
              title: 'Downloads',
              subtitle: 'Watch your saved titles offline',
            ),
            const SizedBox(height: 28),
            RowTitle(title: 'Continue Watching'),
            const SizedBox(height: 12),
            ContentRow(items: watchingMovies),
            const SizedBox(height: 28),
            RowTitle(title: 'My List'),
            const SizedBox(height: 12),
            ContentRow(items: myListMovies),
          ],
        ),
      ),
    );
  }
}

このコードは少し長く見えますが、構造はシンプルです。

Scaffold
↓
SafeArea
↓
ListView
↓
タイトル行
↓
プロフィール
↓
メニューカード
↓
視聴中リスト
↓
My List

MyNetflixPageはStatelessWidgetで作る

MyNetflixPage は、StatelessWidget で作っています。

class MyNetflixPage extends StatelessWidget {
  const MyNetflixPage({super.key});

StatelessWidget は、内部で状態を持たないWidgetです。

今回のMy Netaflix画面では、検索欄のように入力によって変わる状態はありません。

LikeボタンのON/OFFのような状態も、この画面内では管理していません。

そのため、StatelessWidget で十分です。

画面内で状態を変更しない
↓
StatelessWidgetでよい

watchingMoviesとmyListMoviesを作る

build メソッドの最初で、2つの作品リストを作っています。

final watchingMovies = movies.take(4).toList();
final myListMovies = movies.skip(2).take(4).toList();

この2つは、画面下部に表示する作品リストです。

変数役割
watchingMovies視聴中リストに表示する作品
myListMoviesMy Listに表示する作品

本格的なアプリでは、ユーザーごとの視聴履歴や保存リストをデータベースから取得します。

しかし、今回は教材用なので、すでに用意している movies から一部を取り出して表示しています。


takeで先頭から作品を取り出す

まず、watchingMovies を見てみましょう。

final watchingMovies = movies.take(4).toList();

take(4) は、リストの先頭から4件を取り出す処理です。

movies = [A, B, C, D, E, F]
↓
movies.take(4)
↓
[A, B, C, D]

take の結果はそのままだと Iterable なので、toList() を使って List に変換しています。

.toList()

今回の ContentRowList<MovieItem> を受け取る想定なので、toList() をつけています。


skipで途中から作品を取り出す

次に、myListMovies を見てみましょう。

final myListMovies = movies.skip(2).take(4).toList();

skip(2) は、先頭から2件を飛ばす処理です。

movies = [A, B, C, D, E, F]
↓
movies.skip(2)
↓
[C, D, E, F]

そこからさらに take(4) で4件を取り出しています。

movies.skip(2).take(4)
↓
[C, D, E, F]

このようにすると、Continue WatchingMy List で少し違う作品を表示できます。


takeとskipを使う理由

教材用アプリでは、まだ本物のログイン機能や保存機能を作っていません。

そのため、ユーザーごとの視聴履歴やMy Listは、実際には存在しません。

そこで、仮のデータとして movies から一部を取り出しています。

本格的なアプリ
↓
データベースから視聴履歴を取得

今回の教材
↓
moviesから一部を取り出して表示

このようにしておくと、見た目は本物のアプリらしくなります。

あとでログイン機能や保存機能を追加するときにも、表示部分の考え方はそのまま使えます。


Scaffoldで画面の土台を作る

My Netaflix画面全体は、Scaffold で作っています。

return Scaffold(
  backgroundColor: NetflixColors.black,
  body: SafeArea(
    child: ListView(
      ...
    ),
  ),
);

Scaffold は、Flutterで画面の土台を作るためのWidgetです。

今回は、背景色を黒にしています。

backgroundColor: NetflixColors.black,

Netflix風の画面なので、全体を黒背景にしています。


SafeAreaで端末の余白を守る

Scaffold の中では、SafeArea を使っています。

body: SafeArea(
  child: ListView(
    ...
  ),
),

SafeArea は、スマホのノッチ、ステータスバー、ホームインジケーターなどにUIが重ならないようにするWidgetです。

ステータスバー
ノッチ
ホームインジケーター
↓
UIが重ならないようにする

My Netaflix画面は、上にタイトル、下にリストがあるため、画面端に近いUIが多くなります。

そのため、SafeArea で包んでおくと安心です。


ListViewで縦スクロールできる画面にする

My Netaflix画面の中身は、ListView で作っています。

ListView(
  padding: const EdgeInsets.fromLTRB(18, 18, 18, 110),
  children: [
    ...
  ],
)

ListView を使うと、画面の内容が多くなったときに縦スクロールできます。

今回のMy Netaflix画面には、プロフィール、メニューカード、作品リストが入ります。

スマホの画面サイズによっては、すべてが一画面に収まらない場合があります。

内容が多い
↓
一画面に収まらない
↓
ListViewでスクロールできるようにする

paddingで画面全体の余白を作る

ListView には、画面全体の余白を指定しています。

padding: const EdgeInsets.fromLTRB(18, 18, 18, 110),

これは、左・上・右・下の余白を表します。

位置余白
18
18
18
110

下の余白が大きい理由は、BottomNavigationと重ならないようにするためです。

画面下部
↓
BottomNavigationがある
↓
最後のコンテンツが隠れないように下余白を大きくする

アプリで下部ナビゲーションを使うときは、下の余白を忘れないようにしましょう。


上部タイトル行を作る

My Netaflix画面の一番上には、タイトル行があります。

Row(
  children: [
    const Text(
      'My Netaflix',
      style: TextStyle(
        color: NetflixColors.white,
        fontSize: 28,
        fontWeight: FontWeight.w900,
      ),
    ),
    const Spacer(),
    IconButton(
      onPressed: () {},
      icon: const Icon(
        Icons.edit_outlined,
        color: NetflixColors.white,
      ),
    ),
  ],
),

左に My Netaflix、右に編集アイコンを置いています。

My Netaflix                         編集アイコン

横並びなので、Row を使います。


Rowで横並びにする

Row は、子Widgetを横方向に並べるためのWidgetです。

Row(
  children: [
    Text(...),
    Spacer(),
    IconButton(...),
  ],
)

今回のように、左にタイトル、右にアイコンを置きたい場合に使います。

左側:タイトル
右側:アイコン

Column が縦並び、Row が横並びと覚えておくと分かりやすいです。


Textで画面タイトルを表示する

タイトルは、Text で表示しています。

const Text(
  'My Netaflix',
  style: TextStyle(
    color: NetflixColors.white,
    fontSize: 28,
    fontWeight: FontWeight.w900,
  ),
),

黒背景なので、文字色は白です。

color: NetflixColors.white,

タイトルなので、文字サイズは大きめの 28 にしています。

fontSize: 28,

太さも強めにしています。

fontWeight: FontWeight.w900,

画面の見出しは、他の文字よりも大きく・太くすると、画面構造が分かりやすくなります。


Spacerでタイトルとアイコンを左右に分ける

タイトルとアイコンの間には、Spacer があります。

const Spacer(),

Spacer は、空いているスペースを埋めるWidgetです。

これにより、タイトルは左に、編集アイコンは右に配置されます。

タイトル          空白          編集アイコン

Row の中で左右に分けたいときに、Spacer はとてもよく使います。


IconButtonで編集アイコンを置く

右側には、IconButton を置いています。

IconButton(
  onPressed: () {},
  icon: const Icon(
    Icons.edit_outlined,
    color: NetflixColors.white,
  ),
),

IconButton は、アイコンをボタンとして使うWidgetです。

ここでは、編集を表すアイコンを置いています。

Icons.edit_outlined

今回は、まだ編集機能は実装していません。

そのため、onPressed は空にしています。

onPressed: () {},

本格的に作る場合は、プロフィール編集画面へ移動する処理を入れます。


SizedBoxでタイトル下に余白を作る

タイトル行の下には、余白を入れています。

const SizedBox(height: 24),

SizedBox は、決まった大きさの空間を作るWidgetです。

タイトルのすぐ下にプロフィール画像が来ると、少し詰まって見えます。

タイトル
プロフィール画像

余白を入れると、見やすくなります。

タイトル

プロフィール画像

画面デザインでは、このような余白がとても大切です。


プロフィール部分を中央に配置する

プロフィール部分は、CenterColumn で作っています。

Center(
  child: Column(
    children: [
      CircleAvatar(
        radius: 44,
        backgroundImage: NetworkImage(profileImageUrl),
      ),
      const SizedBox(height: 10),
      const Text(
        'Netaflix User',
        style: TextStyle(
          color: NetflixColors.white,
          fontSize: 17,
          fontWeight: FontWeight.w800,
        ),
      ),
    ],
  ),
),

Center を使うことで、プロフィール画像と名前を画面中央に置いています。

      プロフィール画像
          User名

Myページでは、ユーザー自身の情報が中心になるので、中央配置にすると分かりやすくなります。


CircleAvatarでプロフィール画像を表示する

プロフィール画像は、CircleAvatar で表示しています。

CircleAvatar(
  radius: 44,
  backgroundImage: NetworkImage(profileImageUrl),
),

CircleAvatar は、丸いプロフィール画像を表示するためによく使われるWidgetです。

SNSやアカウント画面では、丸いアイコンがよく使われます。

丸いプロフィール画像

今回の画面でも、アカウント画面らしさを出すために CircleAvatar を使っています。


radiusでプロフィール画像の大きさを決める

CircleAvatar の大きさは、radius で決めています。

radius: 44,

radius は半径です。

つまり、直径は約88になります。

半径 44
↓
直径 88

大きすぎると画面を圧迫します。

小さすぎるとプロフィールらしさが弱くなります。

Myページの中央に置くプロフィール画像としては、ほどよいサイズです。


NetworkImageで画像URLを読み込む

プロフィール画像には、NetworkImage を使っています。

backgroundImage: NetworkImage(profileImageUrl),

profileImageUrl には、プロフィール画像のURLが入っています。

profileImageUrl

NetworkImage を使うと、インターネット上の画像を読み込んで表示できます。

画像URL
↓
NetworkImage
↓
CircleAvatarに表示

作品画像では Image.network を使いましたが、CircleAvatar の背景画像として使う場合は NetworkImage を指定します。


ユーザー名を表示する

プロフィール画像の下には、ユーザー名を表示しています。

const Text(
  'Netaflix User',
  style: TextStyle(
    color: NetflixColors.white,
    fontSize: 17,
    fontWeight: FontWeight.w800,
  ),
),

今回は教材用なので、固定で Netaflix User としています。

本格的なアプリでは、ログインしているユーザーの名前を表示します。

教材用
↓
固定のユーザー名

本格的なアプリ
↓
ログイン中のユーザー名

ここまでのまとめ

ここまでで、My Netaflix画面の上部とプロフィール部分を確認しました。

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

  • My Netaflix画面は、自分の情報や保存した作品を表示する画面。
  • MyNetflixPage は、状態を持たないので StatelessWidget で作れる。
  • movies.take(4).toList() で、先頭から4件の作品を取り出せる。
  • movies.skip(2).take(4).toList() で、途中から4件の作品を取り出せる。
  • Scaffold で画面の土台を作る。
  • SafeArea でノッチやステータスバーを避ける。
  • ListView を使うと、内容が多い画面でも縦スクロールできる。
  • padding の下余白を大きくすると、BottomNavigationと重なりにくくなる。
  • Row でタイトルと編集アイコンを横に並べる。
  • Spacer を使うと、タイトルを左、アイコンを右に分けられる。
  • IconButton は、アイコンをボタンとして使うWidget。
  • CenterColumn を使うと、プロフィール画像と名前を中央に並べられる。
  • CircleAvatar は、丸いプロフィール画像を表示するときに便利。
  • NetworkImage を使うと、URL画像を CircleAvatar に表示できる。

MyNetflixCardでメニューカードを作る

ここからは、プロフィール下に表示するメニューカードを見ていきます。

My Netaflix画面では、通知とダウンロードを次のように表示しています。

const MyNetflixCard(
  icon: Icons.notifications_none_rounded,
  title: 'Notifications',
  subtitle: 'Check your latest updates',
),
const SizedBox(height: 12),
const MyNetflixCard(
  icon: Icons.file_download_outlined,
  title: 'Downloads',
  subtitle: 'Watch your saved titles offline',
),

同じようなデザインのカードを2つ作るため、MyNetflixCard という部品にしています。

Notificationsカード
Downloadsカード
↓
同じ形なのでMyNetflixCardとして部品化する

部品化しておくと、あとで「Settings」や「Account」などのカードを増やすときにも使い回せます。


MyNetflixCardのコード

MyNetflixCard は、次のようなWidgetです。

class MyNetflixCard extends StatelessWidget {
  const MyNetflixCard({
    super.key,
    required this.icon,
    required this.title,
    required this.subtitle,
  });

  final IconData icon;
  final String title;
  final String subtitle;

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: NetflixColors.darkGray,
        borderRadius: BorderRadius.circular(14),
      ),
      padding: const EdgeInsets.all(16),
      child: Row(
        children: [
          Icon(
            icon,
            color: NetflixColors.white,
            size: 28,
          ),
          const SizedBox(width: 14),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  title,
                  style: const TextStyle(
                    color: NetflixColors.white,
                    fontSize: 15.5,
                    fontWeight: FontWeight.w900,
                  ),
                ),
                const SizedBox(height: 3),
                Text(
                  subtitle,
                  style: TextStyle(
                    color: NetflixColors.white.withValues(alpha: 0.58),
                    fontSize: 12.5,
                    fontWeight: FontWeight.w600,
                  ),
                ),
              ],
            ),
          ),
          const Icon(
            Icons.chevron_right_rounded,
            color: NetflixColors.white,
          ),
        ],
      ),
    );
  }
}

このカードは、左にアイコン、中央にタイトルと説明文、右に矢印を置いています。

[アイコン]  タイトル
          説明文                  >

requiredでカードごとの情報を受け取る

MyNetflixCard は、3つの値を受け取ります。

required this.icon,
required this.title,
required this.subtitle,
役割
icon左側に表示するアイコン
titleカードの見出し
subtitleカードの説明文

このようにしておくと、同じカードデザインのまま、中身だけ変えられます。

const MyNetflixCard(
  icon: Icons.notifications_none_rounded,
  title: 'Notifications',
  subtitle: 'Check your latest updates',
),
const MyNetflixCard(
  icon: Icons.file_download_outlined,
  title: 'Downloads',
  subtitle: 'Watch your saved titles offline',
),

同じ部品に違う値を渡すことで、違うカードとして表示できます。


Containerでカードの背景を作る

カード全体は、Container で作っています。

return Container(
  decoration: BoxDecoration(
    color: NetflixColors.darkGray,
    borderRadius: BorderRadius.circular(14),
  ),
  padding: const EdgeInsets.all(16),
  child: Row(
    ...
  ),
);

背景色は、少し明るい黒系の NetflixColors.darkGray です。

color: NetflixColors.darkGray,

黒背景の画面上に、少し明るいカードを置くことで、メニューとして認識しやすくなります。


borderRadiusで角丸にする

カードの角は、borderRadius で丸めています。

borderRadius: BorderRadius.circular(14),

検索結果カードでは 10 でしたが、My Netaflixのメニューカードでは少し大きめの 14 にしています。

アカウント画面のカードは、少し柔らかく見せると、メニューとして押しやすい印象になります。

角が四角い
↓
硬い印象

少し角丸
↓
ボタン・カードらしい印象

paddingでカード内の余白を作る

カードの中には、余白を入れています。

padding: const EdgeInsets.all(16),

左のアイコン、中央の文字、右の矢印がカードの端にくっつかないようにするためです。

余白があることで、タップできるメニューとして見やすくなります。

余白なし:窮屈
余白あり:押しやすそうに見える

Rowで横並びにする

カードの中身は、Row で横並びにしています。

child: Row(
  children: [
    Icon(...),
    const SizedBox(width: 14),
    Expanded(...),
    const Icon(Icons.chevron_right_rounded),
  ],
),

構造は次の通りです。

左:アイコン
中央:タイトル・説明文
右:矢印

メニューカードでは、この並びがよく使われます。

ユーザーは、右側の矢印を見ることで「押すと次の画面に進めそう」と感じます。


Iconで左側のアイコンを表示する

左側のアイコンは、受け取った icon を使って表示しています。

Icon(
  icon,
  color: NetflixColors.white,
  size: 28,
),

icon は、カードごとに違います。

通知カードなら、通知アイコンです。

Icons.notifications_none_rounded

ダウンロードカードなら、ダウンロードアイコンです。

Icons.file_download_outlined

同じ MyNetflixCard でも、アイコンを変えることで意味が変わります。


SizedBoxでアイコンと文字の間に余白を作る

アイコンと文字の間には、横方向の余白があります。

const SizedBox(width: 14),

この余白がないと、アイコンと文字がくっついて読みにくくなります。

アイコンタイトル

余白を入れると、自然なメニューカードになります。

アイコン  タイトル

Expandedで文字エリアを広げる

中央のタイトルと説明文は、Expanded で包まれています。

Expanded(
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Text(title),
      Text(subtitle),
    ],
  ),
),

Expanded を使うことで、左のアイコンと右の矢印以外の残りスペースを、文字エリアとして使えます。

左アイコン 固定
中央テキスト 可変
右矢印 固定

もし説明文が少し長くなっても、中央部分ができるだけ広く使われます。


タイトルと説明文を縦に並べる

中央部分では、Column を使っています。

Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Text(title),
    const SizedBox(height: 3),
    Text(subtitle),
  ],
)

タイトルを上、説明文を下に置いています。

Notifications
Check your latest updates

タイトルは強く、説明文は控えめに表示します。


タイトルを強く表示する

カードのタイトルは、次のように表示しています。

Text(
  title,
  style: const TextStyle(
    color: NetflixColors.white,
    fontSize: 15.5,
    fontWeight: FontWeight.w900,
  ),
),

タイトルはカードの主役なので、白く太く表示します。

Notifications
Downloads

ユーザーがぱっと見たときに、何のカードか分かるようにします。


説明文を控えめに表示する

説明文は、少し薄い白で表示しています。

Text(
  subtitle,
  style: TextStyle(
    color: NetflixColors.white.withValues(alpha: 0.58),
    fontSize: 12.5,
    fontWeight: FontWeight.w600,
  ),
),

説明文は補助情報です。

タイトルより目立ちすぎないように、透明度を下げています。

withValues(alpha: 0.58)

情報の重要度に合わせて文字の強さを変えると、画面が読みやすくなります。


chevron_rightで次に進める印象を作る

カードの右端には、矢印アイコンを置いています。

const Icon(
  Icons.chevron_right_rounded,
  color: NetflixColors.white,
),

chevron_right_rounded は、右向きの矢印のようなアイコンです。

これがあると、ユーザーは「このカードを押すと次に進めそう」と感じます。

今回は、まだ遷移先の画面は作っていません。

ただし、UIとしてはアカウント画面らしくなります。


Continue Watchingを表示する

メニューカードの下には、Continue Watching を表示しています。

const SizedBox(height: 28),
RowTitle(title: 'Continue Watching'),
const SizedBox(height: 12),
ContentRow(items: watchingMovies),

RowTitle は、Home画面でも使った見出し用のWidgetです。

Continue Watching
[作品] [作品] [作品]

ここでは、watchingMoviesContentRow に渡しています。

ContentRow(items: watchingMovies)

ContentRowを再利用する

ContentRow は、横スクロールの作品リストを表示するWidgetです。

Home画面でも使っていました。

My Netaflix画面でも同じWidgetを使っています。

ContentRow(items: watchingMovies)

同じ作品リストUIを何度も作らず、共通Widgetとして再利用しているのがポイントです。

Home画面
↓
ContentRowを使う

My Netaflix画面
↓
ContentRowを使う

これにより、画面ごとのコード量を減らせます。


My Listを表示する

さらに下には、My List を表示しています。

const SizedBox(height: 28),
RowTitle(title: 'My List'),
const SizedBox(height: 12),
ContentRow(items: myListMovies),

こちらは、myListMovies を表示しています。

ContentRow(items: myListMovies)

watchingMoviesmyListMovies は、どちらも movies から作った仮のリストです。

final watchingMovies = movies.take(4).toList();
final myListMovies = movies.skip(2).take(4).toList();

本格的なアプリでは、My List はユーザーが追加した作品を表示する場所になります。


画面全体の流れを整理する

My Netaflix画面の流れを整理すると、次のようになります。

MyNetflixPage
↓
Scaffoldで画面の土台を作る
↓
SafeAreaで端末の端を避ける
↓
ListViewで縦スクロール画面にする
↓
上部にタイトルと編集アイコンを表示
↓
中央にプロフィール画像とユーザー名を表示
↓
MyNetflixCardで通知・ダウンロードを表示
↓
Continue Watchingを表示
↓
My Listを表示

Myページは、機能としてはまだシンプルです。

しかし、プロフィール、メニュー、作品リストが入ることで、アプリの完成度が大きく上がります。


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

まずは、ユーザー名を変更してみましょう。

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

const Text(
  'Netaflix User',

これを好きな名前に変更します。

const Text(
  'Guest Profile',

または、日本語にしてもよいです。

const Text(
  'ゲストユーザー',

プロフィール画面らしさが変わります。


メニューカードを追加してみよう

NotificationsDownloads の下に、設定カードを追加してみましょう。

const SizedBox(height: 12),
const MyNetflixCard(
  icon: Icons.settings_outlined,
  title: 'Settings',
  subtitle: 'Manage app preferences',
),

これを Downloads の下に追加すると、メニューが3つになります。

Notifications
Downloads
Settings

MyNetflixCard を部品化しているので、カードの追加が簡単です。


Continue Watchingの件数を変えてみよう

現在は、先頭から4件を表示しています。

final watchingMovies = movies.take(4).toList();

これを3件にしたい場合は、次のようにします。

final watchingMovies = movies.take(3).toList();

逆に5件にしたい場合は、次のようにします。

final watchingMovies = movies.take(5).toList();

表示したい件数に合わせて、take の数字を変えます。


My Listの開始位置を変えてみよう

現在の My List は、先頭から2件飛ばして4件表示しています。

final myListMovies = movies.skip(2).take(4).toList();

開始位置を変えたい場合は、skip の数字を変えます。

final myListMovies = movies.skip(1).take(4).toList();
final myListMovies = movies.skip(3).take(4).toList();

skiptake を組み合わせると、同じ movies から違う範囲の作品を取り出せます。


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

Q. My Netaflix画面がスクロールできません。

画面全体が ListView になっているか確認してください。

ListView(
  padding: const EdgeInsets.fromLTRB(18, 18, 18, 110),
  children: [
    ...
  ],
)

Column だけで作ると、画面の高さに収まらない場合に表示がはみ出すことがあります。

内容が多い画面では、ListView を使うと安心です。


Q. BottomNavigationと内容が重なります。

ListView の下余白を確認してください。

padding: const EdgeInsets.fromLTRB(18, 18, 18, 110),

下の 110 が、BottomNavigationと重ならないための余白です。

重なる場合は、少し大きくしてもよいです。

padding: const EdgeInsets.fromLTRB(18, 18, 18, 130),

Q. プロフィール画像が表示されません。

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

backgroundImage: NetworkImage(profileImageUrl),

ネットワーク画像なので、通信環境によって表示に時間がかかることがあります。

画像URLが無効になっている場合も表示されません。


Q. メニューカードを押しても何も起きません。

今回の MyNetflixCard は、見た目だけのカードです。

まだ onTap は実装していません。

押したときに何かしたい場合は、GestureDetectorInkWell で包んで、onTap を追加します。

GestureDetector(
  onTap: () {
    // ここに処理を書く
  },
  child: MyNetflixCard(
    icon: Icons.settings_outlined,
    title: 'Settings',
    subtitle: 'Manage app preferences',
  ),
)

チャレンジ

チャレンジ1:ユーザー名を日本語にしよう

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

const Text(
  'Netaflix User',

これを次のように変更します。

const Text(
  'ゲストユーザー',

プロフィール名が日本語になるか確認してください。


チャレンジ2:Settingsカードを追加しよう

Downloads カードの下に、次のコードを追加してください。

const SizedBox(height: 12),
const MyNetflixCard(
  icon: Icons.settings_outlined,
  title: 'Settings',
  subtitle: 'Manage app preferences',
),

通知・ダウンロード・設定の3つのカードが並びます。


チャレンジ3:Continue Watchingを3件にしよう

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

final watchingMovies = movies.take(4).toList();

これを次のように変更します。

final watchingMovies = movies.take(3).toList();

Continue Watchingに表示される作品数が変わります。


チャレンジ4:My Listの作品範囲を変えよう

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

final myListMovies = movies.skip(2).take(4).toList();

これを次のように変更します。

final myListMovies = movies.skip(1).take(4).toList();

My Listに表示される作品が少し変わります。


チャレンジの答え

チャレンジ1の答え

変更前:

const Text(
  'Netaflix User',

変更後:

const Text(
  'ゲストユーザー',

プロフィール名が日本語になります。


チャレンジ2の答え

追加するコード:

const SizedBox(height: 12),
const MyNetflixCard(
  icon: Icons.settings_outlined,
  title: 'Settings',
  subtitle: 'Manage app preferences',
),

Downloads カードの下に追加します。


チャレンジ3の答え

変更前:

final watchingMovies = movies.take(4).toList();

変更後:

final watchingMovies = movies.take(3).toList();

Continue Watching に表示される作品数が3件になります。


チャレンジ4の答え

変更前:

final myListMovies = movies.skip(2).take(4).toList();

変更後:

final myListMovies = movies.skip(1).take(4).toList();

My List に表示される作品の範囲が変わります。


この節のまとめ

この節では、My Netaflix画面の後半として、通知・ダウンロードカード、視聴中リスト、My Listの表示を学びました。

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

  • MyNetflixCard を作ると、通知・ダウンロード・設定などのメニューカードを再利用できる。
  • required this.iconrequired this.titlerequired this.subtitle によって、カードごとに中身を変えられる。
  • ContainerBoxDecoration で、カードの背景色と角丸を作れる。
  • Row を使うと、アイコン・文字・矢印を横に並べられる。
  • Expanded を使うと、中央の文字エリアを自然に広げられる。
  • chevron_right_rounded を置くと、次の画面に進めるメニューらしく見える。
  • RowTitleContentRow を再利用すると、Home画面と同じ作品リストUIをMy画面にも表示できる。
  • movies.take(4).toList() で、先頭から4件の作品を取り出せる。
  • movies.skip(2).take(4).toList() で、途中から4件の作品を取り出せる。
  • Myページは、プロフィール・メニュー・作品リストをまとめることで、アプリ全体の完成度を高める画面になる。

次のステップ

次の節では、iOS用の Info.plist 設定を確認します。アプリ名、画面向き、YouTube表示に関わる設定など、FlutterアプリをiOSで動かすときに大切な項目を見ていきます。画面を作るだけでなく、実機やシミュレーターで正しく動かすための設定も、アプリ開発では重要です。

教材トップへ戻る