Skip to content
Open
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
5 changes: 5 additions & 0 deletions packages/go_router_builder/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 4.3.0

- Adds support for `overrideOnExit` parameter in `TypedGoRoute` and `TypedRelativeGoRoute` annotations.
When set to `true`, the generated route will include `overrideOnExit: true` in the GoRoute constructor, enabling custom `onExit` callback implementation for the route. Defaults to `false`.
Comment on lines +3 to +4

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This changelog entry is a bit verbose. For better readability, it's good practice for changelog entries to be concise. Consider removing implementation details like what happens when the flag is true and its default value.

Suggested change
- Adds support for `overrideOnExit` parameter in `TypedGoRoute` and `TypedRelativeGoRoute` annotations.
When set to `true`, the generated route will include `overrideOnExit: true` in the GoRoute constructor, enabling custom `onExit` callback implementation for the route. Defaults to `false`.
- Adds `overrideOnExit` parameter to `TypedGoRoute` and `TypedRelativeGoRoute` annotations to support custom `onExit` callbacks.

Comment on lines +3 to +4

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This changelog entry is a bit verbose. For better conciseness, which is encouraged by the repository style guide, you could shorten it to be more in line with other entries in this file.

Suggested change
- Adds support for `overrideOnExit` parameter in `TypedGoRoute` and `TypedRelativeGoRoute` annotations.
When set to `true`, the generated route will include `overrideOnExit: true` in the GoRoute constructor, enabling custom `onExit` callback implementation for the route. Defaults to `false`.
- Adds an `overrideOnExit` parameter to `TypedGoRoute` and `TypedRelativeGoRoute` annotations to allow custom `onExit` callback implementations.
References
  1. The repository style guide, in the 'Review Agent Guidelines' section, states a preference for conciseness. While aimed at review summaries, this principle is valuable for changelogs to ensure they are easy to scan and understand. The suggested change makes the entry more direct. (link)


## 4.2.0

- Adds supports for `TypedQueryParameter` annotation.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore_for_file: public_member_api_docs, unreachable_from_main

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

part 'not_override_on_exit_example.g.dart';

void main() => runApp(App());

class App extends StatelessWidget {
App({super.key});

@override
Widget build(BuildContext context) =>
MaterialApp.router(routerConfig: _router, title: _appTitle);

final GoRouter _router = GoRouter(routes: $appRoutes);
}

@TypedGoRoute<HomeRoute>(path: '/')
class HomeRoute extends GoRouteData with $HomeRoute {
const HomeRoute();

@override
Widget build(BuildContext context, GoRouterState state) => const HomeScreen();
}

@TypedGoRoute<Sub1Route>(path: '/sub-1-route')
class Sub1Route extends GoRouteData with $Sub1Route {
const Sub1Route();

@override
Widget build(BuildContext context, GoRouterState state) => const Sub1Screen();
}

@TypedGoRoute<Sub2Route>(path: '/sub-2-route')
class Sub2Route extends GoRouteData with $Sub2Route {
const Sub2Route();

@override
Widget build(BuildContext context, GoRouterState state) => const Sub2Screen();
}

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

@override
State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
String? _result;

@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text(_appTitle)),
body: Center(
child: ElevatedButton(
onPressed: () async {
final String? result = await const Sub1Route().push<String?>(context);
if (!context.mounted) {
return;
}
setState(() => _result = result);
},
child: Text(_result ?? 'Go to sub 1 screen'),
),
),
);
}

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

@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('$_appTitle Sub 1 screen')),
body: Center(
child: ElevatedButton(
onPressed: () async {
final String? result = await const Sub2Route().push<String?>(context);
if (!context.mounted) {
return;
}
context.pop(result);
},
child: const Text('Go to sub 2 screen'),
),
),
);
}

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

@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('$_appTitle Sub 2 screen')),
body: Center(
child: ElevatedButton(
onPressed: () => context.pop('Sub2Screen'),
child: const Text('Go back to sub 1 screen'),
),
),
);
}

const String _appTitle = 'GoRouter Example: builder';

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class App extends StatelessWidget {
@TypedGoRoute<HomeRoute>(
path: '/',
routes: <TypedGoRoute<GoRouteData>>[
TypedGoRoute<SubRoute>(path: 'sub-route'),
TypedGoRoute<SubRoute>(path: 'sub-route', overrideOnExit: true),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This api is not ideal since the customer may not know why the flag is here if they don't have the context.

I have several questions

  1. looking at the code I am not sure why an onExit that always returns true will cause the bug in the original github issue, do you know why?
  2. if we really want to make such change, we should consider make it default that it only conditionally pass in onExit if user does override instead of controlling by a flag that may be confusing for the customer

Copy link
Author

@hantrungkien hantrungkien Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chunhtai Thank you for your response.

  1. I think the issue is caused by this:
    https://github.com/flutter/packages/blob/main/packages/go_router/lib/src/delegate.dart#L69

In my example here:

If I do it like this, I can return to HomePage:

final result = await Details2RouteData(
  $extra: Details2PageExtra(data: 'Test data 2'),
).push<String?>(context);
if (!context.mounted) return;
await WidgetsBinding.instance.endOfFrame;
if (!context.mounted) return;
context.pop(result);

However, the user will briefly see Details1Page before returning to Home. Therefore, I think a better solution would be to allow the default onExit parameter to be null, instead of always returning true.

  1. we should consider make it default that it only conditionally pass in onExit if user does override

What do you think if I handle it like this?

go_router_builder

        final bool hasOverriddenOnExit = classElement.methods.any(
          (method) => method.name == 'onExit',
        );

],
)
class HomeRoute extends GoRouteData with $HomeRoute {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/go_router_builder/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ dependencies:
collection: ^1.15.0
flutter:
sdk: flutter
go_router: ^17.1.0
go_router: ^17.2.0
provider: 6.0.5

dev_dependencies:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2013 The Flutter Authors
// 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:flutter_test/flutter_test.dart';
import 'package:go_router_builder_example/not_override_on_exit_example.dart';

void main() {
testWidgets('HomeScreen should return result from Sub2Screen', (
WidgetTester tester,
) async {
await tester.pumpWidget(App());
expect(find.byType(HomeScreen), findsOne);

await tester.tap(find.widgetWithText(ElevatedButton, 'Go to sub 1 screen'));
await tester.pumpAndSettle();

expect(find.byType(Sub1Screen), findsOne);

await tester.tap(find.widgetWithText(ElevatedButton, 'Go to sub 2 screen'));
await tester.pumpAndSettle();

expect(find.byType(Sub2Screen), findsOne);

await tester.tap(find.widgetWithText(ElevatedButton, 'Go back to sub 1 screen'));
await tester.pumpAndSettle();

expect(find.byType(HomeScreen), findsOne);

await tester.tap(find.widgetWithText(ElevatedButton, 'Sub2Screen'));

expect(find.byType(Sub1Screen), findsNothing);
expect(find.byType(Sub2Screen), findsNothing);
});
}
30 changes: 30 additions & 0 deletions packages/go_router_builder/lib/src/route_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ class GoRouteConfig extends RouteBaseConfig with _GoRouteMixin {
required this.path,
required this.name,
required this.caseSensitive,
required this.overrideOnExit,
required this.parentNavigatorKey,
required super.routeDataClass,
required super.parent,
Expand All @@ -436,6 +437,15 @@ class GoRouteConfig extends RouteBaseConfig with _GoRouteMixin {
/// The case sensitivity of the GoRoute to be created by this configuration.
final bool caseSensitive;

/// Whether to enable the onExit callback for this route.
///
/// When set to true, the route will include an onExit parameter in the
/// generated GoRoute constructor, allowing you to implement custom logic
/// when navigating away from this route.
///
/// Defaults to false.
final bool overrideOnExit;

/// The parent navigator key.
final String? parentNavigatorKey;

Expand Down Expand Up @@ -505,6 +515,7 @@ mixin $_mixinName on $routeDataClassName {
'path: ${escapeDartString(path)},'
'${name != null ? 'name: ${escapeDartString(name!)},' : ''}'
'${caseSensitive ? '' : 'caseSensitive: $caseSensitive,'}'
'${overrideOnExit ? 'overrideOnExit: $overrideOnExit,' : ''}'
'${parentNavigatorKey == null ? '' : 'parentNavigatorKey: $parentNavigatorKey,'}';

@override
Expand All @@ -516,6 +527,7 @@ class RelativeGoRouteConfig extends RouteBaseConfig with _GoRouteMixin {
RelativeGoRouteConfig._({
required this.path,
required this.caseSensitive,
required this.overrideOnExit,
required this.parentNavigatorKey,
required super.routeDataClass,
required super.parent,
Expand All @@ -527,6 +539,15 @@ class RelativeGoRouteConfig extends RouteBaseConfig with _GoRouteMixin {
/// The case sensitivity of the GoRoute to be created by this configuration.
final bool caseSensitive;

/// Whether to enable the onExit callback for this route.
///
/// When set to true, the route will include an onExit parameter in the
/// generated GoRoute constructor, allowing you to implement custom logic
/// when navigating away from this route.
///
/// Defaults to false.
final bool overrideOnExit;

/// The parent navigator key.
final String? parentNavigatorKey;

Expand Down Expand Up @@ -582,6 +603,7 @@ mixin $_mixinName on $routeDataClassName {
String get routeConstructorParameters =>
'path: ${escapeDartString(path)},'
'${caseSensitive ? '' : 'caseSensitive: $caseSensitive,'}'
'${overrideOnExit ? 'overrideOnExit: $overrideOnExit,' : ''}'
'${parentNavigatorKey == null ? '' : 'parentNavigatorKey: $parentNavigatorKey,'}';

@override
Expand Down Expand Up @@ -717,10 +739,14 @@ abstract class RouteBaseConfig {
}
final ConstantReader nameValue = reader.read('name');
final ConstantReader caseSensitiveValue = reader.read('caseSensitive');
final ConstantReader overrideOnExitValue = reader.read(
'overrideOnExit',
);
value = GoRouteConfig._(
path: pathValue.stringValue,
name: nameValue.isNull ? null : nameValue.stringValue,
caseSensitive: caseSensitiveValue.boolValue,
overrideOnExit: overrideOnExitValue.boolValue,
routeDataClass: classElement,
parent: parent,
parentNavigatorKey: _generateParameterGetterCode(
Expand All @@ -744,9 +770,13 @@ abstract class RouteBaseConfig {
);
}
final ConstantReader caseSensitiveValue = reader.read('caseSensitive');
final ConstantReader overrideOnExitValue = reader.read(
'overrideOnExit',
);
value = RelativeGoRouteConfig._(
path: pathValue.stringValue,
caseSensitive: caseSensitiveValue.boolValue,
overrideOnExit: overrideOnExitValue.boolValue,
routeDataClass: classElement,
parent: parent,
parentNavigatorKey: _generateParameterGetterCode(
Expand Down
4 changes: 2 additions & 2 deletions packages/go_router_builder/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: go_router_builder
description: >-
A builder that supports generated strongly-typed route helpers for
package:go_router
version: 4.2.0
version: 4.3.0
repository: https://github.com/flutter/packages/tree/main/packages/go_router_builder
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router_builder%22

Expand All @@ -27,7 +27,7 @@ dev_dependencies:
dart_style: ">=2.3.7 <4.0.0"
flutter:
sdk: flutter
go_router: ^17.1.0
go_router: ^17.2.0
leak_tracker_flutter_testing: ">=3.0.0"
package_config: ^2.1.1
pub_semver: ^2.1.5
Expand Down
Loading