準備と画面の設計
Flutter入門のWidgetとListViewまでを使います。study_appのlib/main.dartを次の完成例で全体ごと置き換え、flutter run -d chrome で実行してください。外部パッケージは不要です。一覧と詳細を別のWidgetに分け、選んだ講座名を渡します。
一覧と詳細の完成例
Navigatorは画面の履歴を管理します。push で新しい画面を上に積み、pop で取り除いて戻ります。MaterialPageRouteのbuilderは移動先のWidgetを作る関数です。
import 'package:flutter/material.dart';
void main() => runApp(const StudyApp());
class StudyApp extends StatelessWidget {
const StudyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: LessonListPage());
}
}
class LessonListPage extends StatelessWidget {
const LessonListPage({super.key});
@override
Widget build(BuildContext context) {
const lessons = ['変数', '関数', 'Widget'];
return Scaffold(
appBar: AppBar(title: const Text('学習一覧')),
body: ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: lessons.length,
itemBuilder: (context, index) {
final title = lessons[index];
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
child: ListTile(
title: Text(title),
subtitle: const Text('タップして目標を確認'),
onTap: () {
Navigator.of(context).push<void>(
MaterialPageRoute<void>(
builder: (context) => DetailPage(title: title),
),
);
},
),
);
},
),
);
}
}
class DetailPage extends StatelessWidget {
final String title;
const DetailPage({super.key, required this.title});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${title}の例を1つ動かしましょう'),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () { Navigator.of(context).pop(); },
child: const Text('一覧へ戻る'),
),
],
),
),
);
}
}最初は3件の学習一覧
「関数」をタップするとタイトルが「関数」の詳細画面
「一覧へ戻る」で元の3件の一覧へ戻るBuildContextと値の受け渡し
Navigator.of(context)は、そのcontextより上にあるNavigatorを探します。完成例ではMaterialAppの下にあるLessonListPageのcontextを使っています。MaterialAppを作っているStudyApp自身のcontextでは、そのMaterialAppのNavigatorを見つけられません。
DetailPageの required this.title に講座名を渡します。値を直接渡すことで、詳細画面が一覧の内部変数へ依存せずに表示できます。まず「変数」「Widget」も開き、選択した値が変わることを確かめます。
「変数」を選ぶ → 変数の例を1つ動かしましょう
「Widget」を選ぶ → Widgetの例を1つ動かしましょう幅と高さの制約
Flutterの親Widgetは子に幅・高さの制約を渡します。Columnの中にListViewを置く場合は、残りの高さをExpandedで渡します。完成例のLessonListPageのbodyを次に置き換えて試せます。これはレイアウト確認用なので、行のタップ処理は含めていません。
Column(
children: [
const Padding(
padding: EdgeInsets.all(16),
child: Text('上から順に学びましょう'),
),
Expanded(
child: ListView.builder(
itemCount: lessons.length,
itemBuilder: (context, index) {
return ListTile(title: Text(lessons[index]));
},
),
),
],
)案内文が上に固定され、その下の一覧が残りの高さを使う練習:4件目を追加
完成例へ戻し、一覧に「状態管理」を追加してください。詳細画面にも選んだ名前が表示されることを確認します。
解答例を確認する
const lessons = ['変数', '関数', 'Widget', '状態管理'];4件の一覧が表示される
「状態管理」を開くと「状態管理の例を1つ動かしましょう」
「一覧へ戻る」で4件の一覧へ戻るWebのURLとの連動や深いリンクが必要なアプリでは、Routerやルーティングパッケージを学びます。このページは画面遷移の基礎として、アプリ内のpush/popを扱いました。
