Learn
Redirects and Guards
Send users to another typed destination, deny a transition, protect unsaved work, and refresh policy when session state changes.
Redirects and Guards
Use a Redirect when navigation should continue to a different Destination. Use a Guard when navigation should stop.
Redirect a signed-out user to Login
final class AuthenticationRedirect implements NavigationRedirect {
AuthenticationRedirect(this.session);
final SessionState session;
@override
NavigationTarget<Object?>? redirect(NavigationPolicyContext context) {
final destination = context.target.destination;
if (session.isSignedIn || destination is LoginDestination) return null;
return NavigationTarget<Object?>(
destination: LoginDestination(returnTo: destination),
);
}
}
The original Account page is never rendered. Login receives the typed return target and can replace itself after sign-in.
Deny access without choosing another screen
enum AccessDeniedReason { subscriptionRequired }
final class SubscriptionGuard implements NavigationGuard {
SubscriptionGuard(this.account);
final AccountState account;
@override
NavigationGuardDecision evaluate(NavigationPolicyContext context) {
if (context.phase == NavigationGuardPhase.enter && !account.isPremium) {
return const NavigationGuardDenied(
reason: AccessDeniedReason.subscriptionRequired,
);
}
return const NavigationGuardAllowed();
}
}
The caller receives NavigationRejected; your UI can show an upgrade prompt without mutating the stack.
Protect unsaved changes
Check leave when moving away from an editor:
if (context.phase == NavigationGuardPhase.leave && editor.hasChanges) {
return const NavigationGuardDenied(reason: UnsavedReason.pendingChanges);
}
The Guard reports policy. A confirmation dialog is UI and should be handled by the caller or application layer, followed by a second explicit navigation request.
Re-evaluate when authentication changes
final session = NavigationSession(
id: const NavigationSessionId('main'),
graph: createGraph(sessionState),
policyRefresh: ListenableNavigationPolicyRefresh(sessionState),
);
When sessionState.notifyListeners() runs, the Session reconciles the current target. This is how sign-out can move an already-open private screen through the same policy pipeline.
Attach policies at the smallest useful boundary: Route for one page, Module for a feature, Mount for one placement, Graph for the whole application.