feat: enhance routing logic and improve login screen validation; add guarded API call for error handling
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'api_exception.dart';
|
||||
import '../../features/auth/data/auth_providers.dart';
|
||||
|
||||
/// Executes [call] and automatically logs out the user if the server
|
||||
/// returns 401 Unauthorized. Re-throws the exception so the calling
|
||||
/// widget/provider can still display an error if needed.
|
||||
Future<T> guardedApiCall<T>(Ref ref, Future<T> Function() call) async {
|
||||
try {
|
||||
return await call();
|
||||
} on ApiException catch (e) {
|
||||
if (e.type == ApiErrorType.unauthorized) {
|
||||
await ref.read(authStateProvider.notifier).logout();
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../core/ui/app_shell.dart';
|
||||
import '../ui/app_shell.dart';
|
||||
import '../ui/async_state_views.dart';
|
||||
import '../../features/auth/data/auth_providers.dart';
|
||||
import '../../features/recipes/presentation/recipes_screen.dart';
|
||||
import '../../features/auth/presentation/login_screen.dart';
|
||||
import '../../features/profile/presentation/profile_screen.dart';
|
||||
import '../../features/recipes/presentation/recipes_screen.dart';
|
||||
|
||||
final appRouterProvider = Provider<GoRouter>((ref) {
|
||||
final authState = ref.watch(authStateProvider);
|
||||
@@ -17,29 +18,39 @@ final appRouterProvider = Provider<GoRouter>((ref) {
|
||||
final isLoading = authState.isLoading;
|
||||
final token = authState.valueOrNull;
|
||||
final isLoggedIn = token != null && token.isNotEmpty;
|
||||
final isLoginRoute = state.matchedLocation == '/login';
|
||||
final isRootRoute = state.matchedLocation == '/';
|
||||
final location = state.matchedLocation;
|
||||
final isSplash = location == '/';
|
||||
final isLogin = location == '/login';
|
||||
|
||||
// Keep user on splash while auth state is being resolved from storage.
|
||||
if (isLoading) {
|
||||
return null;
|
||||
return isSplash ? null : '/';
|
||||
}
|
||||
|
||||
if (isRootRoute) {
|
||||
// Redirect away from splash once auth is known.
|
||||
if (isSplash) {
|
||||
return isLoggedIn ? '/recipes' : '/login';
|
||||
}
|
||||
|
||||
if (!isLoggedIn && !isLoginRoute) {
|
||||
// Unauthenticated user trying to reach a protected route.
|
||||
if (!isLoggedIn && !isLogin) {
|
||||
return '/login';
|
||||
}
|
||||
|
||||
if (isLoggedIn && isLoginRoute) {
|
||||
// Authenticated user landing on login.
|
||||
if (isLoggedIn && isLogin) {
|
||||
return '/recipes';
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
routes: [
|
||||
GoRoute(path: '/', builder: (context, state) => const SizedBox.shrink()),
|
||||
GoRoute(
|
||||
path: '/',
|
||||
builder: (context, state) => const Scaffold(
|
||||
body: LoadingStateView(label: 'Startar...'),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/login',
|
||||
builder: (context, state) => const LoginScreen(),
|
||||
|
||||
Reference in New Issue
Block a user