Files
SkyJourney b8b63282ee fix: 清除 platform-flutter 残留代码,重构文档与开发者体验
P0 代码修复:删除 imSdkAppId、清除内网 IP 与原项目域名、修正 USE_MOCK 默认值
P1 CLAUDE.md 重组:零容忍规则前置,新增 dart-defines 字段对照表
P2 文档优化:先跑再配的 setup 流程,新增 architecture.md 架构图
2026-05-14 13:21:06 +08:00

11 KiB
Raw Permalink Blame History

CLAUDE.md — Flutter 企业级脚手架(sunny_mochi

本文件为 Claude Code 提供项目上下文。每次新会话首先读取本文件 需要时再按需加载 docs/ 下的补充文档。


30 秒上手(开箱即用验证)

flutter pub get && make gen && make gen-i18n
# dev.json 默认 USE_MOCK=true,无需后端即可运行:
make run-dev
# 期望结果:App 启动 → 显示登录页 → 右上角可进 Dev PanelTalker 日志)

这是脚手架,不是完整 App。以下内容"需要业务项目填写":

  • lib/core/config/api_paths.dart — 所有路径均为空字符串
  • lib/core/config/api_config.dart — 三套 baseUrl 均为 localhost 占位符
  • dart-defines/*.json — RSA 公钥 / Sentry DSN 均为占位符

项目身份信息

业务项目接手后,将下表 TODO 字段替换为真实值,AI 才能给出精准建议。

字段 当前值(脚手架占位) 说明
项目名称 sunny_mochi TODO: 替换为实际项目名
Application ID com.example.sunny_mochi TODO: 如 com.company.appname
API Base URL (dev) http://localhost:8080 TODO: 改 api_config.dart + dev.json
API Base URL (staging) https://staging.your-domain.com TODO: 改 api_config.dart
API Base URL (prod) https://api.your-domain.com TODO: 改 api_config.dart
后端鉴权框架 sa-token(动态 tokenName header TODO: 若用 Bearer JWT 改 auth_interceptor
Sentry DSN 空(不上报) TODO: prod.json
RSA 公钥 空(加密跳过) TODO: 三个 dart-defines JSON

零容忍规则(必须遵守,不可商量)

规则 正确 禁止
API 路径 ApiPaths.xxx 常量 字符串字面量散落代码中
错误处理 ExceptionMapper().fromUnknown(e, st) catch (e) 直接 toString() 显示
Token 存储 SecureStorageKeychain / EncryptedPrefs 普通 SharedPreferences
数据库加密 SQLCipher(默认,禁止替换) 无加密 sqflite
日志 appTalker.info/warning/error() print() / debugPrint()
路由跳转 XxxRoute().go(context) Navigator.push()
状态管理 @riverpod / @Riverpod(keepAlive: true) setState 跨组件 / Provider 包
Riverpod provider 命名 生成名(AuthNotifierauthProvider 手写 authNotifierProvider
生成文件 只读,运行 make gen 刷新 手动修改 .g.dart / .freezed.dart
Domain 层依赖 纯 Dart,无 Flutter/Drift/Dio domain 层 import 'package:dio'

禁止修改的文件(架构决策,除非全面评估影响)

lib/core/network/dio_client.dart          # 7 拦截器顺序固定,顺序即语义
lib/core/storage/db_key_provider.dart     # AES-256 密钥派生策略,改动会破坏已有 DB
lib/core/error/failures.dart             # 全局错误分类,改 sealed 影响所有 switch
lib/core/network/response_code.dart      # 业务码分类,影响 TokenRefresh 互斥逻辑

dart-defines 字段完整对照表

JSON 键 Env.dart 读取字段 类型 说明
ENV Env.name String dev / test / release
API_BASE_URL Env.apiBaseUrlOverride String 留空则用 api_config.dart 中的固定 URL
USE_MOCK Env.useMock bool true = 走 MockAdapter,无需后端
INTERNAL_BUILD Env.isInternalBuild bool true = 显示 Dev Panel 入口
SENTRY_DSN Env.sentryDsn String 留空时 Sentry 自动跳过
PINNED_FINGERPRINTS Env.pinnedFingerprints List 逗号分隔的 SHA-256 指纹,留空跳过 SSL 绑定
RSA_PUBLIC_KEY Env.rsaPublicKey String DER-SPKI Base64,留空时密码不加密传输

技术栈(脚手架锁定版本)

分类 技术 版本
状态管理 flutter_riverpod + riverpod_annotation ^3.3.0 / ^4.0.0
路由 go_router + go_router_builder ^17.0.0 / ^4.3.0
网络 Dio ^5.9.0
本地数据库 Drift + SQLCipher 2.31.0(锁定)
安全存储 flutter_secure_storage ^10.0.0
序列化 Freezed + json_serializable ^3.x / 6.13.0(锁定)
日志 Talker + talker_flutter ^5.x
错误追踪 sentry_flutter ^9.0.0
i18n Slangbase_locale: zh-CN ^4.0.0
UI 自适应 flutter_screenutil(基准 375×812 ^5.9.0

drift: 2.31.0json_serializable: 6.13.0 锁定原因: 更新版本需要 analyzer ^10.x,与 riverpod_generator 的 analyzer ^9.x 冲突。勿升级。


目录结构与关键文件

lib/
├── main.dart              # 启动序列(固定顺序:preInit→consumePending→SentryInit→installHooks
├── app.dart               # AppRoot → ScreenUtilInit(375×812) → App → MaterialApp.router
├── i18n/                  # Slang 翻译(zh-CN 基准 + en 备用)
├── core/
│   ├── config/
│   │   ├── env.dart              # ← 读取 dart-defines,只读
│   │   ├── api_config.dart       # ★ 填入三套环境 baseUrl
│   │   └── api_paths.dart        # ★ 填入所有 API 路径常量
│   ├── router/
│   │   ├── app_router.dart       # GoRouter providerref.read + refreshListenable
│   │   └── routes.dart           # ★ 添加业务 @TypedGoRoute(然后 make gen
│   ├── network/
│   │   ├── dio_client.dart       # 7 拦截器(禁止改顺序)
│   │   ├── api_response.dart     # envelope 解析(parseEnvelope / unwrapVoid
│   │   ├── response_code.dart    # 业务状态码(成功=00000,token 类码分类)
│   │   ├── interceptors/         # 7 个拦截器(禁止改顺序)
│   │   └── mock/                 # USE_MOCK=true 时生效,读 assets/fixtures/
│   ├── storage/
│   │   ├── app_database.dart     # schemaVersion=1@DriftDatabase([Users, ErrorLogs])
│   │   ├── secure_storage.dart   # token / userId / tokenName 的唯一真源
│   │   ├── db_key_provider.dart  # AES-256 密钥派生(禁止改策略)
│   │   ├── tables/               # ★ 业务项目在此添加 Drift 表
│   │   └── daos/                 # DAO(只放纯 DB 操作)
│   ├── error/
│   │   ├── failures.dart         # sealed Failure(禁止改结构)
│   │   └── exception_mapper.dart # 异常 → Failure 映射 + Riverpod provider
│   └── widgets/                  # AppToast / AppButton / EmptyView / AvatarWidget 等
└── features/
    ├── auth/                     # ★ 修改 LoginPage UI 品牌,业务路由骨架已就绪
    ├── dev_panel/                # Talker 面板(INTERNAL_BUILD=true 时可路由进入)
    └── error_report/             # 本地错误日志 + 批量上报(完整实现)

常用开发范式

新增 Feature(标准目录结构)

lib/features/{feature}/
├── domain/
│   ├── entities/{name}_entity.dart        # @freezed,纯 Dart
│   └── repositories/{name}_repository.dart # abstract interface
├── data/
│   ├── models/{name}_model.dart           # @freezed + fromJson + toEntity()
│   ├── datasources/{name}_remote_datasource.dart  # @riverpod,只用 Dio
│   └── repositories/{name}_repository_impl.dart   # @riverpod,实现接口
└── presentation/
    ├── notifiers/{name}_notifier.dart     # @riverpod class XxxNotifier
    └── pages/{name}_page.dart             # ConsumerWidget

新增路由(routes.dart → make gen

@TypedGoRoute<MyRoute>(path: '/my-path')
class MyRoute extends GoRouteData with $MyRoute {
  const MyRoute();
  @override
  Widget build(BuildContext context, GoRouterState state) => const MyPage();
}

标准错误处理

// Notifier 层
} on Object catch (e, st) {
  final failure = ExceptionMapper().fromUnknown(e, st);
  state = MyState.error(failure.message);
}

// UI 层
ref.listen(myProvider, (_, next) {
  if (next is AsyncError) context.showError(ref, next.error!, next.stackTrace!);
});

新增 DB 表

  1. lib/core/storage/tables/{name}_table.dart — 定义 @DataClassName + 字段
  2. app_database.dart@DriftDatabase(tables: [..., NewTable]) + 递增 schemaVersion + 添加 onUpgrade 迁移
  3. lib/core/storage/daos/{name}_dao.dart@DriftAccessor(tables: [NewTable])
  4. 运行 make gen

开发命令

make gen          # build_runner → .g.dart / .freezed.dart(改 Dart 文件后必跑)
make gen-i18n     # slang → lib/i18n/strings.g.dart(改翻译 JSON 后必跑)
make run-dev      # dev 包(USE_MOCK=true,无需后端)
make run-staging  # staging 包(连 test 服务器)
make build-staging# staging APK
make build-prod   # prod AAB(需先配置 prod.json + key.properties
flutter analyze   # 目标 0 errors(当前正常 info 数约 103 条)
flutter test      # 全量测试

新项目接手 Checklist

完成并删除本节(或移至项目 wiki

  • 更新本文件"项目身份信息"表格(让 AI 上下文准确)
  • 修改 lib/core/config/api_config.dart 三套 baseUrl
  • 填入 lib/core/config/api_paths.dart 所有路径
  • 配置三个 dart-defines/*.jsonRSA 公钥 / Sentry DSN
  • 修改 Android 包名:android/app/build.gradle.kts applicationId
  • 修改 iOS Bundle IDXcode → Runner Target → General
  • 修改 pubspec.yaml name 并批量替换 import 路径(见 setup-project.md Step 5
  • Android 签名:cp android/key.properties.template android/key.properties 并填写
  • iOS 签名:Xcode → Signing & Capabilities(见 setup-ios.md
  • 替换登录页 UI 品牌(lib/features/auth/presentation/pages/login_page.dart
  • 替换 App IconAndroid mipmap / iOS AppIcon.appiconset
  • 实现 Tab 页面(lib/core/router/routes.dart HomeRoute / MineRoute 的 build
  • 更新 README.md 项目描述和快速开始地址

参考文档

文档 内容
README.md 项目概述、技术栈、基础设施一览
docs/setup-project.md 完整配置指南(dart-defines / 包名 / 首次运行)
docs/setup-android.md Android Flavor / 签名 / 构建 / CI
docs/setup-ios.md iOS Scheme / CocoaPods / 证书 / Archive
docs/architecture.md 数据流 / 认证流 / 错误链 ASCII 架构图