将数据发送到新屏幕

通常,您不仅希望导航到新屏幕,还希望将数据传递到该屏幕。例如,您可能希望传递有关已点击项目的的信息。

记住:屏幕只是小部件。在本例中,创建一个待办事项列表。当点击待办事项时,导航到一个新的屏幕(小部件),该屏幕显示有关待办事项的信息。此食谱使用以下步骤

  1. 定义一个待办事项类。
  2. 显示待办事项列表。
  3. 创建一个可以显示有关待办事项信息的详细信息屏幕。
  4. 导航并将数据传递到详细信息屏幕。

1. 定义一个 todo 类

#

首先,您需要一种简单的方法来表示待办事项。在本例中,创建一个包含两部分数据的类:标题和描述。

dart
class Todo {
  final String title;
  final String description;

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

2. 创建一个 todo 列表

#

其次,显示待办事项列表。在本例中,生成 20 个待办事项,并使用 ListView 显示它们。有关使用列表的更多信息,请参阅使用列表食谱。

生成 todo 列表

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

使用 ListView 显示 todo 列表

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

到目前为止,一切都很好。这将生成 20 个待办事项并在 ListView 中显示它们。

3. 创建一个 Todo 屏幕来显示列表

#

为此,我们创建一个StatelessWidget。我们将其称为TodosScreen。由于此页面的内容在运行时不会改变,因此我们必须在该小部件的范围内要求待办事项列表。

我们将ListView.builder作为要返回给build()的小部件的主体传递。这将为您渲染屏幕上的列表,让您开始使用!

dart
class TodosScreen extends StatelessWidget {
  // Requiring the list of todos.
  const TodosScreen({super.key, required this.todos});

  final List<Todo> todos;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const 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. 创建一个详细信息屏幕来显示有关 todo 的信息

#

现在,创建第二个屏幕。屏幕的标题包含待办事项的标题,屏幕的主体显示描述。

由于详细信息屏幕是一个普通的StatelessWidget,因此要求用户在 UI 中输入一个Todo。然后,使用给定的待办事项构建 UI。

dart
class DetailScreen extends StatelessWidget {
  // In the constructor, require a Todo.
  const DetailScreen({super.key, required this.todo});

  // Declare a field that holds the Todo.
  final Todo todo;

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

5. 导航并传递数据到详细信息屏幕

#

有了 DetailScreen,您就可以执行导航操作。在本例中,当用户点击列表中的待办事项时,导航到 DetailScreen。将待办事项传递给 DetailScreen

为了捕获用户在 TodosScreen 中的点击,请为 ListTile 小部件编写一个 onTap() 回调。在 onTap() 回调中,使用 Navigator.push() 方法。

dart
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]),
          ),
        );
      },
    );
  },
),

交互式示例

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

class Todo {
  final String title;
  final String description;

  const 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 {
  const TodosScreen({super.key, required this.todos});

  final List<Todo> todos;

  @override
  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),
            // 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 {
  // In the constructor, require a Todo.
  const DetailScreen({super.key, required this.todo});

  // Declare a field that holds the Todo.
  final Todo todo;

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

或者,使用 RouteSettings 传递参数

#

重复前两个步骤。

创建一个详细信息屏幕来提取参数

#

接下来,创建一个详细屏幕,从 Todo 中提取并显示标题和描述。要访问 Todo,请使用 ModalRoute.of() 方法。此方法返回带有参数的当前路由。

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

  @override
  Widget build(BuildContext context) {
    final todo = ModalRoute.of(context)!.settings.arguments as Todo;

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

最后,当用户点击 ListTile 小部件时,使用 Navigator.push() 导航到 DetailScreen。将参数作为 RouteSettings 的一部分传递。DetailScreen 提取这些参数。

dart
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) => const DetailScreen(),
            // Pass the arguments as part of the RouteSettings. The
            // DetailScreen reads the arguments from these settings.
            settings: RouteSettings(
              arguments: todos[index],
            ),
          ),
        );
      },
    );
  },
)

完整示例

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

class Todo {
  final String title;
  final String description;

  const 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 {
  const TodosScreen({super.key, required this.todos});

  final List<Todo> todos;

  @override
  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),
            // 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) => const 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 {
  const DetailScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final todo = ModalRoute.of(context)!.settings.arguments as Todo;

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