feat(mobile): in-app locale switching
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,112 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
/// App 语言设置(脚手架)。
|
||||
/// App 语言设置(P3 计划 Task 10 完整版)。
|
||||
///
|
||||
/// `null` = 跟随系统;Task 10 补齐:持久化(settings 存储)、
|
||||
/// 设置页交互与全部页面的 l10n 覆盖检查。
|
||||
class LocaleSettings extends Notifier<Locale?> {
|
||||
@override
|
||||
Locale? build() => null;
|
||||
/// 三档选择(跟随系统 / 中文 / English),持久化于 shared_preferences
|
||||
/// 键 `app.locale`('system'|'zh'|'en')。main() 在 runApp 前读持久值
|
||||
/// 注入 Notifier 初始状态——首帧即正确语言(无 system→选择闪变);
|
||||
/// 持久值非法(旧版本/手改损坏)回退 system。
|
||||
///
|
||||
/// 存储读写失败均不阻断:读失败按 system;写失败保留本次会话状态
|
||||
/// (仅丢失记忆,下次启动回退旧值)。
|
||||
library;
|
||||
|
||||
void setLocale(Locale? locale) => state = locale;
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// 语言档位(枚举值顺序即设置页选项顺序)。
|
||||
enum AppLocaleChoice {
|
||||
/// 跟随系统(MaterialApp.locale=null,交由 localeResolutionCallback 解析)。
|
||||
system('system'),
|
||||
|
||||
/// 固定中文。
|
||||
zh('zh'),
|
||||
|
||||
/// 固定英文。
|
||||
en('en');
|
||||
|
||||
const AppLocaleChoice(this.persistedValue);
|
||||
|
||||
/// 持久化字符串(shared_preferences `app.locale` 的值域)。
|
||||
final String persistedValue;
|
||||
|
||||
/// 对应 [MaterialApp.locale];system 为 null。
|
||||
Locale? get locale => switch (this) {
|
||||
AppLocaleChoice.system => null,
|
||||
AppLocaleChoice.zh => const Locale('zh'),
|
||||
AppLocaleChoice.en => const Locale('en'),
|
||||
};
|
||||
|
||||
/// 持久值解析;null/未知值回退 [system](前向兼容未来新增档位)。
|
||||
static AppLocaleChoice fromPersisted(String? value) =>
|
||||
AppLocaleChoice.values.firstWhere(
|
||||
(choice) => choice.persistedValue == value,
|
||||
orElse: () => AppLocaleChoice.system,
|
||||
);
|
||||
}
|
||||
|
||||
/// 存储抽象(测试注入内存实现;生产=shared_preferences 单键)。
|
||||
abstract class LocalePrefsStore {
|
||||
Future<String?> read();
|
||||
Future<void> write(String value);
|
||||
}
|
||||
|
||||
/// [LocalePrefsStore] 的 shared_preferences 生产实现。
|
||||
class SharedPrefsLocaleStore implements LocalePrefsStore {
|
||||
const SharedPrefsLocaleStore();
|
||||
|
||||
static const String kKey = 'app.locale';
|
||||
|
||||
@override
|
||||
Future<String?> read() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString(kKey);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> write(String value) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(kKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// 语言设置 Notifier:状态为当前档位,[localeSettingsProvider] 全局唯一。
|
||||
///
|
||||
/// 生产经 main() 以持久值构造(overrideWith 注入,见 main.dart);
|
||||
/// 测试可注入初始档位与内存存储。
|
||||
class LocaleSettings extends Notifier<AppLocaleChoice> {
|
||||
LocaleSettings({
|
||||
this.initial = AppLocaleChoice.system,
|
||||
LocalePrefsStore? store,
|
||||
}) : _store = store ?? const SharedPrefsLocaleStore();
|
||||
|
||||
/// 启动注入的初始档位(main() 以持久值构造)。
|
||||
final AppLocaleChoice initial;
|
||||
final LocalePrefsStore _store;
|
||||
|
||||
@override
|
||||
AppLocaleChoice build() => initial;
|
||||
|
||||
/// 切换档位:状态立即生效(MaterialApp 即刻重建);持久化尽力而为。
|
||||
Future<void> setChoice(AppLocaleChoice choice) async {
|
||||
state = choice;
|
||||
try {
|
||||
await _store.write(choice.persistedValue);
|
||||
} on Object {
|
||||
// 写失败不回滚:语言记忆属锦上添花,不值得打断交互。
|
||||
}
|
||||
}
|
||||
|
||||
/// 启动读取持久档位(main() runApp 前调用;失败按 system)。
|
||||
static Future<AppLocaleChoice> readPersisted([
|
||||
LocalePrefsStore store = const SharedPrefsLocaleStore(),
|
||||
]) async {
|
||||
try {
|
||||
return AppLocaleChoice.fromPersisted(await store.read());
|
||||
} on Object {
|
||||
return AppLocaleChoice.system;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final localeSettingsProvider =
|
||||
NotifierProvider<LocaleSettings, Locale?>(LocaleSettings.new);
|
||||
NotifierProvider<LocaleSettings, AppLocaleChoice>(LocaleSettings.new);
|
||||
|
||||
Reference in New Issue
Block a user