8f3966d7b5
Co-Authored-By: Claude Code <noreply@anthropic.com>
113 lines
3.7 KiB
Dart
113 lines
3.7 KiB
Dart
/// App 语言设置(P3 计划 Task 10 完整版)。
|
||
///
|
||
/// 三档选择(跟随系统 / 中文 / English),持久化于 shared_preferences
|
||
/// 键 `app.locale`('system'|'zh'|'en')。main() 在 runApp 前读持久值
|
||
/// 注入 Notifier 初始状态——首帧即正确语言(无 system→选择闪变);
|
||
/// 持久值非法(旧版本/手改损坏)回退 system。
|
||
///
|
||
/// 存储读写失败均不阻断:读失败按 system;写失败保留本次会话状态
|
||
/// (仅丢失记忆,下次启动回退旧值)。
|
||
library;
|
||
|
||
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, AppLocaleChoice>(LocaleSettings.new);
|