conti-retail-app
大陆马门店 App。Flutter + Melos 单仓多包。
架构约束全部来自相邻仓库 conti-docs/ 的 01–14 号文档。本仓库对这些文档的
偏离,逐条记在 SCAFFOLD-NOTES.md——那份文件是回写文档的
依据,改动之前先看一眼。
怎么跑起来
三步。不要跳过第二步。
# 0. 工具链(一次性)
fvm use 3.44.9 # 或者保证 PATH 上的 flutter 就是 3.44.9
dart pub global activate melos 8.2.2
# 1. 装依赖
melos bootstrap
# 2. 生成代码(riverpod 的 *.g.dart 不入库,不跑这步全仓库编译不过)
melos run gen
# 3. 跑起来
cd app
flutter run --flavor dev -t lib/main_dev.dart --dart-define-from-file=env/dev.json
- 或者安装vscodeflutter插件并修改launch.json F5运行
{
"version": "0.0.1",
"configurations": [
{
"name": "Flutter Dev (Web)",
"request": "launch",
"type": "dart",
"program": "app/lib/main_dev.dart",
"flavor": "dev",
"deviceId": "chrome",
"toolArgs": [
"--dart-define-from-file=env/dev.json"
]
}
]
}
--flavor / -t / --dart-define-from-file 三个参数缺一不可:
- 少了
--dart-define-from-file,AppEnv.fromDartDefine会在启动瞬间抛错。 这是故意的——带着空 baseUrl 跑起来,问题会在第一个请求 404 时才暴露。 - 少了
-t,Flutter 会去找lib/main.dart,本仓库没有这个文件。
VS Code / Android Studio 用户建议把三条 flavor 配进 launch.json。
环境与工具链版本
跑通时的实测版本,与 pubspec.lock 一致:
| 版本 | 备注 | |
|---|---|---|
| Flutter | 3.44.9 | .fvmrc 里锁着 |
| Dart | 3.12.2 | Flutter 自带的那个,见下面的坑 |
| Melos | 8.2.2 | 配置内联在根 pubspec.yaml,没有 melos.yaml |
| flutter_riverpod | 3.3.2 | |
| riverpod_annotation | 4.0.3 | |
| riverpod_generator | 4.0.4 | |
| build_runner | 2.15.1 | |
| analyzer | 12.1.0 |
riverpod 的版本被
flutter_test卡着。flutter_testpin 了test_api 0.7.11,往上升 riverpod 会拉起不兼容的analyzer,解析直接失败。 想升级先确认这条链路,不要只看 pub.dev 上的最新版。
坑:机器上有两个 Dart SDK
如果你的 PATH 上装了独立的 Dart SDK(dart --version 不等于 3.12.2),那么
dart run build_runner build # ← 会用错 SDK,报一堆看不懂的解析错误
要显式用 Flutter 自带的那个:
export FDART="$(dirname "$(which flutter)")/cache/dart-sdk/bin/dart"
$FDART run build_runner build
# fvm 用户:~/fvm/versions/stable/bin/cache/dart-sdk/bin/dart
melos run gen 内部走的是 dart run,所以同样受影响。最省事的做法是让 PATH
上只有 Flutter 自带的 Dart。
坑:melos 命令
pub cache 里只有 melos.bat,git-bash 下直接敲 melos 可能找不到。用:
dart pub global run melos:melos <cmd>
常用命令
melos run gen # 代码生成(riverpod)
melos run gen:watch # 开发期常驻
melos run gen:pigeon # native_* 的 Pigeon 产物(产物入库)
melos run analyze # flutter analyze --fatal-infos,含 riverpod_lint
melos run format # dart format --set-exit-if-changed
melos run test # flutter test --coverage
analyze 带 --fatal-infos。不加等于没加 lint——绝大多数 riverpod_lint 规则
报的是 info 级。
riverpod_lint 由 flutter analyze 直接执行(顶层 plugins: 映射),不再
需要 custom_lint,也没有 dart run custom_lint 这一步。文档 03/14 写的还是
旧方案,见 SCAFFOLD-NOTES §B。
包结构与依赖规则
app/ 壳工程。唯一知道所有包的地方:环境注入、启动编排、路由聚合
packages/
core_foundation/ AppEnv / AppException 体系 / ApiCode ← 叶子包,谁都能依赖
core_logging/ AppLogger / CrashReporter / 脱敏
core_analytics/ Analytics 接口 + 事件常量表
core_storage/ Prefs(KV)。Drift 暂缓,见 SCAFFOLD-NOTES
core_auth/ AppSession 四态 / TokenStorage / SessionNotifier
core_network/ dio + 4 个拦截器 + ApiClient
core_router/ goRouterProvider / menuRouteMap / go_router re-export
core_ui/ 主题 / AsyncValueView / ErrorPresenter
core_webview/ UrlGuard / JSBridge / WebViewSession
feature_auth/ 登录、选店
feature_home/ 工作台
native_scan/ Pigeon 扫码接口(原生实现待补)
依赖规则(01):
feature_* → core_* / native_*。feature 之间禁止互相依赖——两个 feature 要共享东西,说明那个东西属于某个 core_*。core_* → core_*只允许三条边:core_network → core_auth、core_router → core_auth、core_webview → core_auth。外加所有包都可以依赖 叶子包core_foundation。native_*只依赖 Flutter SDK 和 Pigeon 产物,一个 core_ 都不依赖*。- feature 的 pubspec 里不出现
go_router:路由类型由core_routerre-export。
这些规则靠 --fatal-infos 守,不是靠编译器(实测结论)
直觉上会以为「没在 pubspec 里声明就 import 不到」。在 Pub Workspace 里这是错的:
所有成员包共用根目录一份 .dart_tool/package_config.json,任何成员都能解析到
任何其他成员。实测在 feature_home 里 import feature_auth 而不声明依赖:
flutter test → All tests passed! ← 编译通过,跑得起来
flutter analyze → info: depend_on_referenced_packages
只有一条 info。所以:
melos run analyze的--fatal-infos是这套包边界唯一的强制点。 谁把它从 CI 里拿掉,或者在某个包里 ignore 掉depend_on_referenced_packages,边界当天就失效,而且没有任何别的信号。
想自己复现:在 feature_home/lib/src/ 下扔一个 import feature_auth 的文件,
flutter test 是绿的,flutter analyze --fatal-infos 是红的。
那些 throw UnimplementedError('必须在 bootstrap 里 override')
依赖规则会挡住一些合理的调用(比如 core_auth 想发 HTTP、core_webview 想
换票)。这些地方一律用依赖反转解决:包内声明 abstract interface + 一个会抛错
的 provider,实现落在 app/lib/bootstrap.dart 里 override 进去。
新增一个这样的端口时,必须同时在 bootstrap 里接上——它是运行期才炸的, 编译器帮不了你。
待办 / 阻塞项
- iOS flavor 未配置:Scheme 和 Build Configuration 只能在 Xcode 里建,
步骤见
app/ios/FLAVORS.md。目前没有 Mac 构建机。 - native_scan 没有原生实现:Pigeon 接口和 Dart 侧齐了,Kotlin/Swift 侧是
模板。现在调
startScan会抛MissingPluginException,属预期。 - 神策 SDK 未采购:
analyticsProvider是NoopAnalytics。接入时注意必须在 用户同意隐私政策之后再初始化。 - env/*.json 里全是占位域名,
SENTRY_DSN全空,待运维确认。 - CI 镜像名未定,
.gitlab-ci.yml里标了 TODO(ops)。