Skip to content

Commit

Permalink
Navigate to a new screen and back (add stronger iOS support) (#11776)
Browse files Browse the repository at this point in the history
_Description of what this PR is changing or adding, and why:_

* Merged the iOS topic at the bottom of the guide into the existing
recipe so that a developer can quickly toggle between Android and iOS
steps.

* Added additional code snippet tests for the iOS samples.

_Issues fixed by this PR (if any):_

_PRs or commits this PR depends on (if any):_

## Presubmit checklist

- [x] This PR is marked as draft with an explanation if not meant to
land until a future stable release.
- [x] This PR doesn’t contain automatically generated corrections
(Grammarly or similar).
- [x] This PR follows the [Google Developer Documentation Style
Guidelines](https://developers.google.com/style) — for example, it
doesn’t use _i.e._ or _e.g._, and it avoids _I_ and _we_ (first person).
- [x] This PR uses [semantic line
breaks](https://github.com/dart-lang/site-shared/blob/main/doc/writing-for-dart-and-flutter-websites.md#semantic-line-breaks)
of 80 characters or fewer.
  • Loading branch information
antfitch authored Mar 5, 2025
1 parent 5c3e68a commit 32ec393
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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!'),
),
),
);
}
}
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

0 comments on commit 32ec393

Please sign in to comment.