Skip to content
Next Next commit
refactor: Add state to onExit method
  • Loading branch information
ValentinVignal committed Apr 10, 2024
commit 7b05a771cf61d11847e247d3e3ad523b113cc766
5 changes: 4 additions & 1 deletion packages/go_router/example/lib/on_exit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ final GoRouter _router = GoRouter(
builder: (BuildContext context, GoRouterState state) {
return const DetailsScreen();
},
onExit: (BuildContext context) async {
onExit: (
BuildContext context,
GoRouterState state,
) async {
final bool? confirmed = await showDialog<bool>(
context: context,
builder: (_) {
Expand Down
58 changes: 41 additions & 17 deletions packages/go_router/lib/src/delegate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,17 @@ class GoRouterDelegate extends RouterDelegate<RouteMatchList>
}
walker = walker.matches.last;
}
assert(walker is RouteMatch);
if (state != null) {
return state.maybePop();
}
// This should be the only place where the last GoRoute exit the screen.
final GoRoute lastRoute = currentConfiguration.last.route;
if (lastRoute.onExit != null && navigatorKey.currentContext != null) {
return !(await lastRoute.onExit!(navigatorKey.currentContext!));
return !(await lastRoute.onExit!(
navigatorKey.currentContext!,
walker.buildState(_configuration, currentConfiguration),
));
}
return false;
}
Expand Down Expand Up @@ -137,8 +141,10 @@ class GoRouterDelegate extends RouterDelegate<RouteMatchList>
// a microtask in case the onExit callback want to launch dialog or other
// navigator operations.
scheduleMicrotask(() async {
final bool onExitResult =
await routeBase.onExit!(navigatorKey.currentContext!);
final bool onExitResult = await routeBase.onExit!(
navigatorKey.currentContext!,
match.buildState(_configuration, currentConfiguration),
);
if (onExitResult) {
_completeRouteMatch(result, match);
}
Expand Down Expand Up @@ -217,14 +223,14 @@ class GoRouterDelegate extends RouterDelegate<RouteMatchList>
}

if (indexOfFirstDiff < currentGoRouteMatches.length) {
final List<GoRoute> exitingGoRoutes = currentGoRouteMatches
.sublist(indexOfFirstDiff)
.map<RouteBase>((RouteMatch match) => match.route)
.whereType<GoRoute>()
.toList();
return _callOnExitStartsAt(exitingGoRoutes.length - 1,
context: navigatorContext, routes: exitingGoRoutes)
.then<void>((bool exit) {
final List<RouteMatch> exitingMatches =
currentGoRouteMatches.sublist(indexOfFirstDiff).toList();
return _callOnExitStartsAt(
exitingMatches.length - 1,
context: navigatorContext,
matches: exitingMatches,
configuration: configuration,
).then<void>((bool exit) {
if (!exit) {
return SynchronousFuture<void>(null);
}
Expand All @@ -240,24 +246,42 @@ class GoRouterDelegate extends RouterDelegate<RouteMatchList>
///
/// The returned future resolves to true if all routes below the index all
/// return true. Otherwise, the returned future resolves to false.
static Future<bool> _callOnExitStartsAt(int index,
{required BuildContext context, required List<GoRoute> routes}) {
Future<bool> _callOnExitStartsAt(
int index, {
required BuildContext context,
required List<RouteMatch> matches,
required RouteMatchList configuration,
}) {
if (index < 0) {
return SynchronousFuture<bool>(true);
}
final GoRoute goRoute = routes[index];
final RouteMatch match = matches[index];
final GoRoute goRoute = match.route;
if (goRoute.onExit == null) {
return _callOnExitStartsAt(index - 1, context: context, routes: routes);
return _callOnExitStartsAt(
index - 1,
context: context,
matches: matches,
configuration: configuration,
);
}

Future<bool> handleOnExitResult(bool exit) {
if (exit) {
return _callOnExitStartsAt(index - 1, context: context, routes: routes);
return _callOnExitStartsAt(
index - 1,
context: context,
matches: matches,
configuration: configuration,
);
}
return SynchronousFuture<bool>(false);
}

final FutureOr<bool> exitFuture = goRoute.onExit!(context);
final FutureOr<bool> exitFuture = goRoute.onExit!(
context,
match.buildState(_configuration, configuration),
);
if (exitFuture is bool) {
return handleOnExitResult(exitFuture);
}
Expand Down
3 changes: 2 additions & 1 deletion packages/go_router/lib/src/route.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ typedef NavigatorBuilder = Widget Function(
///
/// If the return value is true or the future resolve to true, the route will
/// exit as usual. Otherwise, the operation will abort.
typedef ExitCallback = FutureOr<bool> Function(BuildContext context);
typedef ExitCallback = FutureOr<bool> Function(
BuildContext context, GoRouterState state);

/// The base class for [GoRoute] and [ShellRoute].
///
Expand Down
9 changes: 9 additions & 0 deletions packages/go_router/lib/src/route_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ abstract class GoRouteData extends RouteData {
/// Corresponds to [GoRoute.redirect].
FutureOr<String?> redirect(BuildContext context, GoRouterState state) => null;

/// Called when this route is removed from GoRouter's route history.
///
/// Corresponds to [GoRoute.onExit].
FutureOr<bool> onExit(BuildContext context, GoRouterState state) => true;

/// A helper function used by generated code.
///
/// Should not be used directly.
Expand Down Expand Up @@ -106,6 +111,9 @@ abstract class GoRouteData extends RouteData {
FutureOr<String?> redirect(BuildContext context, GoRouterState state) =>
factoryImpl(state).redirect(context, state);

FutureOr<bool> onExit(BuildContext context, GoRouterState state) =>
factoryImpl(state).onExit(context, state);

return GoRoute(
path: path,
name: name,
Expand All @@ -114,6 +122,7 @@ abstract class GoRouteData extends RouteData {
redirect: redirect,
routes: routes,
parentNavigatorKey: parentNavigatorKey,
onExit: onExit,
);
}

Expand Down