import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'l10n/i18n.dart'; import 'l10n/locale_provider.dart'; import 'screens/report_flow/capture_screen.dart'; import 'screens/map/map_screen.dart'; import 'screens/my_reports/my_reports_screen.dart'; import 'screens/settings/settings_screen.dart'; import 'theme/themes.dart'; import 'package:shared_preferences/shared_preferences.dart'; class FixMateApp extends StatelessWidget { const FixMateApp({super.key}); @override Widget build(BuildContext context) { return Consumer( builder: (context, localeProvider, child) { return MaterialApp( title: I18n.t('app.name'), theme: AppThemes.light(), darkTheme: AppThemes.dark(), themeMode: ThemeMode.system, locale: localeProvider.locale, localizationsDelegates: const [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: const [ Locale('en'), Locale('ms'), ], localeResolutionCallback: (locale, supported) { debugPrint('[i18n] localeResolution: device=$locale, supported=$supported'); if (locale == null) return supported.first; for (final s in supported) { if (s.languageCode == locale.languageCode) { return s; } } return supported.first; }, builder: (context, child) { debugPrint('[i18n] Building MaterialApp; locale=${localeProvider.locale}'); return child!; }, home: const StartRouter(), ); }, ); } } class MainScreen extends StatefulWidget { const MainScreen({super.key}); @override State createState() => _MainScreenState(); } class _MainScreenState extends State { int _selectedIndex = 0; final List _screens = [ const CaptureScreen(), const MapScreen(), const MyReportsScreen(), const SettingsScreen(), ]; final List _navLabels = [ 'nav.report', 'nav.map', 'nav.myReports', 'nav.settings', ]; @override Widget build(BuildContext context) { return Scaffold( body: _screens[_selectedIndex], bottomNavigationBar: BottomNavigationBar( currentIndex: _selectedIndex, onTap: (index) { setState(() { _selectedIndex = index; }); }, type: BottomNavigationBarType.fixed, items: [ BottomNavigationBarItem( icon: const Icon(Icons.camera_alt), label: I18n.t(_navLabels[0]), ), BottomNavigationBarItem( icon: const Icon(Icons.map), label: I18n.t(_navLabels[1]), ), BottomNavigationBarItem( icon: const Icon(Icons.list), label: I18n.t(_navLabels[2]), ), BottomNavigationBarItem( icon: const Icon(Icons.settings), label: I18n.t(_navLabels[3]), ), ], ), ); } } class PlaceholderScreen extends StatelessWidget { final String title; const PlaceholderScreen({super.key, required this.title}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(title), ), body: Center( child: Text('$title - Coming Soon!'), ), ); } } /// Router that decides whether to show onboarding or main app class StartRouter extends StatefulWidget { const StartRouter({super.key}); @override State createState() => _StartRouterState(); } class _StartRouterState extends State { bool _loading = true; bool _onboarded = false; static const String _kOnboardedKey = 'onboarded_v1'; @override void initState() { super.initState(); _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); final flag = prefs.getBool(_kOnboardedKey) ?? false; if (mounted) { setState(() { _onboarded = flag; _loading = false; }); } } Future _setOnboarded() async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_kOnboardedKey, true); if (mounted) { setState(() { _onboarded = true; }); } } @override Widget build(BuildContext context) { debugPrint('[i18n] StartRouter: hasMaterial=${Localizations.of(context, MaterialLocalizations) != null} locale=${Localizations.localeOf(context)}'); if (_loading) { return const Scaffold(body: Center(child: CircularProgressIndicator())); } if (_onboarded) return const MainScreen(); return WelcomeScreen( onContinue: () async { final completed = await Navigator.push( context, MaterialPageRoute(builder: (_) => const OnboardingFlow()), ); if (completed == true) { await _setOnboarded(); } }, onSignIn: () { Navigator.push( context, MaterialPageRoute(builder: (_) => const SignInScreen()), ); }, onSkip: () async { await _setOnboarded(); }, ); } } /// Branded welcome screen (in-app splash handoff) class WelcomeScreen extends StatelessWidget { final VoidCallback onContinue; final VoidCallback onSignIn; final VoidCallback onSkip; const WelcomeScreen({ super.key, required this.onContinue, required this.onSignIn, required this.onSkip, }); @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; return Scaffold( body: SafeArea( child: Padding( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: 12), Row( children: [ CircleAvatar( radius: 20, backgroundColor: cs.primary, child: const Icon(Icons.build, color: Colors.white), ), const SizedBox(width: 12), Text(I18n.t('app.name'), style: Theme.of(context).textTheme.titleLarge), ], ), const Spacer(), Text( I18n.t('welcome.title'), style: Theme.of(context).textTheme.displaySmall?.copyWith(fontWeight: FontWeight.w700), ), const SizedBox(height: 12), Text( I18n.t('welcome.subtitle'), style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: cs.onSurface.withOpacity(0.75)), ), const Spacer(), SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: onContinue, icon: const Icon(Icons.arrow_forward), label: Text(I18n.t('cta.continueGuest')), ), ), const SizedBox(height: 12), SizedBox( width: double.infinity, child: OutlinedButton.icon( onPressed: onSignIn, icon: const Icon(Icons.login), label: Text(I18n.t('cta.signIn')), ), ), const SizedBox(height: 8), TextButton( onPressed: onSkip, child: Text(I18n.t('cta.skip')), ), ], ), ), ), ); } } /// Three-step onboarding flow with concise benefits class OnboardingFlow extends StatefulWidget { const OnboardingFlow({super.key}); @override State createState() => _OnboardingFlowState(); } class _OnboardingFlowState extends State { final PageController _pc = PageController(); int _index = 0; void _next() { if (_index < 2) { _pc.nextPage(duration: const Duration(milliseconds: 220), curve: Curves.easeOutCubic); } else { Navigator.pop(context, true); } } @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; Widget page(String titleKey, String bodyKey, IconData icon) { return Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircleAvatar(radius: 48, backgroundColor: cs.primary, child: Icon(icon, color: Colors.white, size: 40)), const SizedBox(height: 24), Text(I18n.t(titleKey), style: Theme.of(context).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.w700), textAlign: TextAlign.center), const SizedBox(height: 12), Text(I18n.t(bodyKey), style: Theme.of(context).textTheme.bodyLarge, textAlign: TextAlign.center), ], ), ); } return Scaffold( appBar: AppBar( title: Text(I18n.t('onboarding.header')), ), body: Column( children: [ Expanded( child: PageView( controller: _pc, onPageChanged: (i) => setState(() => _index = i), children: [ page('onboarding.title1', 'onboarding.body1', Icons.flash_on), page('onboarding.title2', 'onboarding.body2', Icons.map), page('onboarding.title3', 'onboarding.body3', Icons.check_circle), ], ), ), Padding( padding: const EdgeInsets.fromLTRB(24, 0, 24, 24), child: Row( children: [ TextButton(onPressed: () => Navigator.pop(context, true), child: Text(I18n.t('cta.skip'))), const Spacer(), ElevatedButton( onPressed: _next, child: Text(_index < 2 ? I18n.t('cta.next') : I18n.t('cta.getStarted')), ), ], ), ), ], ), ); } } /// Sign-in placeholder with SSO buttons; supports continue as guest class SignInScreen extends StatelessWidget { const SignInScreen({super.key}); @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; return Scaffold( appBar: AppBar(title: Text(I18n.t('auth.title'))), body: Padding( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: 24), ElevatedButton.icon( onPressed: () => ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(I18n.t('auth.comingSoon')))), icon: const Icon(Icons.apple), label: Text(I18n.t('auth.signInWithApple')), style: ElevatedButton.styleFrom(backgroundColor: cs.onSurface, foregroundColor: cs.surface), ), const SizedBox(height: 12), OutlinedButton.icon( onPressed: () => ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(I18n.t('auth.comingSoon')))), icon: const Icon(Icons.g_mobiledata), label: Text(I18n.t('auth.signInWithGoogle')), ), const Spacer(), TextButton( onPressed: () => Navigator.pop(context), child: Text(I18n.t('cta.continueGuest')), ), ], ), ), ); } }