app scaffold

This commit is contained in:
Guangfei.Zhao
2026-08-17 15:29:55 +08:00
commit 681688dfae
301 changed files with 18414 additions and 0 deletions
@@ -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';
+68
View File
@@ -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());
+27
View File
@@ -0,0 +1,27 @@
name: core_storage
description: 本地持久化。当前只封装 SharedPreferencesKV),数据库层暂缓。
publish_to: none
version: 0.1.0
resolution: workspace
# 偏离 06 的说明:Drift 单库暂不落地。
# 骨架阶段还没有任何需要本地缓存的业务表,先建库只会留下一个没人用、
# 却要一直维护 schema 快照和迁移测试的空壳。06 §「所有业务缓存表必须带
# storeId」「登出/切店清理策略」「migration 必须被验证」这三条规则在真的
# 加第一张表时再一次性落地——那时才有东西可验证。
# 连带收益:去掉 drift_dev 后 melos 可以留在 8.xdrift_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