🚪

次のページに値を渡す

2022/08/30に公開

詳細画面に値を渡すには?

ListView.builderの公式ドキュメントを見ていたら、見つけたのですが、Classを型として、Listに格納して、画面に表示して、タップしたら、1だと1の詳細ページへ移動して、2だと2の詳細ページへ移動する仕組みをやっと理解できました😅
英語のコメントを翻訳して、このコードがどんな役割なのかも調べました。

https://docs.flutter.dev/cookbook/navigation/passing-data

コードは少ないですが、モデルと画面に分離したいので、分けました。

model/todo_model.dart

// モデルとなるクラスを作成する。
class Todo {
  final String title;
  final String description;

  const Todo(this.title, this.description);
}

screen/detail_screen.dart

import 'package:flutter/material.dart';
import 'package:list_app/model/todo_model.dart';

class DetailScreen extends StatelessWidget {
  // コンストラクタで、Todoを要求します。
  const DetailScreen({super.key, required this.todo});

  // Todoを保持するフィールドを宣言する。
  final Todo todo;

  
  Widget build(BuildContext context) {
    // Todoを使用してUIを作成します。
    return Scaffold(
      appBar: AppBar(
        title: Text(todo.title),
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Text(todo.description),
        ),
      ),
    );
  }
}

main.dart

import 'package:flutter/material.dart';
import 'package:list_app/model/todo_model.dart';
import 'package:list_app/screen/detail_screen.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Passing Data',
      home: TodosScreen(
        todos: List.generate(
          20,
          (i) => Todo(
            'Todo $i',
            'Todoのために必要な説明文 $i',
          ),
        ),
      ),
    );
  }
}

class TodosScreen extends StatelessWidget {
  const TodosScreen({super.key, required this.todos});
  // クラスを型として使う
  final List<Todo> todos;

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Todos'),
      ),
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(todos[index].title),
            // ユーザーがListTileをタップすると、DetailScreenに遷移する。
            // DetailScreenを作成するだけでなく、次のことに注意してください。
            // また、現在のTodoもそれに渡します。
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => DetailScreen(todo: todos[index]),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

やってみた感想

コンストラクターなるものをどうやって使うのか、公式ドキュメントを見て理解できた気がします。まずは公式ドキュメントを見てから、YouTubeやUdemyで勉強した方がいいのでしょうね。

おまけ

Freezedを使うとどうなるのか、試してみたくてやってみた。引数を追加しないとエラーが出てくる以外は特に変更はなさそうですね。
書き方が独特になっただけですね。

Freezedに変更
todo_model.dart

import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';

part 'todo_model.freezed.dart';
part 'todo_model.g.dart';


class Todo with _$Todo {
  const factory Todo({
    required String title,
    required String description,
  }) = _Todo;

  factory Todo.fromJson(Map<String, dynamic> json) =>
      _$TodoFromJson(json);
}

main.dart

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Passing Data',
      home: TodosScreen(
        todos: List.generate(
          20,
          (i) => Todo(description: 'Todo$i', title: 'Todoのために必要な説明文$i'),
        ),
      ),
    );
  }
}

Discussion