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

[go_router] Refactored RouteMatchList and imperative APIs #5497

Merged
merged 9 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions packages/go_router/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 13.0.0

- Refactors `RouteMatchList` and imperative APIs.
- **BREAKING CHANGE**:
- RouteMatchList structure changed.
- Matching logic updated.

## 12.1.3

* Fixes a typo in `navigation.md`.
Expand Down
2 changes: 1 addition & 1 deletion packages/go_router/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ See the API documentation for details on the following topics:
- [Error handling](https://pub.dev/documentation/go_router/latest/topics/Error%20handling-topic.html)

## Migration Guides
- [Migrating to 13.0.0](https://flutter.dev/go/go-router-v13-breaking-changes).
- [Migrating to 12.0.0](https://flutter.dev/go/go-router-v12-breaking-changes).
- [Migrating to 11.0.0](https://flutter.dev/go/go-router-v11-breaking-changes).
- [Migrating to 10.0.0](https://flutter.dev/go/go-router-v10-breaking-changes).
Expand Down Expand Up @@ -67,4 +68,3 @@ The project follows the same priority system as flutter framework.
[P3](https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc+label%3Ateam-go_router+label%3AP3+)

[Package PRs](https://github.com/flutter/packages/pulls?q=is%3Apr+is%3Aopen+label%3A%22p%3A+go_router%22%2C%22p%3A+go_router_builder%22)

21 changes: 21 additions & 0 deletions packages/go_router/doc/navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,27 @@ Navigator.of(context).push(
);
```

The behavior may change depends on the shell route in current screen and the new screen.

If pushing a new screen without any shell route onto the current screen with shell route, the new
screen is placed entirely on top of the current screen.

![An animation shows a new screen push on top of current screen](https://flutter.github.io/assets-for-api-docs/assets/go_router/push_regular_route.gif)

If pushing a new screen with the same shell route as the current screen, the new
screen is placed inside of the shell.

![An animation shows pushing a new screen with the same shell as current screen](https://flutter.github.io/assets-for-api-docs/assets/go_router/push_same_shell.gif)

If pushing a new screen with the different shell route as the current screen, the new
screen along with the shell is placed entirely on top of the current screen.

![An animation shows pushing a new screen with the different shell as current screen](https://flutter.github.io/assets-for-api-docs/assets/go_router/push_different_shell.gif)

To try out the behavior yourself, see
[push_with_shell_route.dart](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/extra_codec.dart).


## Returning values
Waiting for a value to be returned:

Expand Down
160 changes: 160 additions & 0 deletions packages/go_router/example/lib/push_with_shell_route.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

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

// This scenario demonstrates the behavior when pushing ShellRoute in various
// scenario.
//
// This example have three routes, /shell1, /shell2, and /regular-route. The
// /shell1 and /shell2 are nested in different ShellRoutes. The /regular-route
// is a simple GoRoute.

void main() {
runApp(PushWithShellRouteExampleApp());
}

/// An example demonstrating how to use [ShellRoute]
class PushWithShellRouteExampleApp extends StatelessWidget {
/// Creates a [PushWithShellRouteExampleApp]
PushWithShellRouteExampleApp({super.key});

final GoRouter _router = GoRouter(
initialLocation: '/home',
debugLogDiagnostics: true,
routes: <RouteBase>[
ShellRoute(
builder: (BuildContext context, GoRouterState state, Widget child) {
return ScaffoldForShell1(child: child);
},
routes: <RouteBase>[
GoRoute(
path: '/home',
builder: (BuildContext context, GoRouterState state) {
return const Home();
},
),
GoRoute(
path: '/shell1',
pageBuilder: (_, __) => const NoTransitionPage<void>(
child: Center(
child: Text('shell1 body'),
),
),
),
],
),
ShellRoute(
builder: (BuildContext context, GoRouterState state, Widget child) {
return ScaffoldForShell2(child: child);
},
routes: <RouteBase>[
GoRoute(
path: '/shell2',
builder: (BuildContext context, GoRouterState state) {
return const Center(child: Text('shell2 body'));
},
),
],
),
GoRoute(
path: '/regular-route',
builder: (BuildContext context, GoRouterState state) {
return const Scaffold(
body: Center(child: Text('regular route')),
);
},
),
],
);

@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routerConfig: _router,
);
}
}

/// Builds the "shell" for /shell1
class ScaffoldForShell1 extends StatelessWidget {
/// Constructs an [ScaffoldForShell1].
const ScaffoldForShell1({
required this.child,
super.key,
});

/// The widget to display in the body of the Scaffold.
/// In this sample, it is a Navigator.
final Widget child;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('shell1')),
body: child,
);
}
}

/// Builds the "shell" for /shell1
class ScaffoldForShell2 extends StatelessWidget {
/// Constructs an [ScaffoldForShell1].
const ScaffoldForShell2({
required this.child,
super.key,
});

/// The widget to display in the body of the Scaffold.
/// In this sample, it is a Navigator.
final Widget child;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('shell2')),
body: child,
);
}
}

/// The screen for /home
class Home extends StatelessWidget {
/// Constructs a [Home] widget.
const Home({super.key});

@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextButton(
onPressed: () {
GoRouter.of(context).push('/shell1');
},
child: const Text('push the same shell route /shell1'),
),
TextButton(
onPressed: () {
GoRouter.of(context).push('/shell2');
},
child: const Text('push the different shell route /shell2'),
),
TextButton(
onPressed: () {
GoRouter.of(context).push('/regular-route');
},
child: const Text('push the regular route /regular-route'),
),
],
),
);
}
}
45 changes: 45 additions & 0 deletions packages/go_router/example/test/push_with_shell_route_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter_test/flutter_test.dart';
import 'package:go_router/go_router.dart';
import 'package:go_router_examples/push_with_shell_route.dart' as example;

void main() {
testWidgets('example works', (WidgetTester tester) async {
await tester.pumpWidget(example.PushWithShellRouteExampleApp());
expect(find.text('shell1'), findsOneWidget);

await tester.tap(find.text('push the same shell route /shell1'));
await tester.pumpAndSettle();
expect(find.text('shell1'), findsOneWidget);
expect(find.text('shell1 body'), findsOneWidget);

find.text('shell1 body').evaluate().first.pop();
await tester.pumpAndSettle();
expect(find.text('shell1'), findsOneWidget);
expect(find.text('shell1 body'), findsNothing);

await tester.tap(find.text('push the different shell route /shell2'));
await tester.pumpAndSettle();
expect(find.text('shell1'), findsNothing);
expect(find.text('shell2'), findsOneWidget);
expect(find.text('shell2 body'), findsOneWidget);

find.text('shell2 body').evaluate().first.pop();
await tester.pumpAndSettle();
expect(find.text('shell1'), findsOneWidget);
expect(find.text('shell2'), findsNothing);

await tester.tap(find.text('push the regular route /regular-route'));
await tester.pumpAndSettle();
expect(find.text('shell1'), findsNothing);
expect(find.text('regular route'), findsOneWidget);

find.text('regular route').evaluate().first.pop();
await tester.pumpAndSettle();
expect(find.text('shell1'), findsOneWidget);
expect(find.text('regular route'), findsNothing);
});
}
Loading