hesabixArc/hesabixUI/hesabix_ui/lib/widgets/progress_splash_screen.dart

297 lines
10 KiB
Dart
Raw Normal View History

import 'package:flutter/material.dart';
import 'package:hesabix_ui/l10n/app_localizations.dart';
import 'dart:async';
class ProgressSplashScreen extends StatefulWidget {
final String? message;
final bool showLogo;
final Color? backgroundColor;
final Color? primaryColor;
final Duration minimumDisplayDuration;
final VoidCallback? onComplete;
const ProgressSplashScreen({
super.key,
this.message,
this.showLogo = true,
this.backgroundColor,
this.primaryColor,
this.minimumDisplayDuration = const Duration(seconds: 2),
this.onComplete,
});
@override
State<ProgressSplashScreen> createState() => _ProgressSplashScreenState();
}
class _ProgressSplashScreenState extends State<ProgressSplashScreen>
with TickerProviderStateMixin {
late AnimationController _fadeController;
late AnimationController _scaleController;
late AnimationController _progressController;
late Animation<double> _fadeAnimation;
late Animation<double> _scaleAnimation;
late Animation<double> _progressAnimation;
Timer? _countdownTimer;
int _remainingSeconds = 0;
@override
void initState() {
super.initState();
_remainingSeconds = widget.minimumDisplayDuration.inSeconds;
_fadeController = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
);
_scaleController = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: this,
);
_progressController = AnimationController(
duration: widget.minimumDisplayDuration,
vsync: this,
);
_fadeAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _fadeController,
curve: Curves.easeInOut,
));
_scaleAnimation = Tween<double>(
begin: 0.8,
end: 1.0,
).animate(CurvedAnimation(
parent: _scaleController,
curve: Curves.elasticOut,
));
_progressAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _progressController,
curve: Curves.easeInOut,
));
// Start animations
_fadeController.forward();
_scaleController.forward();
_progressController.forward();
// Start countdown timer
_startCountdown();
}
void _startCountdown() {
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (mounted) {
setState(() {
_remainingSeconds = widget.minimumDisplayDuration.inSeconds - timer.tick;
if (_remainingSeconds <= 0) {
timer.cancel();
widget.onComplete?.call();
}
});
}
});
}
@override
void dispose() {
_fadeController.dispose();
_scaleController.dispose();
_progressController.dispose();
_countdownTimer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
final isDark = theme.brightness == Brightness.dark;
final bgColor = widget.backgroundColor ?? colorScheme.surface;
final primary = widget.primaryColor ?? colorScheme.primary;
return Scaffold(
backgroundColor: bgColor,
body: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: isDark
? [
bgColor,
bgColor.withOpacity(0.95),
]
: [
bgColor,
bgColor.withOpacity(0.98),
],
),
),
child: AnimatedBuilder(
animation: Listenable.merge([_fadeAnimation, _scaleAnimation, _progressAnimation]),
builder: (context, child) {
return Transform.scale(
scale: _scaleAnimation.value,
child: Opacity(
opacity: _fadeAnimation.value,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Logo Section
if (widget.showLogo) ...[
Container(
width: 120,
height: 120,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: primary.withOpacity(0.2),
blurRadius: 20,
spreadRadius: 2,
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image.asset(
isDark ? 'assets/images/logo-light.png' : 'assets/images/logo-blue.png',
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) {
return Container(
decoration: BoxDecoration(
color: primary,
borderRadius: BorderRadius.circular(20),
),
child: Icon(
Icons.account_balance,
size: 60,
color: colorScheme.onPrimary,
),
);
},
),
),
),
const SizedBox(height: 32),
],
// App Name
Text(
'Hesabix',
style: theme.textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
letterSpacing: 1.2,
),
),
const SizedBox(height: 8),
// Subtitle
Text(
AppLocalizations.of(context)?.businessManagementPlatform ?? 'Business Management Platform',
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
fontWeight: FontWeight.w400,
),
),
const SizedBox(height: 48),
// Loading Indicator with Progress
Column(
children: [
SizedBox(
width: 40,
height: 40,
child: CircularProgressIndicator(
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation<Color>(primary),
),
),
const SizedBox(height: 24),
// Progress Bar
Container(
width: 200,
height: 4,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2),
color: colorScheme.surfaceContainerHighest,
),
child: AnimatedBuilder(
animation: _progressAnimation,
builder: (context, child) {
return FractionallySizedBox(
alignment: Alignment.centerLeft,
widthFactor: _progressAnimation.value,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2),
gradient: LinearGradient(
colors: [primary, primary.withOpacity(0.8)],
),
),
),
);
},
),
),
const SizedBox(height: 16),
// Loading Message
Text(
widget.message ?? AppLocalizations.of(context)?.loading ?? 'Loading...',
style: theme.textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurfaceVariant,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 8),
// Countdown Timer
if (_remainingSeconds > 0)
Text(
'${_remainingSeconds}s',
style: theme.textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant.withOpacity(0.7),
fontWeight: FontWeight.w400,
),
),
],
),
const SizedBox(height: 80),
// Version Info
Text(
'Version 1.0.0',
style: theme.textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant.withOpacity(0.6),
),
),
],
),
),
);
},
),
),
);
}
}