fix: persist selected language across app restarts#316
fix: persist selected language across app restarts#316bhavjsh wants to merge 1 commit intofossasia:mainfrom
Conversation
Reviewer's GuidePersists the user-selected locale using SharedPreferences and ensures it is loaded into LocaleProvider during app startup so language choice survives restarts, while also adding an API to clear the stored locale. Sequence diagram for locale selection and restorationsequenceDiagram
actor User
participant SettingsScreen
participant LocaleProvider
participant SharedPreferences
participant FlutterApp
rect rgb(230,230,250)
User->>SettingsScreen: chooseLanguage(languageCode)
SettingsScreen->>LocaleProvider: setLocale(Locale(languageCode))
activate LocaleProvider
LocaleProvider->>SharedPreferences: getInstance()
activate SharedPreferences
SharedPreferences-->>LocaleProvider: prefs
LocaleProvider->>SharedPreferences: setString(app_locale, languageCode)
SharedPreferences-->>LocaleProvider: success
LocaleProvider-->>SettingsScreen: localeUpdated
LocaleProvider->>FlutterApp: notifyListeners()
deactivate SharedPreferences
deactivate LocaleProvider
end
rect rgb(224,255,255)
FlutterApp->>LocaleProvider: createInstance()
activate LocaleProvider
LocaleProvider->>LocaleProvider: loadSavedLocale()
LocaleProvider->>SharedPreferences: getInstance()
activate SharedPreferences
SharedPreferences-->>LocaleProvider: prefs
LocaleProvider->>SharedPreferences: getString(app_locale)
SharedPreferences-->>LocaleProvider: code or null
alt saved locale exists
LocaleProvider->>LocaleProvider: _locale = Locale(code)
LocaleProvider->>FlutterApp: notifyListeners()
else no saved locale
LocaleProvider-->>FlutterApp: keep default Locale(en)
end
deactivate SharedPreferences
deactivate LocaleProvider
end
Updated class diagram for LocaleProviderclassDiagram
class ChangeNotifier {
<<abstract>>
+void addListener(Function listener)
+void removeListener(Function listener)
+void notifyListeners()
}
class LocaleProvider {
-static const String _localeKey
-Locale _locale
+LocaleProvider()
+Locale get locale()
+Future<void> loadSavedLocale()
+Future<void> setLocale(Locale newLocale)
+Future<void> clearLocale()
}
class Locale {
+String languageCode
+String? countryCode
+Locale(String languageCode)
+Locale(String languageCode, String countryCode)
}
class SharedPreferences {
+static Future<SharedPreferences> getInstance()
+String? getString(String key)
+Future<bool> setString(String key, String value)
+Future<bool> remove(String key)
}
LocaleProvider ..|> ChangeNotifier
LocaleProvider --> Locale : uses
LocaleProvider --> SharedPreferences : persists locale
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- Persisting only
languageCodeinsetLocale/loadSavedLocaledrops anycountryCodeorscriptCode; if you support region-specific locales (e.g.en_USvsen_GB), consider storing and restoring the full locale identifier. - Initializing
LocaleProviderwith..loadSavedLocale()triggers an unawaited async call that may cause a brief flicker from the default'en'locale to the saved one; if this is noticeable in the UI, consider delaying app build until the locale is loaded or exposing a loading state in the provider.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Persisting only `languageCode` in `setLocale`/`loadSavedLocale` drops any `countryCode` or `scriptCode`; if you support region-specific locales (e.g. `en_US` vs `en_GB`), consider storing and restoring the full locale identifier.
- Initializing `LocaleProvider` with `..loadSavedLocale()` triggers an unawaited async call that may cause a brief flicker from the default `'en'` locale to the saved one; if this is noticeable in the UI, consider delaying app build until the locale is loaded or exposing a loading state in the provider.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
@mariobehling please review this , i have fixed the issue |
There was a problem hiding this comment.
Pull request overview
This PR addresses issue #167 by persisting the user-selected app language and restoring it on startup so localization remains consistent across app restarts.
Changes:
- Added locale persistence via
SharedPreferencesinLocaleProvider. - Restored saved locale during app startup by triggering
loadSavedLocale()when creating the provider.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| lib/provider/locale_provider.dart | Adds load/save/clear locale logic using SharedPreferences. |
| lib/main.dart | Initializes LocaleProvider by triggering locale load during provider creation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import 'package:flutter/material.dart'; | ||
| import 'package:shared_preferences/shared_preferences.dart'; |
There was a problem hiding this comment.
shared_preferences is imported here, but the package isn’t declared in pubspec.yaml dependencies (so this change will fail to compile). Add shared_preferences to pubspec.yaml and run flutter pub get / update lockfile as needed.
| _locale = Locale(code); | ||
| notifyListeners(); | ||
| } | ||
|
|
||
| /// Sets the application's locale and persists it. | ||
| /// | ||
| /// When the locale is changed, it notifies all listening widgets to rebuild. | ||
| void setLocale(Locale newLocale) { | ||
| Future<void> setLocale(Locale newLocale) async { | ||
| _locale = newLocale; | ||
|
|
||
| final prefs = await SharedPreferences.getInstance(); | ||
| await prefs.setString(_localeKey, newLocale.languageCode); | ||
|
|
There was a problem hiding this comment.
Locale persistence currently stores only newLocale.languageCode and restores via Locale(code). This loses countryCode/scriptCode and can’t round-trip supported locales like pt_BR, nb_NO, or zh-Hans/Hant (see AppLocalizations.supportedLocales). Persist the full locale (e.g., toLanguageTag() or language+script+country) and parse it back with Locale.fromSubtags(...).
| Future<void> loadSavedLocale() async { | ||
| final prefs = await SharedPreferences.getInstance(); | ||
| final code = prefs.getString(_localeKey); | ||
|
|
||
| if (code == null || code.isEmpty) return; | ||
|
|
||
| _locale = Locale(code); | ||
| notifyListeners(); | ||
| } |
There was a problem hiding this comment.
SharedPreferences.getInstance() / read/write calls can throw (platform/channel issues). Since these methods are called without awaiting at the call site (e.g., startup cascade), an exception could surface as an unhandled async error. Consider wrapping SharedPreferences operations in try/catch and failing gracefully (keep the in-memory locale, optionally log).
| /// Optional: Reset to system/default behavior. | ||
| /// Use this if you provide a "System Default" option in UI. | ||
| Future<void> clearLocale() async { | ||
| _locale = const Locale('en'); // or set to null if your app supports null locale |
There was a problem hiding this comment.
The doc comment says “Reset to system/default behavior”, but the implementation forces English (Locale('en')). Either update the comment to match the behavior, or change the provider/API to support a true system-default mode (e.g., nullable locale passed to MaterialApp.locale).
| /// Optional: Reset to system/default behavior. | |
| /// Use this if you provide a "System Default" option in UI. | |
| Future<void> clearLocale() async { | |
| _locale = const Locale('en'); // or set to null if your app supports null locale | |
| /// Reset to the app's default locale (English) and clear any saved override. | |
| /// Use this if you provide a "Use app default" option in the UI. | |
| Future<void> clearLocale() async { | |
| _locale = const Locale('en'); |
| void main() { | ||
| setupLocator(); | ||
| runApp( | ||
| MultiProvider( | ||
| providers: [ | ||
| ChangeNotifierProvider(create: (context) => ImageLoader()), | ||
| ChangeNotifierProvider(create: (_) => ImageLibraryProvider()), | ||
| ChangeNotifierProvider(create: (_) => LocaleProvider()), | ||
| // Load saved locale on startup | ||
| ChangeNotifierProvider(create: (_) => LocaleProvider()..loadSavedLocale()), | ||
| ], |
There was a problem hiding this comment.
loadSavedLocale() is async but is kicked off without being awaited; this will boot the app in the default locale and then rebuild once the preference loads (visible language “flash” on cold start). If you want the correct locale from the first frame, make main() async, call WidgetsFlutterBinding.ensureInitialized(), and await locale loading before runApp (or gate MaterialApp behind a loading state).
Fixes #167
Summary
This PR fixes an issue where the selected language was not retained after restarting the app.
The chosen locale is now persisted locally and restored on app startup, ensuring a consistent
localized experience for users.
What was changed
How this was tested
Screenshots
(Added below)



Summary by Sourcery
Persist the user-selected locale across app restarts and initialize the app with any previously saved language choice.
New Features:
Enhancements: