Files
citypulse/lib/l10n/locale_provider.dart
Zahar 6d6e9300f6 feat(ui): add animated screen transitions and guest onboarding
Improve app UX by adding animated transitions for screen switches
and introducing a persisted guest onboarding flow.

- Add AnimatedSwitcher-based fade+slide transitions for screens and
  tab switches for smoother navigation.
- Persist a 'user_mode' SharedPreferences key and provide guest
  helpers to mark users as guest and influence routing.
- Update SignInScreen continue-as-guest action to set onboarding and
  guest flags and navigate to MainScreen.
- Normalize locales in LocaleProvider to language-only values and
  persist normalized language codes to ensure compatibility with
  Flutter localization delegates.
- Replace Row with Wrap in ReportCard to improve spacing and
  responsiveness.

No breaking changes
2025-09-26 13:57:11 +08:00

110 lines
3.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'i18n.dart';
/// Provider for managing app locale and language switching
class LocaleProvider extends ChangeNotifier {
static const String _languageKey = 'lang';
static const String _defaultLanguage = 'en';
Locale _locale = const Locale('en');
late SharedPreferences _prefs;
/// Get the current locale
Locale get locale => _locale;
/// Get the current language code
String get languageCode => _locale.languageCode;
/// Initialize the locale provider
Future<void> init() async {
_prefs = await SharedPreferences.getInstance();
final saved = _prefs.getString(_languageKey) ?? _defaultLanguage;
final parts = saved.split(RegExp(r'[-_]'));
// Normalize and keep language-only locales so they match Global*Localizations delegates
final language = parts.isNotEmpty && parts[0].isNotEmpty ? parts[0] : _defaultLanguage;
_locale = Locale(language);
await I18n.init(_locale);
notifyListeners();
}
/// Set the locale and persist the change
Future<void> setLocale(Locale locale) async {
// Normalize to language-only locale to ensure compatibility with delegates
final normalized = Locale(locale.languageCode);
if (_locale == normalized) return;
_locale = normalized;
final saveValue = _locale.languageCode;
await _prefs.setString(_languageKey, saveValue);
await I18n.init(_locale);
notifyListeners();
}
/// Toggle between English and Bahasa Malaysia
Future<void> toggleLanguage() async {
final newLanguage = _locale.languageCode == 'en' ? 'ms' : 'en';
await setLocale(Locale(newLanguage));
}
/// Set language to English
Future<void> setEnglish() async {
await setLocale(const Locale('en'));
}
/// Set language to Bahasa Malaysia
Future<void> setMalay() async {
await setLocale(const Locale('ms'));
}
/// Check if current language is English
bool get isEnglish => _locale.languageCode == 'en';
/// Check if current language is Bahasa Malaysia
bool get isMalay => _locale.languageCode == 'ms';
/// Get the display name for the current language
String get currentLanguageDisplayName {
switch (_locale.languageCode) {
case 'en':
return 'English';
case 'ms':
return 'Bahasa Malaysia';
default:
return 'English';
}
}
/// Get the native display name for the current language
String get currentLanguageNativeName {
switch (_locale.languageCode) {
case 'en':
return 'English';
case 'ms':
return 'Bahasa Malaysia';
default:
return 'English';
}
}
/// Get available locales
List<Locale> get availableLocales => const [
Locale('en'),
Locale('ms'),
];
/// Get available language codes
List<String> get availableLanguageCodes => ['en', 'ms'];
/// Clear saved language preference
Future<void> clearSavedLanguage() async {
await _prefs.remove(_languageKey);
_locale = const Locale(_defaultLanguage);
notifyListeners();
}
/// Reset to default language
Future<void> resetToDefault() async {
await setLocale(const Locale(_defaultLanguage));
}
}