导航到一个新页面和返回

我们通常会用“屏”来表示应用的不同页面(界面)。比如,某个应用有一“屏”展示商品列表,当用户点击某个商品的图片,会跳到新的一“屏”展示商品的详细信息。

在 Android 开发中,Activity 相当于“路由”,在 iOS 开发中,ViewController 相当于“路由”。在 Flutter 中,“路由”也是一个 widget。

怎么样从一个“路由”跳转到新的“路由”呢?Navigator 类。

这个教程里我们使用 Navigator 来跳转到一个新的“路由”:

步骤

下面来展示如何在两个路由间跳转,总共分三步:

  1. 创建两个路由

  2. 用 Navigator.push() 跳转到第二个路由

  3. 用 Navigator.pop() 回退到第一个路由

1. 创建两个路由

首先,我们来创建两个路由。这是个最简单的例子,每个路由只包含一个按钮。点击第一个路由上的按钮会跳转到第二个路由,点击第二个路由上的按钮,会回退到第一个路由。

首先来编写界面布局代码:

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('First Route'),
      ),
      body: Center(
        child: ElevatedButton(
          child: const Text('Open route'),
          onPressed: () {
            // Navigate to second route when tapped.
          },
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Second Route'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // Navigate back to first route when tapped.
          },
          child: const Text('Go back!'),
        ),
      ),
    );
  }
}

2. 用 Navigator.push() 跳转到第二个路由

使用 Navigator.push() 方法跳转到新的路由, push() 方法会添加一个 Route 对象到导航器的堆栈上。那么这个 Route 对象是从哪里来的呢?你可以自己实现一个,或者直接使用 MaterialPageRoute 类。使用 MaterialPageRoute 是非常方便的,框架已经为我们实现了和平台原生类似的切换动画。

FirstRoute widget 的 build() 方法中,我们来修改 onPressed() 回调函数:

// Within the `FirstRoute` widget
onPressed: () {
  Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => const SecondRoute()),
  );
}

3. 用 Navigator.pop() 回退到第一个路由

怎么关闭第二个路由回退到第一个呢? 使用 Navigator.pop() 方法, pop() 方法会从导航器堆栈上移除 Route 对象。

实现返回第一个页面,更新 SecondRoute widget 的 onPressed() 方法回调。

// Within the SecondRoute widget
onPressed: () {
  Navigator.pop(context);
}

交互式样例

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(
    title: 'Navigation Basics',
    home: FirstRoute(),
  ));
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('First Route'),
      ),
      body: Center(
        child: ElevatedButton(
          child: const Text('Open route'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => const SecondRoute()),
            );
          },
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Second Route'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: const Text('Go back!'),
        ),
      ),
    );
  }
}

In the previous example you learned how to navigate between screens using the MaterialPageRoute from Material Components. However, in Flutter you are not limited to Material design language, instead, you also have access to Cupertino (iOS-style) widgets.

Implementing navigation with Cupertino widgets follows the same steps as when using MaterialPageRoute, but instead you use CupertinoPageRoute which provides an iOS-style transition animation.

In the following example, these widgets have been replaced:

This way, the example follows the current iOS design language.

import 'package:flutter/cupertino.dart';

void main() {
  runApp(const CupertinoApp(
    title: 'Navigation Basics',
    home: FirstRoute(),
  ));
}

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

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: const CupertinoNavigationBar(
        middle: Text('First Route'),
      ),
      child: Center(
        child: CupertinoButton(
          child: const Text('Open route'),
          onPressed: () {
            Navigator.push(
              context,
              CupertinoPageRoute(builder: (context) => const SecondRoute()),
            );
          },
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: const CupertinoNavigationBar(
        middle: Text('Second Route'),
      ),
      child: Center(
        child: CupertinoButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: const Text('Go back!'),
        ),
      ),
    );
  }
}