2021/3/13 : Flutter : Send data to a new screenの訳

まとめ

Navigator.pushメソッドでページ遷移する場合、第2引数で指定するウィジェットのコンストラクタに渡したいものを渡せば、ページ遷移時の引数渡しができる。

↑でできるのだが、この方法だと遷移先クラス(DetailScreen)内で受け取ったオブジェクトを保持するフィールド(todo)を宣言する必要がある。

それをしたくない場合は、遷移元でNavigator.pushメソッド内で渡したいものを指定して、遷移先ではModalRoute.ofを使って受け取る方法もある。

named routeでの渡し方についてはこちら。

named routeの場合も全体的にはやり方は似ているが、微妙に違うので整理する必要がある。


Send data to a new screen

多くの場合、新しい画面に移動するだけでなく、データを画面に渡したい場合もあります。

たとえば、タップされたアイテムに関する情報を(遷移先ページへ)渡したい場合があります。

思い出してください。:画面は単なるウィジェットです。

この例では、ToDoのリストを作成します。ToDoリストのアイテムをタップすると、ToDoに関する情報を表示する新しい画面(ウィジェット)に移動します。このレシピでは、次の手順を使用します。

  1. todoクラスを定義します。
  2. ToDoのリストを表示します。
  3. ToDoに関する情報を表示できる詳細画面を作成します。
  4. ナビゲートして、詳細画面にデータを渡します。

1. Define a todo class

まず、ToDoを表す簡単な方法が必要です。この例では、タイトル(title)と説明(description)の2つのデータを含むクラスを作成します。

class Todo {
  final String title;
  final String description;

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

 




2. Create a list of todos

次に、ToDoのリストを表示します。この例では、20個のToDoを生成し、ListViewを使用してそれらを表示します。リストの操作の詳細については、Use listsレシピを参照してください。


Generate the list of todos

final todos = List<Todo>.generate(
  20,
  (i) => Todo(
    'Todo $i',
    'A description of what needs to be done for Todo $i',
  ),
);

 


Display the list of todos using a ListView

ListView.builder(
  itemCount: todos.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(todos[index].title),
    );
  },
);

ここまでは順調ですね。これにより、20個のToDoが生成され、ListViewに表示されます




3. Create a Todo screen to display the list

このために、StatelessWidgetを作成ます。私たちはそれをTodosScreenと呼びますこのページのコンテンツは実行時に変更されないため、このウィジェットのスコープ内にToDoのリストを設置する必要があります。

ScaffoldウィジェットのbodyプロパティにListView.builderを設置します。

これにより、リストが画面に表示され、作業を開始できます。

class TodosScreen extends StatelessWidget {
  final List<Todo> todos;

  //requiring the list of todos
  TodosScreen({Key key, @required this.todos}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Todos'),
      ),
      //passing in the ListView.builder
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(todos[index].title)
          );
        },
      ),
    );
  }
}

Flutterのデフォルトのスタイリングを使用すると、後でやりたいことについて汗を流さずに済ませることができます。


4. Create a detail screen to display information about a todo

次に、2番目の画面(DetailScreen)を作成します。

画面のタイトルにはToDoのタイトルが含まれ、画面の本体には説明(description)が表示されます。

DetailScreenは通常のStatelessWidgetですから、UIの中にTodoを保持する必要が有ります。そして与えられたtodoを使ってUIを構築します。

class DetailScreen extends StatelessWidget {
  // ↓Todoを保持するフィールドを宣言します。
  final Todo todo;

  // コンストラクタの引数にtodoが必要です。
  DetailScreen({Key key, @required this.todo}) : super(key: key);

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

 


5. Navigate and pass data to the detail screen

DetailScreenを定義しましたので、ナビゲーションを実行する準備が整いました。

このサンプルでは、ユーザーがリストのTodoアイテムをタップした時にDetailScreenに遷移するようにします。その時にtodoアイテム(Todoインスタンス)をDetailScreenに渡します。

TodoScreenでユーザーのタップを察知するために、ListTileウィジェットのonTapプロパティにコールバック(無名関数)を設定します。

onTapコールバック内でNavigator.push()メソッドを使います。

ListView.builder(
  itemCount: todos.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(todos[index].title),
      // When a user taps the ListTile, navigate to the DetailScreen.
      // Notice that you're not only creating a DetailScreen, you're
      // also passing the current todo to it.
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => DetailScreen(todo: todos[index]),
          ),
        );
      },
    );
  },
);

 


Interactive example

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

class Todo {
  final String title;
  final String description;

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

void main() {
  runApp(MaterialApp(
    title: 'Passing Data',
    home: TodosScreen(
      todos: List.generate(
        20,
        (i) => Todo(
          'Todo $i',
          'A description of what needs to be done for Todo $i',
        ),
      ),
    ),
  ));
}

class TodosScreen extends StatelessWidget {
  final List<Todo> todos;

  TodosScreen({Key key, @required this.todos}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Todos'),
      ),
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(todos[index].title),
            // When a user taps the ListTile, navigate to the DetailScreen.
            // Notice that you're not only creating a DetailScreen, you're
            // also passing the current todo through to it.
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => DetailScreen(todo: todos[index]),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  // Declare a field that holds the Todo.
  final Todo todo;

  // In the constructor, require a Todo.
  DetailScreen({Key key, @required this.todo}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // Use the Todo to create the UI.
    return Scaffold(
      appBar: AppBar(
        title: Text(todo.title),
      ),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Text(todo.description),
      ),
    );
  }
}

 




Alternatively, pass the arguments using RouteSettings

最初の二つのステップは上記と全く同じ。


Create a detail screen to extract the arguments

次に(遷移先の)DetailScreenのbuildメソッド内で、遷移元から受け取ったTodoにアクセスして表示する必要があります。

TodoにアクセスするためにはModalRoute.of()メソッドを使います。

このメソッドは現在のrouteと、遷移元から受け取ったargumentsを返します。

class DetailScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final Todo todo = ModalRoute.of(context).settings.arguments;

    // Use the Todo to create the UI.
    return Scaffold(
      appBar: AppBar(
        title: Text(todo.title),
      ),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Text(todo.description),
      ),
    );
  }
}

 


最後にユーザーがListTileウィジェットをタップした時に、Navigator.push()メソッドを呼び出してDetailScreenへページ遷移します。

下記のコードのように、RouteSettingsの一部としてarguments(渡したいもの、つまりTodoアイテム)を渡します。

一つ前のステップで示したように、DetailScreenは(ModalRoute.ofを使って)渡されたargumentsを受け取って、表示します。

ListView.builder(
  itemCount: todos.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(todos[index].title),
      // When a user taps the ListTile, navigate to the DetailScreen.
      // Notice that you're not only creating a DetailScreen, you're
      // also passing the current todo through to it.
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => DetailScreen(),
            // Pass the arguments as part of the RouteSettings. The
            // DetailScreen reads the arguments from these settings.
            settings: RouteSettings(
              arguments: todos[index],
            ),
          ),
        );
      },
    );
  },
),

 


Complete example

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

class Todo {
  final String title;
  final String description;

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

void main() {
  runApp(MaterialApp(
    title: 'Passing Data',
    home: TodosScreen(
      todos: List.generate(
        20,
        (i) => Todo(
          'Todo $i',
          'A description of what needs to be done for Todo $i',
        ),
      ),
    ),
  ));
}

class TodosScreen extends StatelessWidget {
  final List<Todo> todos;

  TodosScreen({Key key, @required this.todos}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Todos'),
      ),
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(todos[index].title),
            // When a user taps the ListTile, navigate to the DetailScreen.
            // Notice that you're not only creating a DetailScreen, you're
            // also passing the current todo through to it.
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => DetailScreen(),
                  // Pass the arguments as part of the RouteSettings. The
                  // DetailScreen reads the arguments from these settings.
                  settings: RouteSettings(
                    arguments: todos[index],
                  ),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final Todo todo = ModalRoute.of(context).settings.arguments;

    // Use the Todo to create the UI.
    return Scaffold(
      appBar: AppBar(
        title: Text(todo.title),
      ),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Text(todo.description),
      ),
    );
  }
}

 

参考

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

コメントを残す

メールアドレスが公開されることはありません。