Files
authsec_flutter_new/base_project/lib/view/auth/login.dart
Gaurav Kumar 01be9df2ed baseproject
2025-09-06 19:21:52 +05:30

1008 lines
33 KiB
Dart

import 'dart:math' as math;
import 'package:base_project/core/constants/ui_constants.dart';
import 'package:base_project/routes/route_names.dart';
import 'package:base_project/view_model/auth/auth_view_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../core/providers/dynamic_theme_provider.dart';
import '../../core/providers/theme_provider.dart';
import '../../shared/widgets/buttons/modern_button.dart';
import '../../shared/widgets/inputs/modern_text_field.dart';
import '../../shared/widgets/theme_toggle.dart';
class LoginView extends StatefulWidget {
const LoginView({super.key});
@override
_LoginViewState createState() => _LoginViewState();
}
class _LoginViewState extends State<LoginView> with TickerProviderStateMixin {
// Controllers
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final FocusNode _emailFocusNode = FocusNode();
final FocusNode _passwordFocusNode = FocusNode();
// Animation Controllers
late AnimationController _pageAnimationController;
late AnimationController _formAnimationController;
late AnimationController _logoAnimationController;
late AnimationController _backgroundAnimationController;
late AnimationController _particleAnimationController;
// Animations
late Animation<double> _pageFadeAnimation;
late Animation<Offset> _pageSlideAnimation;
late Animation<double> _formScaleAnimation;
late Animation<double> _logoScaleAnimation;
late Animation<double> _logoRotationAnimation;
late Animation<double> _backgroundOpacityAnimation;
late Animation<double> _particleRotationAnimation;
// State
bool _isPasswordVisible = false;
bool _isFormValid = false;
bool _isEmailFocused = false;
bool _isPasswordFocused = false;
@override
void initState() {
super.initState();
_initializeAnimations();
_startPageAnimation();
_setupFocusListeners();
}
void _setupFocusListeners() {
_emailFocusNode.addListener(() {
setState(() {
_isEmailFocused = _emailFocusNode.hasFocus;
});
});
_passwordFocusNode.addListener(() {
setState(() {
_isPasswordFocused = _passwordFocusNode.hasFocus;
});
});
}
void _initializeAnimations() {
// Page Animation Controller
_pageAnimationController = AnimationController(
duration: UIConstants.durationSlow,
vsync: this,
);
// Form Animation Controller
_formAnimationController = AnimationController(
duration: UIConstants.durationNormal,
vsync: this,
);
// Logo Animation Controller
_logoAnimationController = AnimationController(
duration: UIConstants.durationVerySlow,
vsync: this,
);
// Background Animation Controller
_backgroundAnimationController = AnimationController(
duration: UIConstants.durationSlow,
vsync: this,
);
// Particle Animation Controller
_particleAnimationController = AnimationController(
duration: const Duration(seconds: 10),
vsync: this,
);
// Page Animations
_pageFadeAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _pageAnimationController,
curve: UIConstants.curveNormal,
));
_pageSlideAnimation = Tween<Offset>(
begin: const Offset(0, 0.3),
end: Offset.zero,
).animate(CurvedAnimation(
parent: _pageAnimationController,
curve: UIConstants.curveNormal,
));
// Form Animations
_formScaleAnimation = Tween<double>(
begin: 0.8,
end: 1.0,
).animate(CurvedAnimation(
parent: _formAnimationController,
curve: UIConstants.curveElastic,
));
// Logo Animations
_logoScaleAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _logoAnimationController,
curve: UIConstants.curveElastic,
));
_logoRotationAnimation = Tween<double>(
begin: -0.5,
end: 0.0,
).animate(CurvedAnimation(
parent: _logoAnimationController,
curve: UIConstants.curveElastic,
));
// Background Animations
_backgroundOpacityAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _backgroundAnimationController,
curve: UIConstants.curveNormal,
));
// Particle Animations
_particleRotationAnimation = Tween<double>(
begin: 0.0,
end: 2 * math.pi,
).animate(CurvedAnimation(
parent: _particleAnimationController,
curve: Curves.linear,
));
// Listen to form changes
_emailController.addListener(_validateForm);
_passwordController.addListener(_validateForm);
}
void _startPageAnimation() async {
await Future.delayed(const Duration(milliseconds: 300));
_backgroundAnimationController.forward();
await Future.delayed(const Duration(milliseconds: 200));
_pageAnimationController.forward();
await Future.delayed(const Duration(milliseconds: 500));
_logoAnimationController.forward();
await Future.delayed(const Duration(milliseconds: 300));
_formAnimationController.forward();
_particleAnimationController.repeat();
}
void _validateForm() {
final isValid =
_emailController.text.isNotEmpty && _passwordController.text.isNotEmpty;
if (isValid != _isFormValid) {
setState(() {
_isFormValid = isValid;
});
}
}
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
_emailFocusNode.dispose();
_passwordFocusNode.dispose();
_pageAnimationController.dispose();
_formAnimationController.dispose();
_logoAnimationController.dispose();
_backgroundAnimationController.dispose();
_particleAnimationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// Always use dynamic theme provider for guaranteed dynamic colors
return Consumer<DynamicThemeProvider>(
builder: (context, dynamicThemeProvider, child) {
final colorScheme = dynamicThemeProvider.getCurrentColorScheme(
Theme.of(context).brightness == Brightness.dark);
final size = MediaQuery.of(context).size;
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
colorScheme.primary,
colorScheme.primaryContainer,
colorScheme.secondary,
colorScheme.tertiary,
],
stops: const [0.0, 0.3, 0.7, 1.0],
),
),
child: Stack(
children: [
// Animated Background Particles
_buildParticleBackground(colorScheme),
// Glassmorphism Background
_buildGlassmorphismBackground(colorScheme),
// Theme Toggle Button
Positioned(
top: UIConstants.spacing16,
right: UIConstants.spacing16,
child: Consumer<ThemeProvider>(
builder: (context, themeProvider, child) {
return Container(
decoration: BoxDecoration(
color: colorScheme.surface.withOpacity(0.1),
borderRadius:
BorderRadius.circular(UIConstants.radiusFull),
border: Border.all(
color: colorScheme.surface.withOpacity(0.2),
width: 1,
),
),
child: ThemeToggle(
isDarkMode: themeProvider.isDarkMode,
onThemeChanged: (isDark) {
themeProvider.setTheme(isDark);
},
size: UIConstants.iconSizeLarge,
),
);
},
),
),
// Main Content
SafeArea(
child: SingleChildScrollView(
padding: UIConstants.getResponsivePadding(
context,
mobile: const EdgeInsets.all(UIConstants.spacing16),
tablet: const EdgeInsets.all(UIConstants.spacing24),
desktop: const EdgeInsets.all(UIConstants.spacing32),
),
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: size.height -
MediaQuery.of(context).padding.top -
MediaQuery.of(context).padding.bottom,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Logo Section
_buildLogoSection(colorScheme),
SizedBox(
height: UIConstants.getResponsiveSpacing(
context,
mobile: UIConstants.spacing32,
tablet: UIConstants.spacing40,
desktop: UIConstants.spacing48,
)),
// Welcome Text
_buildWelcomeSection(colorScheme),
SizedBox(
height: UIConstants.getResponsiveSpacing(
context,
mobile: UIConstants.spacing32,
tablet: UIConstants.spacing40,
desktop: UIConstants.spacing48,
)),
// Login Form
_buildLoginForm(colorScheme),
SizedBox(
height: UIConstants.getResponsiveSpacing(
context,
mobile: UIConstants.spacing20,
tablet: UIConstants.spacing24,
desktop: UIConstants.spacing32,
)),
// Login Button
_buildLoginButton(colorScheme),
SizedBox(
height: UIConstants.getResponsiveSpacing(
context,
mobile: UIConstants.spacing32,
tablet: UIConstants.spacing40,
desktop: UIConstants.spacing48,
)),
// Sign Up Section
_buildSignUpSection(colorScheme),
SizedBox(
height: UIConstants.getResponsiveSpacing(
context,
mobile: UIConstants.spacing20,
tablet: UIConstants.spacing24,
desktop: UIConstants.spacing32,
)),
// Social Login Section
_buildSocialLoginSection(colorScheme),
],
),
),
),
),
],
),
),
);
},
);
}
Widget _buildParticleBackground(ColorScheme colorScheme) {
return AnimatedBuilder(
animation: _particleRotationAnimation,
builder: (context, child) {
return CustomPaint(
painter: ParticlePainter(
rotation: _particleRotationAnimation.value,
color: colorScheme.surface.withOpacity(0.1),
),
size: Size.infinite,
);
},
);
}
Widget _buildGlassmorphismBackground(ColorScheme colorScheme) {
return AnimatedBuilder(
animation: _backgroundAnimationController,
builder: (context, child) {
return FadeTransition(
opacity: _backgroundOpacityAnimation,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
colorScheme.surface.withOpacity(0.05),
colorScheme.surface.withOpacity(0.1),
colorScheme.surface.withOpacity(0.05),
],
),
),
),
);
},
);
}
Widget _buildLogoSection(ColorScheme colorScheme) {
return AnimatedBuilder(
animation: _logoAnimationController,
builder: (context, child) {
return Transform.scale(
scale: _logoScaleAnimation.value,
child: Transform.rotate(
angle: _logoRotationAnimation.value,
child: Container(
width: UIConstants.getResponsiveValue(
context,
mobile: UIConstants.logoSizeLarge, // Reduced from 1.5x
tablet: UIConstants.logoSizeXLarge, // Reduced from 1.5x
desktop: UIConstants.logoSizeXLarge, // Reduced from 1.5x
),
height: UIConstants.getResponsiveValue(
context,
mobile: UIConstants.logoSizeLarge, // Reduced from 1.5x
tablet: UIConstants.logoSizeXLarge, // Reduced from 1.5x
desktop: UIConstants.logoSizeXLarge, // Reduced from 1.5x
),
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
colorScheme.surface.withOpacity(0.2),
colorScheme.surface.withOpacity(0.1),
],
),
border: Border.all(
color: colorScheme.surface.withOpacity(0.3),
width: 2,
),
boxShadow: [
BoxShadow(
color: colorScheme.primary.withOpacity(0.3),
blurRadius: 30,
offset: const Offset(0, 15),
spreadRadius: 5,
),
BoxShadow(
color: colorScheme.secondary.withOpacity(0.2),
blurRadius: 50,
offset: const Offset(0, 25),
spreadRadius: 10,
),
],
),
child: Center(
child: Icon(
Icons.security,
size: UIConstants.getResponsiveValue(
context,
mobile: UIConstants.iconSizeLarge,
tablet: UIConstants.iconSizeXLarge,
desktop: UIConstants.iconSizeXLarge,
),
color: colorScheme.onSurface,
),
),
),
),
);
},
);
}
Widget _buildWelcomeSection(ColorScheme colorScheme) {
return AnimatedBuilder(
animation: _pageAnimationController,
builder: (context, child) {
return FadeTransition(
opacity: _pageFadeAnimation,
child: SlideTransition(
position: _pageSlideAnimation,
child: Container(
padding: UIConstants.cardPaddingMedium,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.95),
borderRadius: BorderRadius.circular(UIConstants.radius20),
border: Border.all(
color: Colors.white.withOpacity(0.3),
width: 1,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 20,
offset: const Offset(0, 10),
spreadRadius: 5,
),
],
),
child: Column(
children: [
Text(
'Welcome Back!',
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
color: colorScheme.primary,
fontWeight: FontWeight.w700,
letterSpacing: 0.5,
),
textAlign: TextAlign.center,
),
const SizedBox(height: UIConstants.spacing12),
Text(
'Sign in to continue to your account',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurface.withOpacity(0.8),
fontWeight: FontWeight.w400,
),
textAlign: TextAlign.center,
),
],
),
),
),
);
},
);
}
Widget _buildLoginForm(ColorScheme colorScheme) {
return AnimatedBuilder(
animation: _formAnimationController,
builder: (context, child) {
return Transform.scale(
scale: _formScaleAnimation.value,
child: Container(
width: UIConstants.getResponsiveValue(
context,
mobile: double.infinity,
tablet: 450,
desktop: 500,
),
padding: UIConstants.cardPaddingLarge,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.95),
borderRadius: BorderRadius.circular(UIConstants.radius24),
border: Border.all(
color: Colors.white.withOpacity(0.3),
width: 1,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.15),
blurRadius: 25,
offset: const Offset(0, 15),
spreadRadius: 8,
),
],
),
child: Column(
children: [
// Email Field
_buildEnhancedTextField(
label: 'Email Address',
hint: 'Enter your email',
controller: _emailController,
focusNode: _emailFocusNode,
keyboardType: TextInputType.emailAddress,
prefixIcon: Icons.email_outlined,
isFocused: _isEmailFocused,
colorScheme: colorScheme,
textInputAction: TextInputAction.next,
onSubmitted: (_) => _passwordFocusNode.requestFocus(),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$')
.hasMatch(value)) {
return 'Please enter a valid email';
}
return null;
},
),
const SizedBox(height: UIConstants.spacing24),
// Password Field
_buildEnhancedTextField(
label: 'Password',
hint: 'Enter your password',
controller: _passwordController,
focusNode: _passwordFocusNode,
obscureText: !_isPasswordVisible,
prefixIcon: Icons.lock_outlined,
isFocused: _isPasswordFocused,
colorScheme: colorScheme,
suffixIcon: IconButton(
icon: Icon(
_isPasswordVisible
? Icons.visibility
: Icons.visibility_off,
color: colorScheme.onSurface.withOpacity(0.6),
),
onPressed: () {
setState(() {
_isPasswordVisible = !_isPasswordVisible;
});
},
),
textInputAction: TextInputAction.done,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}
if (value.length < 6) {
return 'Password must be at least 6 characters';
}
return null;
},
),
const SizedBox(height: UIConstants.spacing16),
// Forgot Password - Now below password field
_buildForgotPasswordSection(colorScheme),
],
),
),
);
},
);
}
Widget _buildEnhancedTextField({
required String label,
required String hint,
required TextEditingController controller,
required FocusNode focusNode,
required IconData prefixIcon,
required bool isFocused,
required ColorScheme colorScheme,
bool obscureText = false,
Widget? suffixIcon,
TextInputType? keyboardType,
TextInputAction? textInputAction,
Function(String)? onSubmitted,
String? Function(String?)? validator,
}) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(UIConstants.radius16),
border: Border.all(
color: isFocused ? colorScheme.primary : Colors.grey.withOpacity(0.3),
width: isFocused ? 2 : 1,
),
boxShadow: isFocused
? [
BoxShadow(
color: colorScheme.primary.withOpacity(0.2),
blurRadius: 12,
spreadRadius: 3,
),
]
: null,
),
child: ModernTextField(
label: label,
hint: hint,
controller: controller,
focusNode: focusNode,
obscureText: obscureText,
keyboardType: keyboardType,
prefixIcon: Icon(
prefixIcon,
color: isFocused ? colorScheme.primary : Colors.grey.withOpacity(0.7),
),
suffixIcon: suffixIcon,
textInputAction: textInputAction,
onSubmitted: onSubmitted,
validator: validator,
),
);
}
Widget _buildForgotPasswordSection(ColorScheme colorScheme) {
return AnimatedBuilder(
animation: _formAnimationController,
builder: (context, child) {
return FadeTransition(
opacity: _formScaleAnimation,
child: Align(
alignment: Alignment.centerLeft,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: UIConstants.spacing12,
vertical: UIConstants.spacing6,
),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.9),
borderRadius: BorderRadius.circular(UIConstants.radius12),
border: Border.all(
color: colorScheme.primary.withOpacity(0.3),
width: 1,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 6,
offset: const Offset(0, 2),
spreadRadius: 1,
),
],
),
child: TextButton(
onPressed: () {
// Handle forgot password action
},
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.help_outline,
color: colorScheme.primary,
size: UIConstants.iconSizeSmall,
),
const SizedBox(width: UIConstants.spacing6),
Text(
'Forgot Password?',
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: colorScheme.primary,
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
],
),
),
),
),
);
},
);
}
Widget _buildLoginButton(ColorScheme colorScheme) {
return AnimatedBuilder(
animation: _formAnimationController,
builder: (context, child) {
return Transform.scale(
scale: _formScaleAnimation.value,
child: Container(
width: UIConstants.getResponsiveValue(
context,
mobile: double.infinity,
tablet: 450,
desktop: 500,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(UIConstants.radius20),
boxShadow: [
BoxShadow(
color: colorScheme.primary.withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 10),
spreadRadius: 5,
),
],
),
child: Consumer<AuthViewModel>(
builder: (context, provider, child) {
return ModernButton(
text: 'Sign In',
type: ModernButtonType.primary,
size: ModernButtonSize.large,
isLoading: provider.isLoading,
isDisabled: !_isFormValid,
onPressed: _isFormValid
? () {
final data = {
"email": _emailController.text,
"password": _passwordController.text,
};
provider.login(context, data);
}
: null,
icon: Icon(Icons.login),
);
},
),
),
);
},
);
}
Widget _buildSignUpSection(ColorScheme colorScheme) {
return AnimatedBuilder(
animation: _formAnimationController,
builder: (context, child) {
return FadeTransition(
opacity: _formScaleAnimation,
child: Container(
padding: UIConstants.cardPaddingMedium,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.9),
borderRadius: BorderRadius.circular(UIConstants.radius20),
border: Border.all(
color: Colors.white.withOpacity(0.3),
width: 1,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 15,
offset: const Offset(0, 8),
spreadRadius: 3,
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Don't have an account? ",
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.grey.shade700,
fontWeight: FontWeight.w500,
),
),
TextButton(
onPressed: () {
Navigator.pushNamed(context, RouteNames.getOtpView);
},
child: Text(
'Sign Up',
style: Theme.of(context).textTheme.labelLarge?.copyWith(
color: colorScheme.primary,
fontWeight: FontWeight.w700,
fontSize: 16,
),
),
),
],
),
),
);
},
);
}
Widget _buildSocialLoginSection(ColorScheme colorScheme) {
return AnimatedBuilder(
animation: _formAnimationController,
builder: (context, child) {
return FadeTransition(
opacity: _formScaleAnimation,
child: Column(
children: [
Row(
children: [
Expanded(
child: Container(
height: 1,
color: Colors.white.withOpacity(0.4),
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: UIConstants.spacing16),
child: Text(
'Or continue with',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.white.withOpacity(0.9),
fontWeight: FontWeight.w500,
),
),
),
Expanded(
child: Container(
height: 1,
color: Colors.white.withOpacity(0.4),
),
),
],
),
const SizedBox(height: UIConstants.spacing24),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildSocialButton(
icon: Icons.g_mobiledata,
label: 'Google',
colorScheme: colorScheme,
onPressed: () {
// Handle Google sign in
},
),
const SizedBox(width: UIConstants.spacing16),
_buildSocialButton(
icon: Icons.apple,
label: 'Apple',
colorScheme: colorScheme,
onPressed: () {
// Handle Apple sign in
},
),
],
),
],
),
);
},
);
}
Widget _buildSocialButton({
required IconData icon,
required String label,
required ColorScheme colorScheme,
required VoidCallback onPressed,
}) {
return Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.95),
borderRadius: BorderRadius.circular(UIConstants.radius16),
border: Border.all(
color: Colors.white.withOpacity(0.3),
width: 1,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 5),
spreadRadius: 2,
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onPressed,
borderRadius: BorderRadius.circular(UIConstants.radius16),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: UIConstants.spacing20,
vertical: UIConstants.spacing12,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
icon,
size: UIConstants.iconSizeMedium,
color: colorScheme.primary,
),
const SizedBox(width: UIConstants.spacing8),
Text(
label,
style: Theme.of(context).textTheme.labelMedium?.copyWith(
color: colorScheme.primary,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
),
);
}
}
class ParticlePainter extends CustomPainter {
final double rotation;
final Color color;
ParticlePainter({required this.rotation, required this.color});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..style = PaintingStyle.fill;
final center = Offset(size.width / 2, size.height / 2);
final radius = math.min(size.width, size.height) / 2.5;
// Draw rotating particles
for (int i = 0; i < 12; i++) {
final angle = (i * math.pi / 6) + rotation;
final x = center.dx + radius * math.cos(angle);
final y = center.dy + radius * math.sin(angle);
canvas.drawCircle(
Offset(x, y),
4,
paint,
);
}
}
@override
bool shouldRepaint(ParticlePainter oldDelegate) => true;
}