diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..86826c6 --- /dev/null +++ b/TODO.md @@ -0,0 +1,221 @@ +# TODO — Flutter Wisetronic Dart 3 空安全迁移(剩余工作) + +> **当前进度**:错误从峰值 **2374** 降到 **118(lib)/ 112(web 构建)**,0 语法损坏。 +> 分支 `dev2`,最新提交已 clean。Web 构建 (`flutter build web`) 目标:0 error → 可运行。 + +--- + +## 0. 快速恢复(Resume 起点) + +```bash +cd /Users/peima/StudioProjects/flutter_wisetronic +git log --oneline -1 # 确认在最新提交 +dart analyze lib | tail -5 # 看当前错误(应 118 error) +flutter build web 2>&1 | grep -c '^Error:' # web 错误(应 112) + +# 重跑自动修复脚本(安全,带损坏自检,可随时跑) +python3 tools/nullfix.py --max-rounds 12 +``` + +**核心原则**:每次改动后用 `dart analyze lib | grep -cE '\s+error\s+-'` 数错误;脚本若报"检测到语法损坏"会自动中止,`git checkout -- lib` 回滚。 + +--- + +## 1. 剩余错误分布(118 个) + +| 规则 | 数量 | 说明 | +|------|------|------| +| `unchecked_use_of_nullable_value` | 44 | 可空值当非空用(方法调用/条件/迭代器,多为多行) | +| `invalid_assignment` | 34 | 赋值类型不匹配(复杂表达式) | +| `argument_type_not_assignable` | 22 | 参数类型不匹配(三元/比较/Map字面量) | +| `return_of_invalid_type_from_closure` | 4 | 闭包返回类型 | +| `undefined_getter` | 3 | 多为 `.buttonColor`(Flutter 3 移除) | +| `return_of_invalid_type` | 3 | 函数 `return null` 但返回类型非空 | +| `undefined_named_parameter` | 2 | | +| `undefined_method` | 2 | | +| `creation_with_non_type` | 2 | 类名拼错(util_io 内) | +| `extra_positional_arguments_could_be_named` | 1 | | +| `body_might_complete_normally` | 1 | | + +**错误最多的文件**(详细见 `dart analyze lib`): + +``` + 6 utils/util_io.dart ← 延后(仅原生,Web 构建已排除) + 5 widgets/desktop/desktop_edit_address.dart ← .trim() 多行(见 §3,已知修法) + 5 widgets/mobile/mobile_edit_address.dart ← 同上 + 5 widgets/general/add_remove_button.dart + 5 widgets/general/parabolic_animation_widget.dart + 5 widgets/general/sliding_up_panel.dart + 4 widgets/desktop/desktop_me.dart + 4 widgets/mobile/mobile_attribute_selection.dart + 4 widgets/mobile/mobile_order_detail.dart + 3 widgets/desktop/desktop_coupons.dart / desktop_my_support / desktop_order_detail / shop_products + 3 widgets/general/animation_point_manager / popup_animation_widget + 3 widgets/mobile/mobile_coupons / mobile_me / mobile_my_support / mobile_orders / shopping_cart_bar + 2 约 12 个文件(blog/orders/product_item/shopping_cart 等) + 1 约 25 个文件(单点错误) +``` + +--- + +## 2. 推荐处理顺序 + +1. **edit_address(5+5)** — 已知修法,见 §3,最快。 +2. **parabolic/popup_animation(5+3)** — `late` 字段已修,可能还有 unchecked_use(offset 计算方法调用),手动看。 +3. **sliding_up_panel(5)** — 第三方面板 widget 适配,手动。 +4. **add_remove_button(5)** — badges 相关,手动。 +5. **散落的 2-error / 1-error 文件** — 用 `dart analyze lib/` 逐个看,多为加 `!`。 +6. **util_io.dart(6)** — 最后处理(见 §5),不影响 Web 构建。 + +**目标**:先清掉 Web 可达错误(`flutter build web` 的 112),让 Web 能跑;`util_io` 原生路径可延后。 + +--- + +## 3. 已知具体修法(直接套用) + +### 3.1 edit_address `.trim()` 多行(desktop + mobile,各 5 处) +错误:`A nullable expression can't be used to invoke 'trim'`。代码结构: +```dart +validator: (String? value) { + if (value // ← value 是 String? + .trim() + .isEmpty) { +``` +**修法**:把 `if (value` 行改成 `if (value!`。 +```python +# 上次脚本 bug:用了 rstrip() 应该用 strip()。正确版: +for p in ['lib/widgets/desktop/desktop_edit_address.dart','lib/widgets/mobile/mobile_edit_address.dart']: + lines=open(p).read().split('\n'); n=0 + for i in range(len(lines)): + if lines[i].strip()=='if (value' and lines[i+1].lstrip().startswith('.trim()'): + lines[i]=lines[i].rstrip()+'!'; n+=1 # 注意 rstrip() 保留前导缩进 + open(p,'w').write('\n'.join(lines)) +``` +(`.buttonColor`→`.colorScheme.primary` 已在两个文件修好。) + +### 3.2 通用模式(手动加 `!` / `?? 默认` / `== true`) +- `String? → String` 参数:`field` → `field!`(确认运行时非空)。 +- `num?/int?/double?` 比较:`subtotal > minPrice` → `subtotal > minPrice!` 或 `(minPrice ?? 0)`。 +- `bool?` 当条件:`if (x)` → `if (x == true)`;`if (!x)` → `if (x != true)`。 +- `return null;` 但返回类型非空:把函数返回类型加 `?`(如 `CategoryProducts? foo()`)。 +- 局部变量赋值 nullable:声明加 `?`(如 `Map? x = ...`),下游 nullfix 会补 `!`。 +- 多行方法调用 `.toUpperCase()` / `.trim()`:接收者在上一行末尾,在该行末尾加 `!`。 + +### 3.3 可复用的定向脚本(一次性,贴进 bash 跑) + +**a) 条件修复(analyzer 驱动,只修简单 `if (chain)`)** — 已用过,安全: +```python +python3 -c " +import re,subprocess +ERR=re.compile(r'\s+error\s+-\s+([\w/.\-]+):(\d+):(\d+)\s+-\s+(.*?)\s+-\s+([a-z_]+)\s*\$') +pat=re.compile(r'^(\s*if \()(!?)([\w.!\[\]]+)(\)\s*\{?\s*)\$') +out=subprocess.run(['dart','analyze','lib'],capture_output=True,text=True).stdout +tg={} +for ln in out.splitlines(): + m=ERR.match(ln) + if m and 'used as a condition' in m.group(4): tg.setdefault('lib/'+m.group(1),set()).add(int(m.group(2))) +n=0 +for p,ls in tg.items(): + L=open(p).read().split('\n');ch=False + for i,x in enumerate(L,1): + if i in ls: + m=pat.match(x.rstrip('\n')) + if m: L[i-1]=m.group(1)+m.group(3)+(' != true' if m.group(2) else ' == true')+m.group(4);ch=True;n+=1 + if ch: open(p,'w').write('\n'.join(L)) +print('fixed',n) +" +``` + +**b) 局部变量改可空(严格类型,已验证安全)** — 跑完再 `python3 tools/nullfix.py` 吃级联: +```python +python3 -c " +import re,subprocess +ERR=re.compile(r'\s+error\s+-\s+([\w/.\-]+):(\d+):(\d+)\s+-\s+(.*?)\s+-\s+([a-z_]+)\s*\$') +NAMEQ=re.compile(r\"'([^']+)'\"); TYPE=r'(?:[A-Z]\w*(?:<[^;]*>)?|int|double|String|bool|num|dynamic|List<[^;]*>|Map<[^;]*>|Set<[^;]*>)' +out=subprocess.run(['dart','analyze','lib'],capture_output=True,text=True).stdout;tg={} +for ln in out.splitlines(): + m=ERR.match(ln) + if m and m.group(5)=='not_assigned_potentially_non_nullable_local_variable': + nm=NAMEQ.search(m.group(4)) + if nm: tg.setdefault('lib/'+m.group(1),[]).append((int(m.group(2)),nm.group(1))) +dp=re.compile(r'^(\s+(?:final\s+|const\s+)?)('+TYPE+r')\s+(\w+)\s*;\s*\$');n=0 +for p,it in tg.items(): + L=open(p).read().split('\n');ch=False;done=set() + for ul,nm in it: + if (ul,nm) in done: continue + done.add((ul,nm)) + for i in range(ul-2,-1,-1): + m=dp.match(L[i]) + if m and m.group(3)==nm and '?' not in m.group(2): L[i]=m.group(1)+m.group(2)+'? '+nm+';';ch=True;n+=1;break + if ch: open(p,'w').write('\n'.join(L)) +print('fixed',n) +" +``` + +--- + +## 4. 工具脚本说明(`tools/`) + +### `tools/nullfix.py` — 主力自动修复 +```bash +python3 tools/nullfix.py # 修整个 lib,默认 5 轮 +python3 tools/nullfix.py --max-rounds 12 +python3 tools/nullfix.py lib/widgets/mobile/shop.dart # 只修单文件 +``` +- 每轮重新 `dart analyze lib`,按 error 的 (file,line,col) 精确定位修改。 +- **带腐败自检**:每轮后检测 `expected_token`/`missing_identifier`,一旦 >0 立即中止并提示 `git diff`。 +- 已实现的 handler: + - `insert_bang`:成员访问/方法调用/迭代器/return 加 `!`(按 analyzer 分类) + - `insert_arg_bang`:`argument_type`/`invalid_assignment` 在参数值末尾加 `!`(值必须是简单标识符链 `[\w.\[\]()!]`,含运算符/空格跳过) + - `late` 实例字段、参数空安全、field-formal 字段空安全 + - `missing_default_value_for_parameter`、`@required`→`required` 已并入 +- **已禁用的危险 handler**:基于正则的 `if(x)`→`if(x==true)`(曾破坏 89 文件)。条件修复改用 §3.3a 的 analyzer 驱动版。 +- **已知边界**:多行表达式(接收者跨行)、含空格/逗号的函数调用参数、三元/比较表达式 → 跳过,需手动。 + +### `tools/badge_migrate.py` — 未验证,**已不需要** +badges 3.x 迁移已手动完成(`badgeStyle`/`badgeAnimation`),此脚本可忽略/删除。 + +--- + +## 5. 延后项 + +### `lib/utils/util_io.dart`(6 error,仅原生) +- 通过 `dart.library.html` 条件导入从 **Web 构建中排除**,不影响 `flutter build web`。 +- 错误来自 `flutter_local_notifications` v17 API 变更: + - `IOSInitializationSettings` / `IOSNotificationDetails` → v17 改名(`DarwinInitializationSettings` / `DarwinNotificationDetails`) + - `onSelectNotification` 参数已移除 + - `extra_positional_arguments`(构造参数变化) + - `body_might_complete_normally`(`Future` 缺 return) +- 原生(Android/iOS)构建时才需要修。Web 不阻塞。 + +--- + +## 6. 关键上下文 / 决策(避免踩坑) + +- **`late` 用于实例字段**:避免模型 `fromJson` 初始化列表 + State 类的级联错误。模型字段置为可空,调用点加 `!`。 +- **脚本必须 analyzer 驱动**:盲目正则会损坏代码(如 `return? item;`、`this.?field`)。`nullfix.py` 用 analyzer 的列/列定位 + 腐败自检是关键。 +- **badges 实际解析 3.2.0**(不是 2.0.3):参数是 `badgeStyle:`/`badgeAnimation:`(不是 `badgeColor`/`animationType`/`animation`)。 +- **pinput 实际 5.0.2**:类名 `Pinput`(非 `PinPut`),导入 `package:pinput/pinput.dart`,`length`/`onCompleted`/`defaultPinDecoration`。 +- **youtube_player_iframe 5.2.2**:`YoutubePlayerController.fromVideoId(videoId:, startSeconds:, params:)`。 +- **BreadCrumb.route 已改可空**:`String? route`,调用点 `BreadCrumb(X, null)` 合法。 +- **e-Transfer**:Stripe 已全移除,付款走 `payment@wisetronic.com`,备注写 `R#`。 +- **搜索框**:自写轻量 `lib/widgets/general/search_bar.dart`(非第三方包)。 +- **不升级全局 Flutter SDK**:用本地 Flutter 3.29.3 / Dart 3.7.2。 + +## 7. 提交历史(基线 f8a90ad 之后) + +``` +最新 phase3: mobile/shop clean ... 131->118 ← resume 起点 + phase3: checkout files clean ... 142->131 + phase3: fix attribute widgets ... ~30 fixed + phase3: FIX arg-bang regex (missing \]) ... 235->183 ← 关键 bug 修复 + ...(共约 28 个 phase3 提交) +f8a90ad 基线 +``` + +## 8. 最终验收 + +```bash +flutter build web # 目标:无 error,生成 build/web +flutter run -d chrome # 目标:能打开、首页/商品/购物车/结账流程跑通 +```