app scaffold
This commit is contained in:
@@ -0,0 +1 @@
|
||||
include: ../../analysis_options.yaml
|
||||
@@ -0,0 +1,11 @@
|
||||
/// 本地持久化。来源:conti-docs/06-local-storage.md。
|
||||
///
|
||||
/// 当前只有 KV 一档。**Drift 数据库暂缓**——骨架阶段没有任何业务表,
|
||||
/// 见 pubspec.yaml 顶部的说明。
|
||||
///
|
||||
/// 边界约定:**token / refresh token 不在这里**——secure storage 归
|
||||
/// `core_auth` 独占(06 §决策表下的说明)。这里存的都是非敏感数据。
|
||||
library;
|
||||
|
||||
export 'src/prefs.dart';
|
||||
export 'src/providers.dart';
|
||||
@@ -0,0 +1,68 @@
|
||||
/// 简单非敏感 KV 的统一封装。来源:06 §决策表第三档。
|
||||
library;
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// 键名常量。
|
||||
///
|
||||
/// **命名约定:用户级的键一律以 [Prefs.userScopedPrefix] 开头。**
|
||||
/// 登出时只清这些,App 级偏好(主题、是否看过引导页)保留——换人登录不该
|
||||
/// 把设备上的通用设置也重置掉。
|
||||
abstract final class PrefKeys {
|
||||
/// 是否看过引导页。App 级,登出保留。
|
||||
static const String onboardingSeen = 'onboarding_seen';
|
||||
|
||||
/// 上次选中的门店 ID。用户级,登出必须清。
|
||||
static const String lastStoreId = '${Prefs.userScopedPrefix}last_store_id';
|
||||
|
||||
/// 是否已同意隐私政策。App 级——同意是设备行为,且埋点/崩溃 SDK 的
|
||||
/// 延迟初始化依赖它(见 13)。
|
||||
static const String privacyPolicyAccepted = 'privacy_policy_accepted';
|
||||
}
|
||||
|
||||
/// `shared_preferences` 的唯一入口。
|
||||
///
|
||||
/// 这里存的都是**非敏感**数据。token / refresh token 走 `core_auth` 的
|
||||
/// secure storage,不允许出现在这里(06 §使用规则)。
|
||||
class Prefs {
|
||||
/// [store] 默认用 `SharedPreferencesAsync`,测试可注入替身。
|
||||
Prefs([SharedPreferencesAsync? store]) : _store = store ?? SharedPreferencesAsync();
|
||||
|
||||
/// 用户级键的前缀。
|
||||
static const String userScopedPrefix = 'u_';
|
||||
|
||||
final SharedPreferencesAsync _store;
|
||||
|
||||
/// 读一个字符串。
|
||||
Future<String?> getString(String key) => _store.getString(key);
|
||||
|
||||
/// 写一个字符串。
|
||||
Future<void> setString(String key, String value) => _store.setString(key, value);
|
||||
|
||||
/// 读一个布尔值,缺省 [defaultValue]。
|
||||
Future<bool> getBool(String key, {bool defaultValue = false}) async =>
|
||||
await _store.getBool(key) ?? defaultValue;
|
||||
|
||||
/// 写一个布尔值。
|
||||
Future<void> setBool(String key, {required bool value}) => _store.setBool(key, value);
|
||||
|
||||
/// 读一个整数。
|
||||
Future<int?> getInt(String key) => _store.getInt(key);
|
||||
|
||||
/// 写一个整数。
|
||||
Future<void> setInt(String key, int value) => _store.setInt(key, value);
|
||||
|
||||
/// 删除一个键。
|
||||
Future<void> remove(String key) => _store.remove(key);
|
||||
|
||||
/// 登出时调用:只清 [userScopedPrefix] 前缀的键,保留 App 级偏好。
|
||||
///
|
||||
/// 用前缀而不是维护一张手写的键名清单——新增用户级配置项时只要遵守命名
|
||||
/// 约定就自动被清掉,手写清单一定会有人忘了加。
|
||||
Future<void> clearUserScoped() async {
|
||||
final Set<String> keys = await _store.getKeys();
|
||||
for (final String key in keys.where((String k) => k.startsWith(userScopedPrefix))) {
|
||||
await _store.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/// core_storage 的 Riverpod 接线。
|
||||
library;
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'prefs.dart';
|
||||
|
||||
/// KV 存储的唯一入口。
|
||||
final Provider<Prefs> prefsProvider = Provider<Prefs>((Ref ref) => Prefs());
|
||||
@@ -0,0 +1,27 @@
|
||||
name: core_storage
|
||||
description: 本地持久化。当前只封装 SharedPreferences(KV),数据库层暂缓。
|
||||
publish_to: none
|
||||
version: 0.1.0
|
||||
resolution: workspace
|
||||
|
||||
# 偏离 06 的说明:Drift 单库暂不落地。
|
||||
# 骨架阶段还没有任何需要本地缓存的业务表,先建库只会留下一个没人用、
|
||||
# 却要一直维护 schema 快照和迁移测试的空壳。06 §「所有业务缓存表必须带
|
||||
# storeId」「登出/切店清理策略」「migration 必须被验证」这三条规则在真的
|
||||
# 加第一张表时再一次性落地——那时才有东西可验证。
|
||||
# 连带收益:去掉 drift_dev 后 melos 可以留在 8.x(drift_dev 2.34.0 依赖
|
||||
# cli_util ^0.4.0,与 melos 8 的 ^0.5.0 互斥)。详见 SCAFFOLD-NOTES.md §A。
|
||||
environment:
|
||||
sdk: ^3.12.0
|
||||
|
||||
dependencies:
|
||||
core_foundation: ^0.1.0
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_riverpod: ^3.3.2
|
||||
shared_preferences: ^2.5.5
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^6.0.0
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
Reference in New Issue
Block a user