Kodokon kodokon.com

用 Navigator 在界面之间导航

用 Navigator.push 和 pop 压入、弹出界面,通过构造函数传递数据,并在界面关闭时取回一个结果。

8 分钟 · 3 题

在 Kodokon 中打开本课

Flutter 把你的界面当作一个来管理:Navigator.push 把新界面放到顶部,Navigator.pop 移除顶部的界面并显露出前一个。每个界面都是一条路由,而 MaterialPageRoute 会提供该平台原生的过渡动画。Android 的返回键会替你调用 pop,你一行代码都不用写。

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Home')),
      body: Center(
        child: FilledButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) {
                  return const DetailScreen(
                    productName: 'Headphones',
                  );
                },
              ),
            );
          },
          child: const Text('View product'),
        ),
      ),
    );
  }
}
push 把 DetailScreen(在下方定义)压到当前界面的上方。

要把数据传给下一个界面,并没有什么魔法机制:目标界面就是一个和其他 widget 一样的 widget,它通过构造函数接收自己的数据。这是有类型、明确的,而且如果漏了某个字段,编译器会当场抓住你。注意,被压入的界面的 AppBar 会自动显示返回键,你什么都不用写。

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

class DetailScreen extends StatelessWidget {
  const DetailScreen({
    super.key,
    required this.productName,
  });

  final String productName;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(productName)),
      body: Center(
        child: FilledButton(
          onPressed: () {
            Navigator.pop(context, true);
          },
          child: const Text('Add to cart'),
        ),
      ),
    );
  }
}
pop 关闭界面,并把一个值返回给调用方。

Navigator.push 会返回一个 Future,它在界面关闭时以传给 pop 的值完成。因此你可以等待一个界面的结果:一次确认、从列表中选中的一项、一份填好的表单。泛型 push<bool> 记录了你期望取回的内容。

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

Future<void> openDetail(BuildContext context) async {
  final added = await Navigator.push<bool>(
    context,
    MaterialPageRoute<bool>(
      builder: (context) {
        return const DetailScreen(
          productName: 'Headphones',
        );
      },
    ),
  );
  if (!context.mounted) return;
  final message = (added ?? false)
      ? 'Product added to cart'
      : 'No product added';
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(content: Text(message)),
  );
}
push 的 Future 会在界面关闭时完成。

知识检测

确认你已牢记本课的重点内容。

  1. Navigator 是如何组织界面的?
    • 像队列:先进的先显示
    • 像栈:最后压入的界面显示在最上面
    • 像一棵并行的 widget 树
    • 像一个由界面组成的网格
  2. 把数据传给下一个界面的地道做法是什么?
    • 通过一个全局变量
    • 通过目标 widget 的构造函数
    • 把它写到磁盘上
    • Navigator 不允许传递数据
  3. Navigator.pop(context, true) 会做什么?
    • 它会打开一个名为 "true" 的新界面
    • 它会关闭栈中的所有界面
    • 它会关闭当前界面,并把 true 返回给正在等待的 push