import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'login.dart'; import 'screens/improved_home_screen.dart'; import 'services/auth_service.dart'; import 'services/server_discovery_service.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle( statusBarColor: Colors.transparent, statusBarIconBrightness: Brightness.light, ), ); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); static const _primary = Color(0xFF1565C0); static const _primaryDark = Color(0xFF0D47A1); @override Widget build(BuildContext context) { return MaterialApp( title: 'SPK Rekomendasi Kontrakan & Laundry', theme: ThemeData( colorScheme: ColorScheme.fromSeed( seedColor: _primary, primary: _primary, secondary: _primaryDark, surface: Colors.white, brightness: Brightness.light, ), useMaterial3: true, scaffoldBackgroundColor: const Color(0xFFF7F8FC), appBarTheme: const AppBarTheme( backgroundColor: _primary, foregroundColor: Colors.white, elevation: 0, centerTitle: true, titleTextStyle: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, color: Colors.white, ), ), cardTheme: CardThemeData( elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), color: Colors.white, ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( elevation: 0, padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 24), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), textStyle: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600, letterSpacing: 0.3, ), ), ), inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: const Color(0xFFF7F8FC), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Color(0xFFE0E0E0)), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Color(0xFFE0E0E0)), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: _primary, width: 1.5), ), ), dividerTheme: const DividerThemeData( color: Color(0xFFEEEEEE), thickness: 1, space: 0, ), ), home: const SplashScreen(), debugShowCheckedModeBanner: false, ); } } // Splash screen dengan animasi class SplashScreen extends StatefulWidget { const SplashScreen({super.key}); @override State createState() => _SplashScreenState(); } class _SplashScreenState extends State with SingleTickerProviderStateMixin { final _authService = AuthService(); late AnimationController _animController; late Animation _fadeAnim; late Animation _scaleAnim; String _statusText = 'Memulai aplikasi...'; @override void initState() { super.initState(); _animController = AnimationController( vsync: this, duration: const Duration(milliseconds: 1200), ); _fadeAnim = Tween(begin: 0, end: 1).animate( CurvedAnimation(parent: _animController, curve: Curves.easeOut), ); _scaleAnim = Tween(begin: 0.7, end: 1).animate( CurvedAnimation(parent: _animController, curve: Curves.elasticOut), ); _animController.forward(); _checkAuth(); } @override void dispose() { _animController.dispose(); super.dispose(); } Future _checkAuth() async { // 🔍 Auto-detect server IP await ServerDiscoveryService.discover( onStatus: (status) { if (mounted) setState(() => _statusText = status); }, ); if (mounted) setState(() => _statusText = 'Memeriksa sesi...'); await _authService.loadToken(); await Future.delayed(const Duration(milliseconds: 400)); if (!mounted) return; final destination = _authService.isAuthenticated ? const ImprovedHomeScreen() : const LoginScreen(); Navigator.pushReplacement( context, PageRouteBuilder( pageBuilder: (_, __, ___) => destination, transitionsBuilder: (_, animation, __, child) { return FadeTransition(opacity: animation, child: child); }, transitionDuration: const Duration(milliseconds: 500), ), ); } @override Widget build(BuildContext context) { return Scaffold( body: Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color(0xFF1565C0), Color(0xFF0D47A1), Color(0xFF1A237E), ], ), ), child: Center( child: FadeTransition( opacity: _fadeAnim, child: ScaleTransition( scale: _scaleAnim, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( padding: const EdgeInsets.all(28), decoration: BoxDecoration( color: Colors.white, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.15), blurRadius: 30, offset: const Offset(0, 10), ), ], ), child: const Icon( Icons.school_rounded, size: 64, color: Color(0xFF1565C0), ), ), const SizedBox(height: 32), const Text( 'SPK Rekomendasi', style: TextStyle( fontSize: 30, fontWeight: FontWeight.w800, color: Colors.white, letterSpacing: 0.5, ), ), const SizedBox(height: 8), Text( 'Kontrakan & Laundry Terbaik untuk Mahasiswa', style: TextStyle( fontSize: 14, color: Colors.white.withOpacity(0.8), letterSpacing: 0.3, ), ), const SizedBox(height: 48), SizedBox( width: 32, height: 32, child: CircularProgressIndicator( strokeWidth: 2.5, valueColor: AlwaysStoppedAnimation( Colors.white.withOpacity(0.9), ), ), ), const SizedBox(height: 16), Text( _statusText, style: TextStyle( fontSize: 13, color: Colors.white.withOpacity(0.7), ), ), ], ), ), ), ), ), ); } }