2025-09-06 19:21:52 +05:30

493 lines
15 KiB
Dart

import 'dart:io';
import 'dart:math' as math;
import 'package:base_project/core/constants/ui_constants.dart';
import 'package:base_project/view_model/splash_view_model.dart';
import 'package:base_project/view_model/system_params/system_params_view_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../core/providers/dynamic_theme_provider.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({super.key});
@override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen>
with TickerProviderStateMixin {
late AnimationController _logoController;
late AnimationController _textController;
late AnimationController _particleController;
late AnimationController _progressController;
late Animation<double> _logoScale;
late Animation<double> _logoOpacity;
late Animation<double> _textOpacity;
late Animation<double> _textSlide;
late Animation<double> _progressValue;
late Animation<double> _particleRotation;
@override
void initState() {
super.initState();
_initializeAnimations();
_startSplashSequence();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// Listen to dynamic theme changes to ensure colors update
final dynamicThemeProvider =
Provider.of<DynamicThemeProvider>(context, listen: false);
dynamicThemeProvider.addListener(_onThemeChanged);
}
void _onThemeChanged() {
// Force rebuild when dynamic theme changes
if (mounted) {
print('Splash Screen: Theme changed, rebuilding...');
setState(() {});
}
}
// Method to force refresh colors (can be called externally if needed)
void refreshColors() {
if (mounted) {
print('Splash Screen: Colors refreshed manually');
setState(() {});
}
}
void _initializeAnimations() {
// Logo animation controller
_logoController = AnimationController(
duration: UIConstants.durationSlow,
vsync: this,
);
// Text animation controller
_textController = AnimationController(
duration: UIConstants.durationNormal,
vsync: this,
);
// Particle animation controller
_particleController = AnimationController(
duration: const Duration(seconds: 3),
vsync: this,
);
// Progress animation controller
_progressController = AnimationController(
duration: const Duration(seconds: 3),
vsync: this,
);
// Logo animations
_logoScale = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _logoController,
curve: Curves.elasticOut,
));
_logoOpacity = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _logoController,
curve: Curves.easeInOut,
));
// Text animations
_textOpacity = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _textController,
curve: Curves.easeInOut,
));
_textSlide = Tween<double>(
begin: 50.0,
end: 0.0,
).animate(CurvedAnimation(
parent: _textController,
curve: Curves.easeOutCubic,
));
// Progress animation
_progressValue = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _progressController,
curve: Curves.easeInOut,
));
// Particle rotation
_particleRotation = Tween<double>(
begin: 0.0,
end: 2 * math.pi,
).animate(CurvedAnimation(
parent: _particleController,
curve: Curves.linear,
));
}
void _startSplashSequence() async {
// Start logo animation
await _logoController.forward();
// Start text animation
await Future.delayed(const Duration(milliseconds: 300));
_textController.forward();
// Start progress and particle animations
await Future.delayed(const Duration(milliseconds: 200));
_progressController.forward();
_particleController.repeat();
// Check navigation after animations
SplashViewModel().checkNavigation(context);
}
@override
void dispose() {
// Remove listener before disposing
try {
final dynamicThemeProvider =
Provider.of<DynamicThemeProvider>(context, listen: false);
dynamicThemeProvider.removeListener(_onThemeChanged);
} catch (e) {
// Provider might not be available during dispose
}
_logoController.dispose();
_textController.dispose();
_particleController.dispose();
_progressController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// Always use dynamic theme provider for consistent colors
return Consumer<DynamicThemeProvider>(
builder: (context, dynamicThemeProvider, child) {
// Get the current dynamic color scheme
final colorScheme = dynamicThemeProvider.getCurrentColorScheme(
Theme.of(context).brightness == Brightness.dark);
// Debug information for theme tracking
if (dynamicThemeProvider.isUsingDynamicTheme) {
print(
'Splash Screen: Using dynamic theme with primary color: ${colorScheme.primary}');
} else {
print(
'Splash Screen: Using default theme with primary color: ${colorScheme.primary}');
}
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
colorScheme.primary,
colorScheme.primaryContainer,
colorScheme.secondary,
],
stops: const [0.0, 0.5, 1.0],
),
),
child: Stack(
children: [
// Animated background particles
_buildParticleBackground(),
// Main content
SafeArea(
child: Padding(
padding: UIConstants.screenPaddingLarge,
child: Column(
children: [
// Top section with logo
Expanded(
flex: 3,
child: _buildLogoSection(colorScheme),
),
// Middle section with text
Expanded(
flex: 2,
child: _buildTextSection(colorScheme),
),
// Bottom section with progress
Expanded(
flex: 1,
child: _buildProgressSection(colorScheme),
),
],
),
),
),
],
),
),
);
},
);
}
Widget _buildParticleBackground() {
return Consumer<DynamicThemeProvider>(
builder: (context, dynamicThemeProvider, child) {
final colorScheme = dynamicThemeProvider.getCurrentColorScheme(
Theme.of(context).brightness == Brightness.dark);
return AnimatedBuilder(
animation: _particleRotation,
builder: (context, child) {
return CustomPaint(
painter: ParticlePainter(
rotation: _particleRotation.value,
color: colorScheme.primary.withOpacity(0.15),
),
size: Size.infinite,
);
},
);
},
);
}
Widget _buildLogoSection(ColorScheme colorScheme) {
return Center(
child: AnimatedBuilder(
animation: _logoController,
builder: (context, child) {
return Transform.scale(
scale: _logoScale.value,
child: Opacity(
opacity: _logoOpacity.value,
child: Container(
width: UIConstants.logoSizeXLarge * 2,
height: UIConstants.logoSizeXLarge * 2,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: colorScheme.surface.withOpacity(0.2),
border: Border.all(
color: colorScheme.primary.withOpacity(0.3),
width: 2,
),
boxShadow: [
BoxShadow(
color: colorScheme.primary.withOpacity(0.2),
blurRadius: 20,
spreadRadius: 5,
),
BoxShadow(
color: colorScheme.secondary.withOpacity(0.1),
blurRadius: 40,
spreadRadius: 10,
),
],
),
child: Center(
child: Consumer<SystemParamsViewModel>(
builder: (context, provider, _) {
if (provider.profileImg != null) {
return ClipOval(
child: Image.file(
File(provider.profileImg!.path),
fit: BoxFit.cover,
width: UIConstants.logoSizeXLarge * 1.5,
height: UIConstants.logoSizeXLarge * 1.5,
),
);
} else {
return Icon(
Icons.security,
size: UIConstants.logoSizeXLarge,
color: colorScheme.onSurface,
);
}
},
),
),
),
),
);
},
),
);
}
Widget _buildTextSection(ColorScheme colorScheme) {
return AnimatedBuilder(
animation: _textController,
builder: (context, child) {
return Transform.translate(
offset: Offset(0, _textSlide.value),
child: Opacity(
opacity: _textOpacity.value,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"AuthSec",
style: Theme.of(context).textTheme.displayLarge?.copyWith(
color: colorScheme.onSurface,
fontWeight: FontWeight.bold,
letterSpacing: 2.0,
),
),
const SizedBox(height: UIConstants.spacing16),
Text(
"Secure Authentication System",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: colorScheme.onSurface.withOpacity(0.9),
fontWeight: FontWeight.w300,
letterSpacing: 1.0,
),
),
const SizedBox(height: UIConstants.spacing24),
Container(
padding: UIConstants.cardPaddingMedium,
decoration: BoxDecoration(
color: colorScheme.surface.withOpacity(0.2),
borderRadius: BorderRadius.circular(UIConstants.radius20),
border: Border.all(
color: colorScheme.primary.withOpacity(0.3),
width: 1,
),
boxShadow: [
BoxShadow(
color: colorScheme.primary.withOpacity(0.1),
blurRadius: 10,
spreadRadius: 2,
),
],
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.shield,
color: colorScheme.primary,
size: UIConstants.iconSizeMedium,
),
const SizedBox(width: UIConstants.spacing12),
Text(
"Enterprise Security",
style:
Theme.of(context).textTheme.titleMedium?.copyWith(
color: colorScheme.onSurface,
fontWeight: FontWeight.w500,
),
),
],
),
),
],
),
),
);
},
);
}
Widget _buildProgressSection(ColorScheme colorScheme) {
return AnimatedBuilder(
animation: _progressController,
builder: (context, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Progress bar
Container(
width: double.infinity,
height: 4,
decoration: BoxDecoration(
color: colorScheme.surface.withOpacity(0.3),
borderRadius: BorderRadius.circular(UIConstants.radiusFull),
border: Border.all(
color: colorScheme.primary.withOpacity(0.2),
width: 1,
),
),
child: FractionallySizedBox(
alignment: Alignment.centerLeft,
widthFactor: _progressValue.value,
child: Container(
decoration: BoxDecoration(
color: colorScheme.primary,
borderRadius: BorderRadius.circular(UIConstants.radiusFull),
boxShadow: [
BoxShadow(
color: colorScheme.primary.withOpacity(0.5),
blurRadius: 10,
spreadRadius: 2,
),
],
),
),
),
),
const SizedBox(height: UIConstants.spacing16),
Text(
"Initializing...",
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurface.withOpacity(0.8),
fontWeight: FontWeight.w400,
),
),
],
);
},
);
}
}
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) / 3;
// Draw rotating particles
for (int i = 0; i < 8; i++) {
final angle = (i * math.pi / 4) + rotation;
final x = center.dx + radius * math.cos(angle);
final y = center.dy + radius * math.sin(angle);
canvas.drawCircle(
Offset(x, y),
3,
paint,
);
}
}
@override
bool shouldRepaint(ParticlePainter oldDelegate) => true;
}