add some features 3

This commit is contained in:
oxel 2026-04-08 11:35:01 +07:00
parent c4f7c1b3a2
commit dba2f0e4a6
2 changed files with 182 additions and 1 deletions

View File

@ -0,0 +1,180 @@
import 'package:flutter/material.dart';
import 'package:monitoring_jamur/core/theme/app_theme.dart';
import 'package:monitoring_jamur/core/session/user_session.dart';
import 'package:monitoring_jamur/features/auth/presentation/pages/login_page.dart';
import 'package:monitoring_jamur/features/home/presentation/pages/main_screen.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({super.key});
@override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderStateMixin {
late AnimationController _logoController;
late Animation<double> _logoScale;
late Animation<double> _logoOpacity;
@override
void initState() {
super.initState();
_logoController = AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this,
);
_logoScale = Tween<double>(begin: 0.5, 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.easeIn),
);
_logoController.forward();
// Navigate to next screen after animation
Future.delayed(const Duration(milliseconds: 3500), () {
_checkSessionAndNavigate();
});
}
void _checkSessionAndNavigate() {
if (!mounted) return;
Widget nextScreen = UserSession.isLoggedIn ? const MainScreen() : const LoginPage();
Navigator.of(context).pushReplacement(
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => nextScreen,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(opacity: animation, child: child);
},
transitionDuration: const Duration(milliseconds: 800),
),
);
}
@override
void dispose() {
_logoController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppTheme.backgroundBeige,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ScaleTransition(
scale: _logoScale,
child: FadeTransition(
opacity: _logoOpacity,
child: Image.asset(
'lib/assets/mushroom.png',
height: 150,
),
),
),
const SizedBox(height: 32),
_buildAnimatedTextLine("mushrooms", 1000),
const SizedBox(height: 8),
_buildAnimatedTextLine("monitoring apps", 1800, isSecondary: true),
],
),
),
);
}
Widget _buildAnimatedTextLine(String text, int startDelayMs, {bool isSecondary = false}) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: text.split('').asMap().entries.map((entry) {
return AnimatedCharacter(
char: entry.value,
delay: Duration(milliseconds: startDelayMs + (entry.key * 70)),
isSecondary: isSecondary,
);
}).toList(),
);
}
}
class AnimatedCharacter extends StatefulWidget {
final String char;
final Duration delay;
final bool isSecondary;
const AnimatedCharacter({
super.key,
required this.char,
required this.delay,
this.isSecondary = false,
});
@override
State<AnimatedCharacter> createState() => _AnimatedCharacterState();
}
class _AnimatedCharacterState extends State<AnimatedCharacter> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Offset> _offsetAnimation;
late Animation<double> _opacityAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: this,
);
_offsetAnimation = Tween<Offset>(
begin: const Offset(3.0, 0.0),
end: Offset.zero,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeOutCubic,
));
_opacityAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeIn,
));
Future.delayed(widget.delay, () {
if (mounted) _controller.forward();
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _opacityAnimation,
child: SlideTransition(
position: _offsetAnimation,
child: Text(
widget.char == ' ' ? '\u00A0' : widget.char,
style: TextStyle(
fontSize: widget.isSecondary ? 18 : 32,
fontWeight: widget.isSecondary ? FontWeight.w500 : FontWeight.bold,
color: widget.isSecondary ? AppTheme.textLight : AppTheme.primaryGreen,
letterSpacing: widget.isSecondary ? 1.5 : 2.0,
),
),
),
);
}
}

View File

@ -3,6 +3,7 @@ import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:monitoring_jamur/core/constants/supabase_config.dart'; import 'package:monitoring_jamur/core/constants/supabase_config.dart';
import 'package:monitoring_jamur/core/theme/app_theme.dart'; import 'package:monitoring_jamur/core/theme/app_theme.dart';
import 'package:monitoring_jamur/features/auth/presentation/pages/login_page.dart'; import 'package:monitoring_jamur/features/auth/presentation/pages/login_page.dart';
import 'package:monitoring_jamur/features/auth/presentation/pages/splash_screen.dart';
import 'package:monitoring_jamur/features/home/presentation/pages/main_screen.dart'; import 'package:monitoring_jamur/features/home/presentation/pages/main_screen.dart';
import 'package:monitoring_jamur/core/session/user_session.dart'; import 'package:monitoring_jamur/core/session/user_session.dart';
@ -28,7 +29,7 @@ class MyApp extends StatelessWidget {
title: 'Mushroom Monitor', title: 'Mushroom Monitor',
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
theme: AppTheme.lightTheme, theme: AppTheme.lightTheme,
home: UserSession.isLoggedIn ? const MainScreen() : const LoginPage(), home: const SplashScreen(),
); );
} }
} }