chore: 初始化脚手架 — sunny_mochi 企业级 Flutter 模板

Core 基础设施:Flavor 三包体系、Drift+SQLCipher 加密数据库
7 拦截器 Dio 网络栈、CrashReporter 崩溃日志、Sealed Failure 错误体系
5 色板主题系统、Auth 认证骨架(Clean Architecture)、Dev Panel、Mock Adapter

通过验证:flutter analyze(0 errors)、flutter test(全绿)、staging APK 74.8MB
This commit is contained in:
SkyJourney
2026-05-14 12:51:05 +08:00
commit 61017f1c39
204 changed files with 15386 additions and 0 deletions
@@ -0,0 +1,60 @@
import 'dart:async';
import 'package:flutter/foundation.dart' show ValueNotifier;
import 'package:sunny_mochi/core/observability/talker_setup.dart';
import 'package:sunny_mochi/core/storage/secure_storage.dart'
show secureStorageProvider;
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'auth_status_provider.g.dart';
/// 同步可读的登录态门面 — 给 GoRouter `redirect` 用。
///
/// 三态语义:
/// - `null` → 启动期未知(异步 storage 还没读完,redirect 应不动)
/// - `true` → 已登录
/// - `false` → 未登录(redirect 把非 /login 的访问跳 /login
class AuthStatusController {
AuthStatusController() : _notifier = ValueNotifier<bool?>(null);
final ValueNotifier<bool?> _notifier;
ValueNotifier<bool?> get listenable => _notifier;
bool? get value => _notifier.value;
void markLoggedIn() {
appTalker.info('[AuthStatus] markLoggedIn');
_notifier.value = true;
}
void markLoggedOut() {
appTalker.info('[AuthStatus] markLoggedOut');
_notifier.value = false;
}
void dispose() => _notifier.dispose();
}
@Riverpod(keepAlive: true)
AuthStatusController authStatus(Ref ref) {
final controller = AuthStatusController();
unawaited(_bootstrap(ref, controller));
ref.onDispose(controller.dispose);
return controller;
}
Future<void> _bootstrap(Ref ref, AuthStatusController controller) async {
try {
final token = await ref.read(secureStorageProvider).getToken();
final loggedIn = token != null && token.isNotEmpty;
if (loggedIn) {
controller.markLoggedIn();
} else {
controller.markLoggedOut();
}
} on Object catch (e, st) {
appTalker.warning('[AuthStatus] bootstrap 异常 → 视为未登录', e, st);
controller.markLoggedOut();
}
}