POLISHING STUFF and Smarter AI, and alot i mean.. ALOT OF BUG FIX AND OPTIMIZE

This commit is contained in:
Wowieee4
2026-05-29 15:50:51 +07:00
parent 1110e5a42d
commit d2b3534dde
65 changed files with 4150 additions and 2974 deletions
@@ -19,6 +19,9 @@ import '../../core/services/offline_queue_service.dart';
import '../../core/services/tts_service.dart';
import '../../core/services/websocket_service.dart';
import '../../core/storage/secure_storage.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_decorations.dart';
import '../../core/theme/app_text_styles.dart';
// ---------------------------------------------------------------------------
// LoginScreen
@@ -79,7 +82,8 @@ class _LoginScreenState extends State<LoginScreen> {
},
onError: (message) => _snack(context, message),
fallback: 'Login gagal. Periksa email dan password kamu.',
connectionHint: 'Tidak bisa ke server. Pakai URL backend publik/aktif.',
connectionHint:
'Tidak bisa ke server. Cek URL backend di Connect Server dan pastikan server aktif.',
);
if (mounted) setState(() => _loading = false);
}
@@ -150,37 +154,25 @@ class _AuthFrame extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFEAF4FF),
backgroundColor: AppColors.softBlueBg,
body: LayoutBuilder(
builder: (context, constraints) {
final compact =
constraints.maxWidth < 480 || constraints.maxHeight < 720;
return Stack(
children: [
const Positioned.fill(
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFFD9EEFF), Color(0xFFF7FAFC)],
),
),
),
return DecoratedBox(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
AppColors.softBlueBg,
Colors.white,
AppColors.softPinkBg,
],
),
Positioned(
top: compact ? -70 : -90,
right: compact ? -70 : -60,
child: Container(
width: compact ? 180 : 260,
height: compact ? 180 : 260,
decoration: BoxDecoration(
color: const Color(0xFF2563EB).withValues(alpha: 0.12),
shape: BoxShape.circle,
),
),
),
Center(
),
child: SafeArea(
child: Center(
child: SingleChildScrollView(
keyboardDismissBehavior:
ScrollViewKeyboardDismissBehavior.onDrag,
@@ -206,19 +198,10 @@ class _AuthFrame extends StatelessWidget {
child: RepaintBoundary(
child: Container(
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.96),
color: AppColors.cardWhite,
borderRadius:
BorderRadius.circular(compact ? 22 : 30),
border: Border.all(
color: Colors.white.withValues(alpha: 0.8)),
boxShadow: [
BoxShadow(
color: const Color(0xFF1E3A8A)
.withValues(alpha: 0.14),
blurRadius: compact ? 24 : 40,
offset: const Offset(0, 18),
),
],
BorderRadius.circular(compact ? 22 : 28),
boxShadow: AppDecorations.cardShadow,
),
child: Padding(
padding: EdgeInsets.fromLTRB(
@@ -236,13 +219,15 @@ class _AuthFrame extends StatelessWidget {
width: compact ? 44 : 56,
height: compact ? 44 : 56,
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [
Color(0xFF2563EB),
Color(0xFF0891B2)
],
),
gradient: AppDecorations.blueGradient,
borderRadius: BorderRadius.circular(16),
boxShadow: const [
BoxShadow(
color: Color(0x334A90D9),
blurRadius: 18,
offset: Offset(0, 8),
),
],
),
child: Icon(Icons.navigation_rounded,
color: Colors.white,
@@ -256,8 +241,8 @@ class _AuthFrame extends StatelessWidget {
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w900,
color: Color(0xFF0F172A),
fontWeight: FontWeight.w800,
color: AppColors.textDark,
),
),
),
@@ -269,21 +254,22 @@ class _AuthFrame extends StatelessWidget {
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: const Color(0xFFEFF6FF),
color: AppColors.softBlueBg,
borderRadius: BorderRadius.circular(999),
),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.shield_outlined,
size: 14, color: Color(0xFF1D4ED8)),
size: 14,
color: AppColors.primaryBlue),
SizedBox(width: 6),
Text(
'Secure Assistive Navigation',
style: TextStyle(
color: Color(0xFF1D4ED8),
color: AppColors.primaryBlue,
fontSize: 11,
fontWeight: FontWeight.w800,
fontWeight: FontWeight.w700,
),
),
],
@@ -294,14 +280,10 @@ class _AuthFrame extends StatelessWidget {
title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.headlineMedium
?.copyWith(
fontSize: compact ? 26 : null,
fontWeight: FontWeight.w900,
color: const Color(0xFF0F172A),
),
style: AppTextStyles.heading.copyWith(
fontSize: compact ? 26 : null,
fontWeight: FontWeight.w800,
),
),
const SizedBox(height: 6),
Text(
@@ -309,7 +291,7 @@ class _AuthFrame extends StatelessWidget {
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Color(0xFF64748B),
color: AppColors.muted,
height: 1.35,
),
),
@@ -324,7 +306,7 @@ class _AuthFrame extends StatelessWidget {
),
),
),
],
),
);
},
),
@@ -7,6 +7,10 @@ import 'package:shared_preferences/shared_preferences.dart';
import '../../app/injection_container.dart';
import '../../core/errors/friendly_error.dart';
import '../../core/network/api_client.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_decorations.dart';
import '../../core/theme/app_text_styles.dart';
import '../../shared/widgets/animations/animations.dart';
// ---------------------------------------------------------------------------
// RegisterScreen
@@ -69,7 +73,8 @@ class _RegisterScreenState extends State<RegisterScreen> {
},
onError: (message) => _snack(context, message),
fallback: 'Registrasi gagal. Periksa data akun kamu.',
connectionHint: 'Tidak bisa ke server. Pakai URL backend publik/aktif.',
connectionHint:
'Tidak bisa ke server. Cek URL backend di Connect Server dan pastikan server aktif.',
);
if (mounted) setState(() => _loading = false);
}
@@ -128,7 +133,7 @@ class _RegisterScreenState extends State<RegisterScreen> {
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: _role == 'USER'
? const Color(0xFFEFF6FF)
? AppColors.softBlueBg
: const Color(0xFFF0FDF4),
borderRadius: BorderRadius.circular(999),
),
@@ -234,18 +239,19 @@ class _RoleCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
return BounceTap(
onTap: onTap,
child: AnimatedContainer(
duration: const Duration(milliseconds: 180),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: selected ? const Color(0xFFEFF6FF) : Colors.white,
borderRadius: BorderRadius.circular(14),
color: selected ? AppColors.softBlueBg : Colors.white,
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: selected ? const Color(0xFF1A56DB) : const Color(0xFFE2E8F0),
color: selected ? AppColors.primaryBlue : AppColors.border,
width: selected ? 2 : 1,
),
boxShadow: selected ? AppDecorations.cardShadow : null,
),
child: Row(
children: [
@@ -253,10 +259,9 @@ class _RoleCard extends StatelessWidget {
width: 48,
height: 48,
decoration: BoxDecoration(
color: selected
? const Color(0xFF1A56DB)
: const Color(0xFFF1F5F9),
borderRadius: BorderRadius.circular(12),
color:
selected ? AppColors.primaryBlue : const Color(0xFFF1F5F9),
borderRadius: BorderRadius.circular(50),
),
child: Icon(icon,
color: selected ? Colors.white : const Color(0xFF64748B)),
@@ -267,16 +272,16 @@ class _RoleCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,
style: const TextStyle(
fontWeight: FontWeight.w800, fontSize: 16)),
style: AppTextStyles.subheading.copyWith(fontSize: 16)),
Text(subtitle,
style: const TextStyle(
color: Color(0xFF64748B), fontSize: 13)),
color: AppColors.muted, fontSize: 13)),
],
),
),
if (selected)
const Icon(Icons.check_circle_rounded, color: Color(0xFF1A56DB)),
const Icon(Icons.check_circle_rounded,
color: AppColors.primaryBlue),
],
),
),
@@ -298,37 +303,25 @@ class _AuthFrame extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFEAF4FF),
backgroundColor: AppColors.softBlueBg,
body: LayoutBuilder(
builder: (context, constraints) {
final compact =
constraints.maxWidth < 480 || constraints.maxHeight < 720;
return Stack(
children: [
const Positioned.fill(
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFFD9EEFF), Color(0xFFF7FAFC)],
),
),
),
return DecoratedBox(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
AppColors.softBlueBg,
Colors.white,
AppColors.softPinkBg,
],
),
Positioned(
top: compact ? -70 : -90,
right: compact ? -70 : -60,
child: Container(
width: compact ? 180 : 260,
height: compact ? 180 : 260,
decoration: BoxDecoration(
color: const Color(0xFF2563EB).withValues(alpha: 0.12),
shape: BoxShape.circle,
),
),
),
Center(
),
child: SafeArea(
child: Center(
child: SingleChildScrollView(
keyboardDismissBehavior:
ScrollViewKeyboardDismissBehavior.onDrag,
@@ -354,19 +347,10 @@ class _AuthFrame extends StatelessWidget {
child: RepaintBoundary(
child: Container(
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.96),
color: AppColors.cardWhite,
borderRadius:
BorderRadius.circular(compact ? 22 : 30),
border: Border.all(
color: Colors.white.withValues(alpha: 0.8)),
boxShadow: [
BoxShadow(
color: const Color(0xFF1E3A8A)
.withValues(alpha: 0.14),
blurRadius: compact ? 24 : 40,
offset: const Offset(0, 18),
),
],
BorderRadius.circular(compact ? 22 : 28),
boxShadow: AppDecorations.cardShadow,
),
child: Padding(
padding: EdgeInsets.fromLTRB(
@@ -384,8 +368,15 @@ class _AuthFrame extends StatelessWidget {
width: compact ? 44 : 56,
height: compact ? 44 : 56,
decoration: BoxDecoration(
color: const Color(0xFF1D4ED8),
gradient: AppDecorations.blueGradient,
borderRadius: BorderRadius.circular(16),
boxShadow: const [
BoxShadow(
color: Color(0x334A90D9),
blurRadius: 18,
offset: Offset(0, 8),
),
],
),
child: Icon(Icons.navigation_rounded,
color: Colors.white,
@@ -399,8 +390,8 @@ class _AuthFrame extends StatelessWidget {
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w900,
color: Color(0xFF0F172A),
fontWeight: FontWeight.w800,
color: AppColors.textDark,
),
),
),
@@ -411,14 +402,10 @@ class _AuthFrame extends StatelessWidget {
title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.headlineMedium
?.copyWith(
fontSize: compact ? 26 : null,
fontWeight: FontWeight.w900,
color: const Color(0xFF0F172A),
),
style: AppTextStyles.heading.copyWith(
fontSize: compact ? 26 : null,
fontWeight: FontWeight.w800,
),
),
const SizedBox(height: 6),
Text(
@@ -426,7 +413,7 @@ class _AuthFrame extends StatelessWidget {
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Color(0xFF64748B),
color: AppColors.muted,
height: 1.35,
),
),
@@ -441,7 +428,7 @@ class _AuthFrame extends StatelessWidget {
),
),
),
],
),
);
},
),
@@ -13,6 +13,7 @@ import '../../core/services/incoming_call_polling_service.dart';
import '../../core/services/offline_queue_service.dart';
import '../../core/services/websocket_service.dart';
import '../../core/storage/secure_storage.dart';
import '../../shared/widgets/walkguide_loading_screen.dart';
// ---------------------------------------------------------------------------
// SplashScreen
@@ -39,32 +40,35 @@ class SplashScreen extends StatefulWidget {
class _SplashScreenState extends State<SplashScreen>
with SingleTickerProviderStateMixin {
late final AnimationController _animCtrl;
late final Animation<double> _fadeAnim;
late final AnimationController _screenCtrl;
late final Animation<double> _screenFade;
@override
void initState() {
super.initState();
_animCtrl = AnimationController(
_screenCtrl = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 700),
duration: const Duration(milliseconds: 260),
value: 1,
);
_screenFade = CurvedAnimation(
parent: _screenCtrl,
curve: Curves.easeOutCubic,
);
_fadeAnim = CurvedAnimation(parent: _animCtrl, curve: Curves.easeIn);
_animCtrl.forward();
_route();
}
@override
void dispose() {
_animCtrl.dispose();
_screenCtrl.dispose();
super.dispose();
}
Future<void> _route() async {
final routed = await runFriendlyAction(
() async {
// Animasi logo selalu tampil minimal 500ms agar tidak langsung flash.
await Future.delayed(const Duration(milliseconds: 500));
// Animasi logo selalu tampil minimal 900ms agar tidak langsung flash.
await Future.delayed(const Duration(milliseconds: 900));
final storage = sl<SecureStorage>();
final token = await storage.getAccessToken().timeout(
@@ -77,7 +81,7 @@ class _SplashScreenState extends State<SplashScreen>
if (!mounted) return;
if (token == null || role == null) {
context.go('/login');
await _fadeOutThenGo('/login');
return;
}
@@ -88,67 +92,28 @@ class _SplashScreenState extends State<SplashScreen>
sl<IncomingCallPollingService>().start();
}
// Auto-login: arahkan ke home sesuai role.
context.go(
await _fadeOutThenGo(
role == 'ROLE_GUARDIAN' ? '/guardian/dashboard' : '/user/walkguide',
);
},
onError: (_) {},
fallback: 'Sesi belum bisa dipulihkan.',
);
if (!routed && mounted) context.go('/login');
if (!routed && mounted) await _fadeOutThenGo('/login');
}
Future<void> _fadeOutThenGo(String route) async {
if (!mounted) return;
await _screenCtrl.reverse();
if (mounted) context.go(route);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF1A56DB),
body: Center(
child: FadeTransition(
opacity: _fadeAnim,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Logo / icon
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(28),
),
child: const Icon(
Icons.navigation_rounded,
color: Colors.white,
size: 60,
),
),
const SizedBox(height: 24),
const Text(
'WalkGuide',
style: TextStyle(
color: Colors.white,
fontSize: 34,
fontWeight: FontWeight.w800,
letterSpacing: -0.5,
),
),
const SizedBox(height: 6),
const Text(
'AI-powered navigation for everyone',
style: TextStyle(color: Colors.white70, fontSize: 13),
),
const SizedBox(height: 48),
const SizedBox(
width: 28,
height: 28,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2.5,
),
),
],
),
),
return FadeTransition(
opacity: _screenFade,
child: const WalkGuideLoadingScreen(
subtitle: 'Restoring your session',
),
);
}