import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:dio/dio.dart'; import '../../../core/api_service.dart'; import '../../../core/secure_storage.dart'; import '../../home/presentation/guardian_dashboard_screen.dart'; import '../../home/presentation/user_dashboard_screen.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State { final _emailCtrl = TextEditingController(); final _passCtrl = TextEditingController(); final _apiService = ApiService(); final _secureStorage = SecureStorage(); bool _isLoading = false; bool _showPass = false; int _selectedTab = 0; // 0 = Guardian, 1 = User final _hints = [ ['guardian@walkguide.com', 'guardian123'], ['user@walkguide.com', 'user123'], ]; @override void initState() { super.initState(); _emailCtrl.text = _hints[0][0]; _passCtrl.text = _hints[0][1]; } void _switchTab(int idx) { setState(() { _selectedTab = idx; _emailCtrl.text = _hints[idx][0]; _passCtrl.text = _hints[idx][1]; }); } Future _handleLogin() async { setState(() => _isLoading = true); try { final res = await _apiService.post('/auth/login', { 'email': _emailCtrl.text.trim(), 'password': _passCtrl.text.trim(), }); if (res.statusCode == 200 && res.data['success'] == true) { final data = res.data['data']; await _secureStorage.saveToken(data['token']); if (!mounted) return; Navigator.pushReplacement(context, MaterialPageRoute( builder: (_) => data['role'] == 'ROLE_GUARDIAN' ? const GuardianDashboardScreen() : const UserDashboardScreen(), )); } } on DioException catch (e) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(e.response?.data['message'] ?? 'Gagal terhubung'), backgroundColor: Colors.redAccent, behavior: SnackBarBehavior.floating, )); } finally { if (mounted) setState(() => _isLoading = false); } } @override Widget build(BuildContext context) { final isWide = MediaQuery.of(context).size.width > 700; return Scaffold( backgroundColor: const Color(0xFFF1F5F9), body: Center( child: Container( constraints: const BoxConstraints(maxWidth: 900, maxHeight: 600), margin: const EdgeInsets.all(24), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.08), blurRadius: 40, offset: const Offset(0, 16), ) ], ), child: ClipRRect( borderRadius: BorderRadius.circular(20), child: Row( children: [ if (isWide) Expanded(child: _buildHeroPanel()), _buildFormPanel(), ], ), ), ), ), ); } Widget _buildHeroPanel() { return Stack( fit: StackFit.expand, children: [ Image.asset( 'assets/images/walk.jpg', fit: BoxFit.cover, errorBuilder: (_, __, ___) => Container(color: const Color(0xFF0F1923)), ), Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.transparent, Color(0xCC0A1428)], stops: [0.4, 1.0], ), ), ), Positioned( top: 24, left: 24, child: Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 7), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.12), // ✅ Fixed borderRadius: BorderRadius.circular(20), border: Border.all( color: Colors.white.withValues(alpha: 0.15), // ✅ Fixed ), ), child: Row( children: [ Container( width: 6, height: 6, decoration: const BoxDecoration( color: Color(0xFF60EFAB), shape: BoxShape.circle, ), ), const SizedBox(width: 6), Text( 'AI Navigation Active', style: GoogleFonts.inter( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w500, ), ), ], ), ), ), Positioned( bottom: 36, left: 32, right: 32, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '"WalkGuide memberi saya\nkebebasan yang luar biasa."', style: GoogleFonts.outfit( color: Colors.white, fontSize: 22, fontWeight: FontWeight.w600, height: 1.35, ), ), const SizedBox(height: 14), Text( 'Andi Pratama', style: GoogleFonts.inter( color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500, ), ), Text( 'Pengguna — Surabaya, Jawa Timur', style: GoogleFonts.inter( color: Colors.white60, fontSize: 12, ), ), const SizedBox(height: 18), Row( children: [ Container( width: 28, height: 3, decoration: BoxDecoration( color: const Color(0xFF60EFAB), borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 6), Container( width: 20, height: 3, decoration: BoxDecoration( color: Colors.white24, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 6), Container( width: 20, height: 3, decoration: BoxDecoration( color: Colors.white24, borderRadius: BorderRadius.circular(2), ), ), ], ), ], ), ), ], ); } Widget _buildFormPanel() { return Container( width: 320, color: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 36, vertical: 40), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 28, height: 28, decoration: BoxDecoration( color: const Color(0xFF1A56DB), borderRadius: BorderRadius.circular(8), ), child: const Icon(Icons.navigation_rounded, color: Colors.white, size: 16), ), const SizedBox(width: 8), Text( 'WalkGuide', style: GoogleFonts.outfit( fontSize: 15, fontWeight: FontWeight.w600, color: const Color(0xFF0F172A), ), ), ], ), const SizedBox(height: 32), Text( "Let's sign in", style: GoogleFonts.outfit( fontSize: 24, fontWeight: FontWeight.w600, color: const Color(0xFF0F172A), ), ), const SizedBox(height: 4), Text( 'Continue your journey with WalkGuide.', style: GoogleFonts.inter( fontSize: 13, color: const Color(0xFF64748B), ), ), const SizedBox(height: 24), // Tab switcher Container( padding: const EdgeInsets.all(3), decoration: BoxDecoration( color: const Color(0xFFF1F5F9), borderRadius: BorderRadius.circular(10), ), child: Row( children: [ _buildTab(0, 'Guardian'), _buildTab(1, 'User'), ], ), ), const SizedBox(height: 22), _buildLabel('Email address'), const SizedBox(height: 5), _buildInput(_emailCtrl, false), const SizedBox(height: 14), _buildLabel('Password'), const SizedBox(height: 5), _buildInput(_passCtrl, true), const SizedBox(height: 8), Align( alignment: Alignment.centerRight, child: Text( 'Forgot password?', style: GoogleFonts.inter( fontSize: 12, color: const Color(0xFF1A56DB), ), ), ), const SizedBox(height: 22), SizedBox( width: double.infinity, height: 42, child: ElevatedButton( onPressed: _isLoading ? null : _handleLogin, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF1A56DB), elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), child: _isLoading ? const SizedBox( width: 18, height: 18, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : Text( 'Continue', style: GoogleFonts.inter( fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white, ), ), ), ), const SizedBox(height: 20), Center( child: Text( 'Need help? Contact support', style: GoogleFonts.inter( fontSize: 12, color: const Color(0xFF94A3B8), ), ), ), ], ), ); } Widget _buildTab(int idx, String label) { final active = _selectedTab == idx; return Expanded( child: GestureDetector( onTap: () => _switchTab(idx), child: AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( color: active ? Colors.white : Colors.transparent, borderRadius: BorderRadius.circular(7), ), child: Text( label, textAlign: TextAlign.center, style: GoogleFonts.inter( fontSize: 13, fontWeight: active ? FontWeight.w600 : FontWeight.w400, color: active ? const Color(0xFF0F172A) : const Color(0xFF64748B), ), ), ), ), ); } Widget _buildLabel(String text) => Text( text, style: GoogleFonts.inter( fontSize: 12, color: const Color(0xFF64748B), ), ); Widget _buildInput(TextEditingController ctrl, bool isPass) { return Container( height: 40, decoration: BoxDecoration( border: Border.all(color: const Color(0xFFE2E8F0)), borderRadius: BorderRadius.circular(8), ), child: TextField( controller: ctrl, obscureText: isPass && !_showPass, style: GoogleFonts.inter( fontSize: 13, color: const Color(0xFF0F172A), ), decoration: InputDecoration( border: InputBorder.none, contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), suffixIcon: isPass ? IconButton( icon: Icon( _showPass ? Icons.visibility_off : Icons.visibility, size: 16, color: const Color(0xFF94A3B8), ), onPressed: () => setState(() => _showPass = !_showPass), ) : null, ), ), ); } }