fix
This commit is contained in:
@@ -64,9 +64,9 @@ class AuthState {
|
|||||||
class Auth extends _$Auth {
|
class Auth extends _$Auth {
|
||||||
@override
|
@override
|
||||||
AuthState build() {
|
AuthState build() {
|
||||||
// Don't call async operations in build
|
// Start with loading state to show splash screen
|
||||||
// Use a separate method to initialize auth state
|
// Use a separate method to initialize auth state
|
||||||
return const AuthState();
|
return const AuthState(isLoading: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthRepository get _repository => ref.read(authRepositoryProvider);
|
AuthRepository get _repository => ref.read(authRepositoryProvider);
|
||||||
@@ -74,7 +74,9 @@ class Auth extends _$Auth {
|
|||||||
/// Initialize auth state - call this on app start
|
/// Initialize auth state - call this on app start
|
||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
print('🚀 Initializing auth state...');
|
print('🚀 Initializing auth state...');
|
||||||
state = state.copyWith(isLoading: true);
|
|
||||||
|
// Minimum loading time for smooth UX (prevent flashing)
|
||||||
|
final minimumLoadingTime = Future.delayed(const Duration(milliseconds: 800));
|
||||||
|
|
||||||
final isAuthenticated = await _repository.isAuthenticated();
|
final isAuthenticated = await _repository.isAuthenticated();
|
||||||
print('🚀 isAuthenticated result: $isAuthenticated');
|
print('🚀 isAuthenticated result: $isAuthenticated');
|
||||||
@@ -83,6 +85,10 @@ class Auth extends _$Auth {
|
|||||||
print('🚀 Token found, fetching user profile...');
|
print('🚀 Token found, fetching user profile...');
|
||||||
// Get user profile
|
// Get user profile
|
||||||
final result = await _repository.getProfile();
|
final result = await _repository.getProfile();
|
||||||
|
|
||||||
|
// Wait for minimum loading time to complete
|
||||||
|
await minimumLoadingTime;
|
||||||
|
|
||||||
result.fold(
|
result.fold(
|
||||||
(failure) {
|
(failure) {
|
||||||
print('❌ Failed to get profile: ${failure.message}');
|
print('❌ Failed to get profile: ${failure.message}');
|
||||||
@@ -103,6 +109,10 @@ class Auth extends _$Auth {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
print('❌ No token found, user needs to login');
|
print('❌ No token found, user needs to login');
|
||||||
|
|
||||||
|
// Wait for minimum loading time even when not authenticated
|
||||||
|
await minimumLoadingTime;
|
||||||
|
|
||||||
state = const AuthState(
|
state = const AuthState(
|
||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import '../providers/auth_provider.dart';
|
import '../providers/auth_provider.dart';
|
||||||
import '../pages/login_page.dart';
|
import '../pages/login_page.dart';
|
||||||
|
import 'splash_screen.dart';
|
||||||
|
|
||||||
/// Wrapper widget that checks authentication status
|
/// Wrapper widget that checks authentication status
|
||||||
/// Shows login page if not authenticated, otherwise shows child widget
|
/// Shows login page if not authenticated, otherwise shows child widget
|
||||||
@@ -17,20 +18,26 @@ class AuthWrapper extends ConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final authState = ref.watch(authProvider);
|
final authState = ref.watch(authProvider);
|
||||||
print('AuthWrapper build: isAuthenticated=${authState.isAuthenticated}, isLoading=${authState.isLoading}');
|
print('AuthWrapper build: isAuthenticated=${authState.isAuthenticated}, isLoading=${authState.isLoading}');
|
||||||
// Show loading indicator while checking auth status
|
|
||||||
|
// Show splash screen while checking auth status
|
||||||
if (authState.isLoading && authState.user == null) {
|
if (authState.isLoading && authState.user == null) {
|
||||||
return const Scaffold(
|
return const SplashScreen();
|
||||||
body: Center(
|
}
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
|
// Smooth fade transition between screens
|
||||||
|
return AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 400),
|
||||||
|
switchInCurve: Curves.easeInOut,
|
||||||
|
switchOutCurve: Curves.easeInOut,
|
||||||
|
child: authState.isAuthenticated
|
||||||
|
? KeyedSubtree(
|
||||||
|
key: const ValueKey('main_app'),
|
||||||
|
child: child,
|
||||||
|
)
|
||||||
|
: const KeyedSubtree(
|
||||||
|
key: ValueKey('login_page'),
|
||||||
|
child: LoginPage(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show child widget if authenticated, otherwise show login page
|
|
||||||
if (authState.isAuthenticated) {
|
|
||||||
return child;
|
|
||||||
} else {
|
|
||||||
return const LoginPage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
135
lib/features/auth/presentation/widgets/splash_screen.dart
Normal file
135
lib/features/auth/presentation/widgets/splash_screen.dart
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// Splash screen shown while checking authentication status
|
||||||
|
class SplashScreen extends StatefulWidget {
|
||||||
|
const SplashScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SplashScreen> createState() => _SplashScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SplashScreenState extends State<SplashScreen>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late AnimationController _controller;
|
||||||
|
late Animation<double> _fadeAnimation;
|
||||||
|
late Animation<double> _scaleAnimation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller = AnimationController(
|
||||||
|
duration: const Duration(milliseconds: 800),
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
|
||||||
|
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
|
||||||
|
CurvedAnimation(
|
||||||
|
parent: _controller,
|
||||||
|
curve: Curves.easeIn,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
_scaleAnimation = Tween<double>(begin: 0.8, end: 1.0).animate(
|
||||||
|
CurvedAnimation(
|
||||||
|
parent: _controller,
|
||||||
|
curve: Curves.easeOutBack,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
_controller.forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: theme.colorScheme.primary,
|
||||||
|
body: SafeArea(
|
||||||
|
child: Center(
|
||||||
|
child: FadeTransition(
|
||||||
|
opacity: _fadeAnimation,
|
||||||
|
child: ScaleTransition(
|
||||||
|
scale: _scaleAnimation,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
// App Icon/Logo
|
||||||
|
Container(
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(24),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.2),
|
||||||
|
blurRadius: 20,
|
||||||
|
offset: const Offset(0, 10),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
Icons.point_of_sale_rounded,
|
||||||
|
size: 64,
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
|
||||||
|
// App Name
|
||||||
|
Text(
|
||||||
|
'Retail POS',
|
||||||
|
style: theme.textTheme.headlineMedium?.copyWith(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
letterSpacing: 1.2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
|
||||||
|
// Subtitle
|
||||||
|
Text(
|
||||||
|
'Point of Sale System',
|
||||||
|
style: theme.textTheme.bodyLarge?.copyWith(
|
||||||
|
color: Colors.white.withOpacity(0.9),
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 48),
|
||||||
|
|
||||||
|
// Loading Indicator
|
||||||
|
SizedBox(
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 3,
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
|
Colors.white.withOpacity(0.9),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Loading Text
|
||||||
|
Text(
|
||||||
|
'Loading...',
|
||||||
|
style: theme.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Colors.white.withOpacity(0.8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user