Files
flutter_wisetronic/TODO.md

11 KiB
Raw Blame History

TODO — Flutter Wisetronic Dart 3 空安全迁移(剩余工作)

当前进度:错误从峰值 2374 降到 118lib/ 112web 构建)0 语法损坏。 分支 dev2,最新提交已 clean。Web 构建 (flutter build web) 目标0 error → 可运行。


0. 快速恢复Resume 起点)

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 多为 .buttonColorFlutter 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_address5+5 — 已知修法,见 §3最快。
  2. parabolic/popup_animation5+3late 字段已修,可能还有 unchecked_useoffset 计算方法调用),手动看。
  3. sliding_up_panel5 — 第三方面板 widget 适配,手动。
  4. add_remove_button5 — badges 相关,手动。
  5. 散落的 2-error / 1-error 文件 — 用 dart analyze lib/<file> 逐个看,多为加 !
  6. util_io.dart6 — 最后处理(见 §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'。代码结构:

validator: (String? value) {
  if (value          // ← value 是 String?
      .trim()
      .isEmpty) {

修法:把 if (value 行改成 if (value!

# 上次脚本 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 参数:fieldfield!(确认运行时非空)。
  • num?/int?/double? 比较:subtotal > minPricesubtotal > minPrice!(minPrice ?? 0)
  • bool? 当条件:if (x)if (x == true)if (!x)if (x != true)
  • return null; 但返回类型非空:把函数返回类型加 ?(如 CategoryProducts? foo())。
  • 局部变量赋值 nullable声明加 ?(如 Map<String,dynamic>? x = ...),下游 nullfix 会补 !
  • 多行方法调用 .toUpperCase() / .trim():接收者在上一行末尾,在该行末尾加 !

3.3 可复用的定向脚本(一次性,贴进 bash 跑)

a) 条件修复analyzer 驱动,只修简单 if (chain) — 已用过,安全:

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 吃级联:

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 — 主力自动修复

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_bangargument_type/invalid_assignment 在参数值末尾加 !(值必须是简单标识符链 [\w.\[\]()!],含运算符/空格跳过)
    • late 实例字段、参数空安全、field-formal 字段空安全
    • missing_default_value_for_parameter@requiredrequired 已并入
  • 已禁用的危险 handler:基于正则的 if(x)if(x==true)(曾破坏 89 文件)。条件修复改用 §3.3a 的 analyzer 驱动版。
  • 已知边界:多行表达式(接收者跨行)、含空格/逗号的函数调用参数、三元/比较表达式 → 跳过,需手动。

tools/badge_migrate.py — 未验证,已不需要

badges 3.x 迁移已手动完成(badgeStyle/badgeAnimation),此脚本可忽略/删除。


5. 延后项

lib/utils/util_io.dart6 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_normallyFuture<void> 缺 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.dartlength/onCompleted/defaultPinDecoration
  • youtube_player_iframe 5.2.2YoutubePlayerController.fromVideoId(videoId:, startSeconds:, params:)
  • BreadCrumb.route 已改可空String? route,调用点 BreadCrumb(X, null) 合法。
  • e-TransferStripe 已全移除,付款走 payment@wisetronic.com,备注写 R#<receipt.id>
  • 搜索框:自写轻量 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. 最终验收

flutter build web        # 目标:无 error生成 build/web
flutter run -d chrome    # 目标:能打开、首页/商品/购物车/结账流程跑通