pod_router 0.0.1+4 copy "pod_router: ^0.0.1+4" to clipboard
pod_router: ^0.0.1+4 copied to clipboard

A Flutter navigation package that combines go_router with Riverpod for clean, testable routing with authentication support.

pod_router #

pod_router logo

A Flutter package that simplifies integration between Go Router and Riverpod state management with authentication handling. This package helps you manage routing based on authentication state and other app conditions, reducing boilerplate and providing a standardized approach.

Pub Version License: MIT

Features #

  • 🔐 Authentication-aware routing - Automatically redirect users based on auth state
  • 🔄 State-driven navigation - Route changes respond to Riverpod state changes
  • 🌊 Simplified navigation flow - Handle onboarding, authentication flow, and protected routes
  • 📝 Declarative route definition - Define protected and public routes clearly
  • 🪝 Easy integration - Works with your existing Go Router and Riverpod setup
  • 🚀 Initial data loading - Wait for required data before navigation starts

Installation #

dependencies:
  pod_router : ^0.0.1
copied to clipboard

Run:

flutter pub get
copied to clipboard

Quick Start #

import 'package:pod_router/pod_router.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';

// 1. Create your auth notifier
class MyAuthNotifier extends BaseAuthNotifier {
  MyAuthNotifier(Ref ref) : super(ref);
  
  @override
  void initialize() {
    // Your auth initialization logic here
  }
  
  Future<void> login() async {
    setLoading();
    // Login implementation
    setAuthenticated();
  }
  
  Future<void> logout() async {
    setLoading();
    // Logout implementation
    setUnauthenticated();
  }
}

// 2. Create the auth provider
final authProvider = createAuthNotifierProvider<MyAuthNotifier>(
  (ref) => MyAuthNotifier(ref)
);

// 3. Define your routes manager
class AppRoutesManager extends RoutesManager {
  AppRoutesManager(Ref ref) : super(ref);
  
  @override
  List<String> get protectedRoutes => ['/profile', '/settings'];
  
  @override
  List<String> get publicRoutes => ['/login', '/register'];
  
  @override
  String get splashRoute => '/splash';
  
  @override
  String get defaultAuthenticatedRoute => '/home';
  
  @override
  String get defaultUnauthenticatedRoute => '/login';
  
  // 4. Define initial data providers (NEW!)
  @override
  List<ProviderListenable> get initialDataProviders => [
    userDataProvider,
    settingsProvider,
  ];
  
  @override
  void initializeListeners() {
    super.initializeListeners(); // Don't forget this for initial data loading!
    
    // Connect auth state to router
    ref.listen(
      authProvider.select((value) => value.status),
      (prev, next) {
        authState.value = next;
      },
      fireImmediately: true,
    );
    
    // Connect loading state
    ref.listen(authProvider.select((value) => value.isLoading),
      (prev, next) {
        isLoading.value = next;
      },
      fireImmediately: true,
    );
  }
}

// 5. Create the router provider
final routesProvider = routesManagerProvider<AppRoutesManager>(
  (ref) => AppRoutesManager(ref)
);

// 6. Set up the Go Router
final routerProvider = Provider<GoRouter>((ref) {
  final routesManager = ref.watch(routesProvider);
  
  return GoRouter(
    redirect: routesManager.onRedirect,
    refreshListenable: CombinedListen(routesManager.refreshables),
    routes: [
      // Your routes here
    ],
  );
});
copied to clipboard

Detailed Usage #

Authentication State Management #

The package provides a BaseAuthNotifier class that handles authentication state. Extend this class to implement your specific authentication logic:

class FirebaseAuthNotifier extends BaseAuthNotifier {
  FirebaseAuthNotifier(Ref ref) : super(ref);
  
  @override
  void initialize() {
    // Listen to Firebase auth changes
    FirebaseAuth.instance.authStateChanges().listen((user) {
      if (user != null) {
        setAuthenticated();
      } else {
        setUnauthenticated();
      }
    });
  }
  
  Future<void> signIn(String email, String password) async {
    setLoading();
    try {
      await FirebaseAuth.instance.signInWithEmailAndPassword(
        email: email,
        password: password,
      );
    } catch (e) {
      setUnauthenticated();
      rethrow;
    }
  }
  
  Future<void> signOut() async {
    setLoading();
    await FirebaseAuth.instance.signOut();
  }
}
copied to clipboard

Route Management #

The RoutesManager class handles route redirects based on authentication state:

class MyRoutesManager extends RoutesManager {
  MyRoutesManager(Ref ref) : super(ref);
  
  @override
  List<String> get protectedRoutes => [
    '/profile', 
    '/settings',
    '/dashboard'
  ];
  
  @override
  List<String> get publicRoutes => [
    '/login', 
    '/register',
    '/forgot-password'
  ];
  
  @override
  String get splashRoute => '/splash';
  
  @override
  String get defaultAuthenticatedRoute => '/home';
  
  @override
  String get defaultUnauthenticatedRoute => '/login';
  
  @override
  String? get initialAppFlowRoute => '/onboarding';
  
  @override
  void initializeListeners() {
    super.initializeListeners(); // Important for initial data loading!
    
    // Listen to auth state
    ref.listen(
      authProvider.select((value) => value.status),
      (prev, next) {
        authState.value = next;
      },
      fireImmediately: true,
    );
    
    // Optional: Listen to onboarding completion
    ref.listen(onboardingCompletedProvider, (prev, next) {
      appFlowNotifier.value = !next;
    });
    
    // Other state listeners...
  }
}
copied to clipboard

Initial Data Loading (NEW!) #

The package now supports waiting for initial data to load before navigation:

class MyRoutesManager extends RoutesManager {
  // ... other overrides
  
  @override
  List<ProviderListenable> get initialDataProviders => [
    // Add providers that need to be loaded before navigation begins
    userDataProvider,
    themeProvider,
    localizationProvider,
    // Any async provider that should complete before navigation
  ];
}
copied to clipboard

Initial Data Loading #

When these providers are specified, the router will:

  • Show the splash screen until all data is loaded
  • Register the routes manager with the system
  • Track loading state automatically
  • Provide error handling for data loading failures

Setting Up Go Router #

Integrate with Go Router using the CombinedListen utility:

final goRouterProvider = Provider<GoRouter>((ref) {
  final routesManager = ref.watch(routesManagerProvider);
  
  return GoRouter(
    debugLogDiagnostics: true,
    redirect: routesManager.onRedirect,
    refreshListenable: CombinedListen(routesManager.refreshables),
    initialLocation: '/splash',
    routes: [
      // Your routes...
    ],
  );
});
copied to clipboard

Complete Example #

See the example folder for a complete implementation.

Debugging #

Enable debug logs to help troubleshoot routing issues:

void main() {
  PackageLogger.enableDebugLogs = true;
  runApp(ProviderScope(child: MyApp()));
}
copied to clipboard

Additional Configuration #

Customizing Redirect Logic #

You can override the onRedirect method in your RoutesManager subclass to customize the redirect logic:

@override
FutureOr<String?> onRedirect(BuildContext context, GoRouterState state) async {
  // Custom logic before calling super
  if (specialCondition) {
    return '/special-route';
  }
  
  // Use default logic
  return await super.onRedirect(context, state);
}
copied to clipboard

Adding Custom State Triggers #

You can add additional ValueNotifier objects to trigger route refreshes:

final themeChanged = ValueNotifier<bool>(false);

@override
List<ChangeNotifier> get refreshables => [
  ...super.refreshables,
  themeChanged,
];
copied to clipboard

Waiting for Authenticated State #

The package provides a utility to wait for authentication state to be determined:

Future<void> loadInitialData() async {
  // Wait for auth state before proceeding
  final authStatus = await waitForAuthState(ref, authProvider);
  
  // Now you can perform actions based on auth status
  if (authStatus == AuthStatus.authenticated) {
    await ref.read(userDataProvider.notifier).loadUserData();
  }
}
copied to clipboard

Contributing #

Contributions are welcome! Please feel free to submit a Pull Request.

License #

This project is licensed under the MIT License - see the LICENSE file for details.

3
likes
160
points
11
downloads

Publisher

unverified uploader

Weekly Downloads

2024.09.29 - 2025.04.13

A Flutter navigation package that combines go_router with Riverpod for clean, testable routing with authentication support.

Repository (GitHub)

Topics

#navigation #routing #riverpod #go-router #authentication

Documentation

API reference

License

MIT (license)

Dependencies

equatable, flutter, flutter_riverpod, go_router, logger

More

Packages that depend on pod_router