Template
fix: 清除 platform-flutter 残留代码,重构文档与开发者体验
P0 代码修复:删除 imSdkAppId、清除内网 IP 与原项目域名、修正 USE_MOCK 默认值 P1 CLAUDE.md 重组:零容忍规则前置,新增 dart-defines 字段对照表 P2 文档优化:先跑再配的 setup 流程,新增 architecture.md 架构图
This commit is contained in:
@@ -1,25 +1,82 @@
|
||||
# CLAUDE.md — Flutter 企业级脚手架(sunny_mochi)
|
||||
|
||||
> 本文件为 Claude Code 提供项目上下文。**所有 AI 辅助开发会话必须首先读取本文件**,
|
||||
> 再按需加载 `docs/` 下的补充文档。
|
||||
> 本文件为 Claude Code 提供项目上下文。**每次新会话首先读取本文件**,
|
||||
> 需要时再按需加载 `docs/` 下的补充文档。
|
||||
|
||||
---
|
||||
|
||||
## ⚡ 30 秒上手(开箱即用验证)
|
||||
|
||||
```bash
|
||||
flutter pub get && make gen && make gen-i18n
|
||||
# dev.json 默认 USE_MOCK=true,无需后端即可运行:
|
||||
make run-dev
|
||||
# 期望结果:App 启动 → 显示登录页 → 右上角可进 Dev Panel(Talker 日志)
|
||||
```
|
||||
|
||||
**这是脚手架,不是完整 App**。以下内容"需要业务项目填写":
|
||||
- `lib/core/config/api_paths.dart` — 所有路径均为空字符串
|
||||
- `lib/core/config/api_config.dart` — 三套 baseUrl 均为 localhost 占位符
|
||||
- `dart-defines/*.json` — RSA 公钥 / Sentry DSN 均为占位符
|
||||
|
||||
---
|
||||
|
||||
## 项目身份信息
|
||||
|
||||
> **以下带 `TODO` 的字段由业务项目接手时填入,脚手架默认保留占位符。**
|
||||
> **业务项目接手后,将下表 TODO 字段替换为真实值,AI 才能给出精准建议。**
|
||||
|
||||
| 字段 | 当前值(脚手架默认) | 说明 |
|
||||
| 字段 | 当前值(脚手架占位)| 说明 |
|
||||
|------|-----------------|------|
|
||||
| 项目名称 | sunny_mochi | TODO: 替换为实际项目名 |
|
||||
| Application ID (Android) | com.example.sunny_mochi | TODO: 如 com.company.projectname |
|
||||
| Bundle ID (iOS) | com.example.sunny-mochi | TODO: 与 Android 对应 |
|
||||
| API Base URL (dev) | http://localhost:8080 | TODO: 填入 `dart-defines/dev.json` |
|
||||
| API Base URL (staging) | https://staging.example.com | TODO: 填入 `dart-defines/staging.json` |
|
||||
| API Base URL (prod) | https://api.example.com | TODO: 填入 `dart-defines/prod.json` |
|
||||
| 后端鉴权框架 | sa-token(动态 header tokenName) | TODO: 若使用 Bearer/JWT 修改 auth_interceptor |
|
||||
| Sentry DSN | 空(未启用) | TODO: 填入 `dart-defines/prod.json` |
|
||||
| RSA 公钥 | REPLACE_WITH_RSA_PUBLIC_KEY | TODO: 填入三个 dart-defines JSON |
|
||||
| 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 存储 | `SecureStorage`(Keychain / EncryptedPrefs)| 普通 `SharedPreferences` |
|
||||
| 数据库加密 | SQLCipher(默认,禁止替换)| 无加密 sqflite |
|
||||
| 日志 | `appTalker.info/warning/error()` | `print()` / `debugPrint()` |
|
||||
| 路由跳转 | `XxxRoute().go(context)` | `Navigator.push()` |
|
||||
| 状态管理 | `@riverpod` / `@Riverpod(keepAlive: true)` | `setState` 跨组件 / Provider 包 |
|
||||
| Riverpod provider 命名 | 生成名(`AuthNotifier` → `authProvider`)| 手写 `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,留空时密码不加密传输 |
|
||||
|
||||
---
|
||||
|
||||
@@ -27,208 +84,146 @@
|
||||
|
||||
| 分类 | 技术 | 版本 |
|
||||
|------|------|------|
|
||||
| Flutter SDK | Flutter | ≥ 3.41.0 |
|
||||
| 状态管理 | Riverpod + riverpod_annotation | ^3.3.0 / ^4.0.0 |
|
||||
| 路由 | GoRouter + go_router_builder | ^17.0.0 / ^4.3.0 |
|
||||
| 状态管理 | 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(锁定)|
|
||||
| 本地数据库 | Drift + SQLCipher | **2.31.0(锁定)** |
|
||||
| 安全存储 | flutter_secure_storage | ^10.0.0 |
|
||||
| 序列化 | Freezed + json_serializable | ^3.x / 6.13.0(锁定)|
|
||||
| 序列化 | Freezed + json_serializable | ^3.x / **6.13.0(锁定)** |
|
||||
| 日志 | Talker + talker_flutter | ^5.x |
|
||||
| 错误追踪 | Sentry Flutter | ^9.0.0 |
|
||||
| i18n | Slang | ^4.0.0 |
|
||||
| 错误追踪 | sentry_flutter | ^9.0.0 |
|
||||
| i18n | Slang(base_locale: zh-CN)| ^4.0.0 |
|
||||
| UI 自适应 | flutter_screenutil(基准 375×812)| ^5.9.0 |
|
||||
| 图片缓存 | cached_network_image | ^3.4.0 |
|
||||
| 字体 | HarmonyOS Sans SC(Regular/Medium/Bold/Black)| GB2312 子集 |
|
||||
|
||||
**版本锁定原因(勿随意升级):**
|
||||
- `drift: 2.31.0` — 2.32+ 需要 analyzer ^10.x,与 riverpod_generator 的 analyzer ^9.x 冲突
|
||||
- `json_serializable: 6.13.0` — 6.13.1+ 同样需要 analyzer ^10.x
|
||||
> `drift: 2.31.0` 和 `json_serializable: 6.13.0` 锁定原因:
|
||||
> 更新版本需要 analyzer ^10.x,与 riverpod_generator 的 analyzer ^9.x 冲突。**勿升级。**
|
||||
|
||||
---
|
||||
|
||||
## 目录结构
|
||||
## 目录结构与关键文件
|
||||
|
||||
```
|
||||
lib/
|
||||
├── main.dart # 6步启动序列(顺序固定,勿调整)
|
||||
├── app.dart # AppRoot → ScreenUtilInit → App → MaterialApp.router
|
||||
├── i18n/ # Slang 翻译(zh-CN 基准 + en 备用)
|
||||
├── 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 按 Flavor 决策
|
||||
│ │ └── api_paths.dart # ★ 新项目必填:所有 API 路径常量
|
||||
│ │ ├── env.dart # ← 读取 dart-defines,只读
|
||||
│ │ ├── api_config.dart # ★ 填入三套环境 baseUrl
|
||||
│ │ └── api_paths.dart # ★ 填入所有 API 路径常量
|
||||
│ ├── router/
|
||||
│ │ ├── app_router.dart # GoRouter provider(keepAlive)
|
||||
│ │ └── routes.dart # ★ 新项目扩展:添加 @TypedGoRoute
|
||||
│ │ ├── app_router.dart # GoRouter provider(ref.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/ # MockAdapter + fixture JSON(dev 调试用)
|
||||
│ │ ├── 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 # Drift @DriftDatabase(schemaVersion 从 1 开始)
|
||||
│ │ ├── secure_storage.dart# token / userId / tokenName 的唯一真源
|
||||
│ │ ├── db_key_provider.dart# AES-256 密钥派生(勿修改策略)
|
||||
│ │ ├── tables/ # Drift 表定义(Users + ErrorLogs + 业务表)
|
||||
│ │ └── daos/ # DAO(只放纯 DB 操作)
|
||||
│ ├── crash/ # CrashReporter(preInit/consumePending/installHooks)
|
||||
│ │ ├── 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 映射
|
||||
│ ├── theme/ # 5 色板 + 深色模式(ThemeNotifier + SharedPreferences)
|
||||
│ ├── observability/ # Talker(全局 appTalker)+ Sentry(Release 上报)
|
||||
│ ├── sync/ # Offline-First 同步框架骨架(SyncService)
|
||||
│ ├── crypto/ # RSA PKCS#1 v1.5(RsaHelper.encrypt)
|
||||
│ ├── extensions/ # BuildContextX / StringX / NumX / DateTimeX
|
||||
│ ├── utils/ # Validator / PasswordValidator / SmsCountdownMixin
|
||||
│ ├── data/ # FreshnessPolicy(缓存新鲜度判断)
|
||||
│ └── widgets/ # 通用 UI 组件(AppToast / AppButton / EmptyView 等)
|
||||
│ │ ├── failures.dart # sealed Failure(禁止改结构)
|
||||
│ │ └── exception_mapper.dart # 异常 → Failure 映射 + Riverpod provider
|
||||
│ └── widgets/ # AppToast / AppButton / EmptyView / AvatarWidget 等
|
||||
└── features/
|
||||
├── auth/ # ★ 认证(完整骨架,业务项目修改 LoginPage UI)
|
||||
├── dev_panel/ # Talker 面板(Internal 包,勿在 release 中暴露入口)
|
||||
└── error_report/ # 本地错误日志 + 上报(完整实现)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 开发命令
|
||||
|
||||
```bash
|
||||
# 代码生成(修改 Dart 文件后运行)
|
||||
make gen
|
||||
|
||||
# i18n 生成(修改 JSON 翻译文件后运行)
|
||||
make gen-i18n
|
||||
|
||||
# 运行
|
||||
make run-dev # dev 包(连 dev 服务器,mock 可开)
|
||||
make run-staging # staging 包(连 test 服务器,release 模式)
|
||||
|
||||
# 构建
|
||||
make build-apk-dev # dev APK(快速真机验证)
|
||||
make build-staging # staging APK(交测试团队)
|
||||
make build-prod # prod AAB(需 prod.json + key.properties)
|
||||
|
||||
# 质量检查
|
||||
flutter analyze # 目标 0 errors(103 条 info 为正常)
|
||||
flutter test # 全量测试
|
||||
├── auth/ # ★ 修改 LoginPage UI 品牌,业务路由骨架已就绪
|
||||
├── dev_panel/ # Talker 面板(INTERNAL_BUILD=true 时可路由进入)
|
||||
└── error_report/ # 本地错误日志 + 批量上报(完整实现)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常用开发范式
|
||||
|
||||
### 新增 Feature
|
||||
### 新增 Feature(标准目录结构)
|
||||
|
||||
```
|
||||
lib/features/{feature_name}/
|
||||
lib/features/{feature}/
|
||||
├── domain/
|
||||
│ ├── entities/{name}_entity.dart # @freezed,纯 Dart 字段
|
||||
│ └── repositories/{name}_repository.dart # abstract interface
|
||||
│ ├── 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,实现 domain 接口
|
||||
│ ├── 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 / ConsumerStatefulWidget
|
||||
├── notifiers/{name}_notifier.dart # @riverpod class XxxNotifier
|
||||
└── pages/{name}_page.dart # ConsumerWidget
|
||||
```
|
||||
|
||||
### 新增路由
|
||||
|
||||
在 `lib/core/router/routes.dart` 添加(然后运行 `make gen`):
|
||||
### 新增路由(routes.dart → make gen)
|
||||
|
||||
```dart
|
||||
@TypedGoRoute<MyNewRoute>(path: '/my-new-path')
|
||||
class MyNewRoute extends GoRouteData with $MyNewRoute {
|
||||
const MyNewRoute();
|
||||
|
||||
@TypedGoRoute<MyRoute>(path: '/my-path')
|
||||
class MyRoute extends GoRouteData with $MyRoute {
|
||||
const MyRoute();
|
||||
@override
|
||||
Widget build(BuildContext context, GoRouterState state) => const MyNewPage();
|
||||
Widget build(BuildContext context, GoRouterState state) => const MyPage();
|
||||
}
|
||||
```
|
||||
|
||||
### 新增 API 路径
|
||||
|
||||
在 `lib/core/config/api_paths.dart` 添加:
|
||||
### 标准错误处理
|
||||
|
||||
```dart
|
||||
// ============== My Feature ==============
|
||||
static const String myFeatureList = '/api/v1/my-feature/list';
|
||||
```
|
||||
|
||||
### 新增 DB 表
|
||||
|
||||
1. 在 `lib/core/storage/tables/` 新建表文件
|
||||
2. 在 `lib/core/storage/app_database.dart` 的 `@DriftDatabase(tables: [...])` 添加
|
||||
3. 递增 `schemaVersion` 并在 `MigrationStrategy.onUpgrade` 中添加迁移
|
||||
4. 运行 `make gen`
|
||||
|
||||
### 错误处理标准模式
|
||||
|
||||
```dart
|
||||
// Notifier 层:捕获 + 映射
|
||||
// Notifier 层
|
||||
} on Object catch (e, st) {
|
||||
final failure = ExceptionMapper().fromUnknown(e, st);
|
||||
state = MyState.error(failure.message);
|
||||
}
|
||||
|
||||
// UI 层:监听 + 显示
|
||||
// UI 层
|
||||
ref.listen(myProvider, (_, next) {
|
||||
if (next is AsyncError) context.showError(ref, next.error!, next.stackTrace!);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
### 新增 DB 表
|
||||
|
||||
## 零容忍规则(AI 必须遵守)
|
||||
|
||||
| 规则 | 正确 | 禁止 |
|
||||
|------|------|------|
|
||||
| API 路径 | `ApiPaths.xxx` 常量 | 字符串字面量散落在代码中 |
|
||||
| 错误处理 | `ExceptionMapper().fromUnknown(e, st)` | 裸 `catch` 直接 `toString()` 显示 |
|
||||
| Token 存储 | `SecureStorage`(Keychain/EncryptedPrefs)| 普通 `SharedPreferences` |
|
||||
| 数据库加密 | SQLCipher(默认) | 无加密 `sqflite` |
|
||||
| 日志输出 | `appTalker.info/warning/error()` | `print()` / `debugPrint()` |
|
||||
| 路由跳转 | `XxxRoute().go(context)` / `XxxRoute().push(context)` | `Navigator.push()` |
|
||||
| 状态管理 | Riverpod `@riverpod` / `@Riverpod(keepAlive: true)` | `setState` 跨组件共享 / `Provider` 包 |
|
||||
| Riverpod 命名 | 生成 provider 名(`AuthNotifier` → `authProvider`)| 手写 `authNotifierProvider` |
|
||||
| 代码生成文件 | 只读,不手写 `.g.dart` / `.freezed.dart` | 手动修改生成文件 |
|
||||
| Domain 层 | 纯 Dart,无 Flutter / Drift / Dio 依赖 | domain 层 import `package:dio` |
|
||||
| Mock fixture | `assets/fixtures/{group}/{name}.json` | 在代码中硬编码 mock 数据 |
|
||||
| 注释语言 | 中文(团队约定) | 英文注释(除公共 API doc)|
|
||||
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`
|
||||
|
||||
---
|
||||
|
||||
## 关键架构决策(勿重议)
|
||||
## 开发命令
|
||||
|
||||
1. **SQLCipher 不可替换**:企业合规要求,部署后无法无损切换到无加密数据库
|
||||
2. **7 拦截器顺序固定**:CertPinning→Auth→TokenRefresh→Retry→Error→AuthLogout→Log,错误处理链依赖此顺序
|
||||
3. **TokenRefresh 使用 Completer 互斥**:防止并发 401 重复消耗 RefreshToken,不可改为简单 flag
|
||||
4. **SecureStorage 是 token 唯一真源**:DB 中的 refreshToken 仅作可观测性镜像
|
||||
5. **GoRouter 用 `ref.read`(非 `ref.watch`)构建**:router 是 keepAlive 单例,通过 `refreshListenable` 响应 auth 变化
|
||||
6. **Failure 不可绕过**:所有网络/DB 异常必须经 ExceptionMapper 映射,UI 只看 Failure.message
|
||||
```bash
|
||||
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
|
||||
> 完成并删除本节(或移至项目 wiki)
|
||||
|
||||
- [ ] 更新本文件"项目身份信息"表格
|
||||
- [ ] 更新本文件"项目身份信息"表格(让 AI 上下文准确)
|
||||
- [ ] 修改 `lib/core/config/api_config.dart` 三套 baseUrl
|
||||
- [ ] 填入 `lib/core/config/api_paths.dart` 所有路径
|
||||
- [ ] 配置三个 `dart-defines/*.json`(含 RSA 公钥、Sentry DSN)
|
||||
- [ ] 修改 `android/app/build.gradle.kts` 中的 `applicationId`
|
||||
- [ ] 修改 `pubspec.yaml` 中的 `name`(含相关 import 路径)
|
||||
- [ ] 完成 `docs/setup-android.md` 中的 Android 签名配置
|
||||
- [ ] 完成 `docs/setup-ios.md` 中的 iOS Scheme / Signing 配置
|
||||
- [ ] 配置三个 `dart-defines/*.json`(RSA 公钥 / Sentry DSN)
|
||||
- [ ] 修改 Android 包名:`android/app/build.gradle.kts` applicationId
|
||||
- [ ] 修改 iOS Bundle ID:Xcode → 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 Icon(Android mipmap / iOS AppIcon.appiconset)
|
||||
- [ ] 实现业务 Tab 页面(替换 `routes.dart` 中的 placeholder build)
|
||||
- [ ] 更新 README.md 项目描述
|
||||
- [ ] 实现 Tab 页面(`lib/core/router/routes.dart` HomeRoute / MineRoute 的 build)
|
||||
- [ ] 更新 README.md 项目描述和快速开始地址
|
||||
|
||||
---
|
||||
|
||||
@@ -236,7 +231,8 @@ ref.listen(myProvider, (_, next) {
|
||||
|
||||
| 文档 | 内容 |
|
||||
|------|------|
|
||||
| [README.md](README.md) | 项目概述、技术栈、快速开始 |
|
||||
| [docs/setup-project.md](docs/setup-project.md) | 完整项目配置指南(所有开发者必读)|
|
||||
| [docs/setup-android.md](docs/setup-android.md) | Android Flavor / 签名 / 构建详细配置 |
|
||||
| [docs/setup-ios.md](docs/setup-ios.md) | iOS Scheme / CocoaPods / 签名详细配置 |
|
||||
| [README.md](../README.md) | 项目概述、技术栈、基础设施一览 |
|
||||
| [docs/setup-project.md](setup-project.md) | 完整配置指南(dart-defines / 包名 / 首次运行)|
|
||||
| [docs/setup-android.md](setup-android.md) | Android Flavor / 签名 / 构建 / CI |
|
||||
| [docs/setup-ios.md](setup-ios.md) | iOS Scheme / CocoaPods / 证书 / Archive |
|
||||
| [docs/architecture.md](architecture.md) | 数据流 / 认证流 / 错误链 ASCII 架构图 |
|
||||
|
||||
Reference in New Issue
Block a user