diff --git a/Doc/IM_App_架构设计.html b/Doc/IM_App_架构设计.html index c69ff14..bfedfb8 100644 --- a/Doc/IM_App_架构设计.html +++ b/Doc/IM_App_架构设计.html @@ -1400,7 +1400,7 @@ flowchart LR
  • 无法精确控制重建范围
  • -

    实际案例(UUTalk):

    +

    实际案例:

    // 整个 Container 都会重建
     Obx(() => Container(
    @@ -1636,7 +1636,7 @@ Get.put<BaseController>(ChatController());
     final controller = Get.find<UserController>();  // 找到 ChatController,类型不匹配!
     
    -

    运行时错误案例(UUTalk 实际问题):

    +

    运行时错误案例:

    -

    GetX + Obx vs Riverpod:来自 UUTalk 项目的实践教训

    +

    GetX + Obx vs Riverpod:实践教训

    -

    真实案例警示

    -

    以下内容基于 UUTalk 项目的实际经验,展示了 GetX + Obx 在大型项目中暴露的严重问题。

    +

    典型问题示例

    +

    以下内容展示了 GetX + Obx 在大型项目中暴露的典型问题,作为选型参考。

    1. 状态管理混乱
    -

    UUTalk 项目的 GetX + Obx 问题代码(chat_list_controller.dart):

    +

    GetX + Obx 问题代码示例(chat_list_controller.dart):

    class ChatListController extends GetxController {
       var lastClickedSpecialChatId = (-1).obs;
    @@ -1728,7 +1728,7 @@ ChatViewModel
     
     
    2. 过度嵌套和性能问题
    -

    UUTalk 项目的 Obx 嵌套地狱(home_view.dart):

    +

    Obx 嵌套地狱示例(home_view.dart):

    body: Obx(() => AnimatedPadding(
       child: GetBuilder(
    @@ -1765,7 +1765,7 @@ ChatViewModel
     
     
    3. Controller 过于臃肿
    -

    UUTalk 项目的巨型 Controller:

    +

    巨型 Controller 反模式示例:

    // chat_list_controller.dart
     import 'dart:async';
    @@ -1811,7 +1811,7 @@ class ChatListController extends GetxController
     
     
    4. 没有编译时安全
    -

    UUTalk 项目的运行时陷阱:

    +

    GetX 运行时陷阱:

    // 通过字符串查找 Controller,运行时才知道对错
     Get.find<ChatListController>();
    @@ -1839,7 +1839,7 @@ class ChatListController extends GetxController {
     
     
    5. 难以测试
    -

    UUTalk 项目的测试困境:

    +

    GetX 测试困境:

    // 测试时必须初始化整个 GetX 框架
     testWidgets('test chat list', (tester) async {
    @@ -1865,7 +1865,7 @@ testWidgets('test chat list', (tester) async {
     
     
     
    -
    GetX + Obx vs Riverpod 实际对比(基于 UUTalk 项目经验)
    +
    GetX + Obx vs Riverpod 实际对比
    @@ -1933,7 +1933,7 @@ testWidgets('test chat list', (tester) async {
    -

    GetX + Obx(UUTalk 现状)

    +

    GetX + Obx

    状态混乱:

    class ChatListController extends GetxController {
    @@ -1999,10 +1999,10 @@ class ChatListViewModel extends StateNotifier<ChatListState> {
     
     
    -
    UUTalk 项目的痛点总结
    +
    GetX + Obx 痛点总结
    -

    从 UUTalk 项目的实际经验中,我们总结出 GetX + Obx 的核心问题:

    +

    从实践经验中,我们总结出 GetX + Obx 的核心问题:

    1. "快速开发"变成"技术债":初期确实快,但 6 个月后代码无法维护
    2. "响应式"变成"性能杀手":Obx 嵌套导致过度重建,页面卡顿
    3. @@ -2014,7 +2014,7 @@ class ChatListViewModel extends StateNotifier<ChatListState> {

      Riverpod 的技术优势

      -

      基于 UUTalk 项目的实践教训,我们选择 Riverpod 作为新架构的状态管理方案,因为它从根本上解决了 GetX 的所有问题:

      +

      基于以上实践教训,我们选择 Riverpod 作为新架构的状态管理方案,因为它从根本上解决了 GetX 的所有问题:

      • 编译时安全:不会再有运行时崩溃
      • 结构化状态:@freezed 强制统一状态结构
      • diff --git a/scripts/build_android.sh b/scripts/build_android.sh index 3c01780..cf6274b 100755 --- a/scripts/build_android.sh +++ b/scripts/build_android.sh @@ -47,6 +47,7 @@ if [ "$FORMAT" = "aab" ]; then cd "$APP_DIR" flutter build appbundle \ --release \ + --no-pub \ --dart-define-from-file=config/config.json \ --split-debug-info="$DEBUG_INFO_DIR" \ --obfuscate @@ -57,6 +58,7 @@ else cd "$APP_DIR" flutter build apk \ --release \ + --no-pub \ --dart-define-from-file=config/config.json \ --split-debug-info="$DEBUG_INFO_DIR" \ --obfuscate diff --git a/scripts/build_ios.sh b/scripts/build_ios.sh index bcf2b60..b0c5106 100644 --- a/scripts/build_ios.sh +++ b/scripts/build_ios.sh @@ -41,6 +41,7 @@ cd "$APP_DIR" flutter build ipa \ --release \ + --no-pub \ --dart-define-from-file=config/config.json \ --split-debug-info="$DEBUG_INFO_DIR" \ --obfuscate diff --git a/scripts/build_macos.sh b/scripts/build_macos.sh index 4ffef82..26f30b1 100644 --- a/scripts/build_macos.sh +++ b/scripts/build_macos.sh @@ -37,6 +37,7 @@ cd "$APP_DIR" flutter build macos \ --release \ + --no-pub \ --dart-define-from-file=config/config.json \ --split-debug-info="$DEBUG_INFO_DIR" \ --obfuscate diff --git a/scripts/build_windows.sh b/scripts/build_windows.sh index d59d24c..44916ed 100644 --- a/scripts/build_windows.sh +++ b/scripts/build_windows.sh @@ -39,6 +39,7 @@ cd "$APP_DIR" flutter build windows \ --release \ + --no-pub \ --dart-define-from-file=config/config.json \ --split-debug-info="$DEBUG_INFO_DIR" \ --obfuscate diff --git a/scripts/pre-commit b/scripts/pre-commit new file mode 100644 index 0000000..7502da0 --- /dev/null +++ b/scripts/pre-commit @@ -0,0 +1,60 @@ +#!/bin/bash +# Git pre-commit hook:提交前自动 format,analyze 有 error 则拦截提交 +# +# 安装:bash scripts/setup.sh(setup.sh 自动把本文件复制到 .git/hooks/pre-commit) +# 手动安装:cp scripts/pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit + +set -euo pipefail + +ROOT_DIR="$(git rev-parse --show-toplevel)" +DART="$(command -v dart 2>/dev/null || echo "")" + +if [[ -z "$DART" ]]; then + echo "[pre-commit] dart not found, skipping checks." + exit 0 +fi + +# ── pubspec.lock 保护 ──────────────────────────────────────────────────────── +# flutter run / flutter build 会在未经授权的情况下触发 pub get 并修改 lock。 +# 规则:lock 有未暂存的改动(意外修改)→ 自动还原; +# 开发者主动 git add pubspec.lock 后(暂存)→ 正常放行。 +LOCK_UNSTAGED=$(git diff --name-only pubspec.lock 2>/dev/null || true) +LOCK_STAGED=$(git diff --cached --name-only pubspec.lock 2>/dev/null || true) + +if [[ -n "$LOCK_UNSTAGED" && -z "$LOCK_STAGED" ]]; then + git checkout HEAD -- pubspec.lock + echo "[pre-commit] pubspec.lock was auto-modified by flutter, restored to HEAD." + echo " To intentionally update: run 'dart pub get' or 'dart pub upgrade'," + echo " then 'git add pubspec.lock' before committing." +fi + +# ── dart format + analyze ──────────────────────────────────────────────────── +STAGED_DART=$(git diff --cached --name-only --diff-filter=ACM | grep '\.dart$' || true) + +if [[ -z "$STAGED_DART" ]]; then + exit 0 +fi + +echo "[pre-commit] formatting staged dart files..." +echo "$STAGED_DART" | xargs dart format --line-length=80 +echo "$STAGED_DART" | xargs git add + +echo "[pre-commit] running dart analyze..." +cd "$ROOT_DIR" + +ANALYZE_OUTPUT=$(dart analyze 2>&1) + +if echo "$ANALYZE_OUTPUT" | grep -q "^ error"; then + echo "[pre-commit] dart analyze found errors, commit blocked." + echo "$ANALYZE_OUTPUT" | grep "^ error" + exit 1 +fi + +if echo "$ANALYZE_OUTPUT" | grep -q "^ warning"; then + echo "[pre-commit] dart analyze found warnings, commit blocked." + echo "$ANALYZE_OUTPUT" | grep "^ warning" + exit 1 +fi + +echo "[pre-commit] all checks passed." +exit 0 diff --git a/scripts/setup.sh b/scripts/setup.sh index a63e951..50d9e3a 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -28,13 +28,13 @@ echo "=== customer-im-client 环境初始化 ===" echo "" # ---- Step 1: 安装全局工具 ---- -echo "[1/6] 安装全局工具 (melos + mason_cli)..." +echo "[1/7] 安装全局工具 (melos + mason_cli)..." dart pub global activate melos dart pub global activate mason_cli echo "" # ---- Step 2: 配置 PATH ---- -echo "[2/6] 配置 PATH..." +echo "[2/7] 配置 PATH..." if [[ "$SHELL" == */zsh ]]; then SHELL_CONFIG="$HOME/.zshrc" elif [[ "$SHELL" == */bash ]]; then @@ -78,12 +78,19 @@ if [ -f "$ROOT_DIR/mason-lock.json" ]; then fi # ---- Step 5: 注册 mason bricks ---- -echo "[5/6] mason get (注册本地 bricks)..." +echo "[5/7] mason get (注册本地 bricks)..." cd "$ROOT_DIR" && "$PUB_CACHE_BIN/mason" get echo "" +echo "[6/7] mason add clean_plugin_sdk bricks..." +if ! grep -qE '^\s*clean_plugin_sdk:' "$ROOT_DIR/mason.yaml" 2>/dev/null; then + cd "$ROOT_DIR" && "$PUB_CACHE_BIN/mason" add clean_plugin_sdk --path ./mason/bricks/clean_plugin_sdk +else + echo " clean_plugin_sdk 已在 mason.yaml 中,跳過" +fi + # ---- Step 6: 首次代码生成 ---- -echo "[6/6] melos run gen (首次代码生成)..." +echo "[7/7] melos run gen (首次代码生成)..." cd "$ROOT_DIR" && "$PUB_CACHE_BIN/melos" run gen echo ""