Route Protection
Authyra doesn't force a routing pattern. Pick the one that matches your app.
GoRouter (recommended for most apps)
No special widget needed, wire authStateChanges straight into refreshListenable and redirect:
import 'package:authyra_flutter/authyra_flutter.dart';
import 'package:go_router/go_router.dart';
class _StreamListenable extends ChangeNotifier {
_StreamListenable(Stream<dynamic> stream) {
stream.listen((_) => notifyListeners());
}
}
final router = GoRouter(
refreshListenable: _StreamListenable(Authyra.instance.authStateChanges),
redirect: (context, state) {
final authenticated = Authyra.instance.isAuthenticated; // synchronous
final goingToLogin = state.uri.path == '/login';
if (!authenticated && !goingToLogin) return '/login';
if (authenticated && goingToLogin) return '/';
return null;
},
routes: [
GoRoute(path: '/login', builder: (_, __) => const LoginPage()),
GoRoute(path: '/', builder: (_, __) => const HomePage()),
],
);
isAuthenticated is the synchronous cache, safe to read inside redirect without await.
Widget-level guards
For protecting a piece of UI rather than an entire route (a "delete account" button, an admin panel section):
RequireAuth
RequireAuth(
fallback: const Text('Sign in to continue'),
child: ElevatedButton(
onPressed: deleteAccount,
child: const Text('Delete account'),
),
)
Shows child only while a non-expired session exists; fallback otherwise. Reacts live to sessionStream.
RequireRole
RequireRole(
role: 'admin',
fallback: const Text('Admins only'),
child: const AdminPanel(),
)
Checks session.user.metadata['role'] against role. Your AuthUser.metadata has to actually carry a 'role' entry, populate it from your provider's signIn result.
Navigator-based guards
If your app uses plain Navigator (not GoRouter, AutoRoute, or another declarative router), authyra_flutter ships guard widgets for that case specifically:
AuthyraGuard(
config: AuthyraGuardConfig(
loginRoute: '/login',
// or: loginBuilder: (context) => const LoginPage(),
),
child: const HomePage(),
)
AuthyraInverseGuard(
config: AuthyraGuardConfig(homeRoute: '/home'),
child: const LoginPage(), // redirects away if already authenticated
)
Or guard at the navigator level with AuthyraNavigationObserver:
MaterialApp(
navigatorObservers: [
AuthyraNavigationObserver(
loginRoute: '/login',
protectedRoutes: ['/profile', '/settings', '/admin'],
),
],
)
Or wrap the whole app's top-level split with AuthyraRouter:
MaterialApp(
home: AuthyraRouter(
authenticatedBuilder: (context) => const HomePage(),
unauthenticatedBuilder: (context) => const LoginPage(),
loadingBuilder: (context) => const SplashScreen(),
),
)
AuthyraGuard, AuthyraInverseGuard, AuthyraNavigationObserver, and AuthyraRouter are deliberately not for GoRouter/AutoRoute apps, those routers have their own redirect mechanism, shown above, and mixing the two causes fights over navigation. Reach for these only when you're on plain Navigator.