Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Navigate to a new screen and back (add stronger iOS support) #11776

Merged
merged 3 commits into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'package:flutter/cupertino.dart';

// #docregion first-second-routes
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: () {
// Navigate to second route when tapped.
},
),
),
);
}
}

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: () {
// Navigate back to first route when tapped.
},
child: const Text('Go back!'),
),
),
);
}
}
// #enddocregion first-second-routes
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'package:flutter/cupertino.dart';

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'),
// #docregion first-route-on-pressed
// Within the `FirstRoute` widget:
onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(builder: (context) => const SecondRoute()),
);
},
// #enddocregion first-route-on-pressed
),
),
);
}
}

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(
// #docregion second-route-on-pressed
// Within the SecondRoute widget
onPressed: () {
Navigator.pop(context);
},
// #enddocregion second-route-on-pressed
child: const Text('Go back!'),
),
),
);
}
}
2 changes: 1 addition & 1 deletion site-shared
132 changes: 97 additions & 35 deletions src/content/cookbook/navigation/navigation-basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ js:

<?code-excerpt path-base="cookbook/navigation/navigation_basics"?>

Most apps contain several screens for displaying different types of
information.
For example, an app might have a screen that displays products.
When the user taps the image of a product, a new screen displays
details about the product.
Most apps contain several screens for displaying different
types of information. For example, an app might have a
screen that displays products. When the user taps the image
of a product, a new screen displays details about the
product.

:::note Terminology
In Flutter, _screens_ and _pages_ are called _routes_.
Expand All @@ -29,8 +29,8 @@ The next few sections show how to navigate between two routes,
using these steps:

1. Create two routes.
2. Navigate to the second route using Navigator.push().
3. Return to the first route using Navigator.pop().
2. Navigate to the second route using `Navigator.push()`.
3. Return to the first route using `Navigator.pop()`.

## 1. Create two routes

Expand All @@ -41,6 +41,10 @@ second route returns to the first route.

First, set up the visual structure:

{% tabs "os-android" %}

{% tab "Android" %}

<?code-excerpt "lib/main_step1.dart (first-second-routes)"?>
```dart
class FirstRoute extends StatelessWidget {
Expand Down Expand Up @@ -82,18 +86,72 @@ class SecondRoute extends StatelessWidget {
}
```

{% endtab %}

{% tab "iOS" %}

<?code-excerpt "lib/main_step1_cupertino.dart (first-second-routes)"?>
```dart
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: () {
// Navigate to second route when tapped.
},
),
),
);
}
}

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: () {
// Navigate back to first route when tapped.
},
child: const Text('Go back!'),
),
),
);
}
}
```

{% endtab %}

{% endtabs %}

## 2. Navigate to the second route using Navigator.push()

To switch to a new route, use the [`Navigator.push()`][]
method. The `push()` method adds a `Route` to the stack of routes managed by
the `Navigator`. Where does the `Route` come from?
You can create your own, or use a [`MaterialPageRoute`][],
which is useful because it transitions to the
new route using a platform-specific animation.
You can create your own, or use a platform-specific route
such as [`MaterialPageRoute`][] or [`CupertinoPageRoute`][].
A platform-specific route is useful because it transitions
to the new route using a platform-specific animation.

In the `build()` method of the `FirstRoute` widget,
update the `onPressed()` callback:

{% tabs "os-android" %}

{% tab "Android" %}

<?code-excerpt "lib/main_step2.dart (first-route-on-pressed)" replace="/^\},$/}/g"?>
```dart
// Within the `FirstRoute` widget:
Expand All @@ -105,6 +163,25 @@ onPressed: () {
}
```

{% endtab %}

{% tab "iOS" %}

<?code-excerpt "lib/main_step2_cupertino.dart (first-route-on-pressed)" replace="/^\},$/}/g"?>
```dart
// Within the `FirstRoute` widget:
onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(builder: (context) => const SecondRoute()),
);
}
```

{% endtab %}

{% endtabs %}

## 3. Return to the first route using Navigator.pop()

How do you close the second route and return to the first?
Expand All @@ -125,6 +202,10 @@ onPressed: () {

## Interactive example

{% tabs "os-android" %}

{% tab "Android" %}

<?code-excerpt "lib/main.dart"?>
```dartpad title="Flutter navigation hands-on example in DartPad" run="true"
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -179,32 +260,9 @@ class SecondRoute extends StatelessWidget {
<img src="/assets/images/docs/cookbook/navigation-basics.gif" alt="Navigation Basics Demo" class="site-mobile-screenshot" />
</noscript>

## Navigation with CupertinoPageRoute
{% endtab %}

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:

- [`MaterialApp`][] replaced by [`CupertinoApp`].
- [`Scaffold`][] replaced by [`CupertinoPageScaffold`][].
- [`ElevatedButton`][] replaced by [`CupertinoButton`][].

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

:::secondary
You don't need to replace all Material widgets with Cupertino versions
to use [`CupertinoPageRoute`][]
since Flutter allows you to mix and match Material and Cupertino widgets
depending on your needs.
:::
{% tab "iOS" %}

<?code-excerpt "lib/main_cupertino.dart"?>
```dartpad title="Flutter Cupertino theme hands-on example in DartPad" run="true"
Expand Down Expand Up @@ -260,6 +318,10 @@ class SecondRoute extends StatelessWidget {
<img src="/assets/images/docs/cookbook/navigation-basics-cupertino.gif" alt="Navigation Basics Cupertino Demo" class="site-mobile-screenshot" />
</noscript>

{% endtab %}

{% endtabs %}

## Additional navigation methods

The recipe in this topic shows you one way to navigate to a new screen and
Expand Down
Loading