Files
jiu/docs/review/flutter-layout-bugs.md
T
wangjia 66e54af8c6 feat: 商品追踪页面 + 出库提交库存校验
后端:
- feat(backend): 重命名 Batches→Products 接口,新增库存状态(在售/已卖出)和买家信息
- feat(backend): 出库单创建/提交时校验仓库库存,不足则返回明确错误信息
- fix(backend): 出库创建 status 判断逻辑修复(空值默认 draft)

前端:
- feat(client): 批次追踪改为商品追踪,新增状态列(在售/已卖出)和买家/时间列
- fix(client): 无批次号时显示"无批次"而非空
- refactor(client): BatchRecord → ProductTrackingRecord,repository 接口更新

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 22:26:42 +08:00

2.5 KiB
Raw Blame History

Flutter 布局 Bug 报告

BUG-001

严重程度:高

问题描述 DataTableCard 将 toolbar 包裹在 SingleChildScrollView(scrollDirection: Axis.horizontal) 中, 而 toolbar 内使用了 SpacerFlexible 组件)。Spacer 在无界宽度约束下无法布局,导致运行时 抛出 Flutter assertion

RenderFlex children have non-zero flex but incoming width constraints are unbounded.

受影响页面:

  • ProductsScreenproducts_screen.dart:139
  • PartnersScreenpartners_screen.dart
  • StockInListScreenstock_in_list_screen.dart

复现步骤

  1. 打开包含 DataTableCard 的任意页面(商品、往来单位、入库单等)
  2. 由于 SingleChildScrollView 提供无界宽度,toolbar 中的 Spacer 无法展开
  3. Flutter 抛出断言错误,页面渲染失败(测试中表现为所有 Widget 测试失败)

失败的测试用例 所有 products_screen_test.dartpartners_screen_test.dartstock_in_screen_test.dart 中的 测试均失败,错误来自 data_table_card.dart:35 的布局计算。

根因分析 client/lib/widgets/data_table_card.dart 第 33-46 行:

SingleChildScrollView(
  scrollDirection: Axis.horizontal,
  padding: const EdgeInsets.symmetric(horizontal: 12),
  child: Row(
    children: [
      ConstrainedBox(
        constraints: BoxConstraints(
          minWidth: (MediaQuery.of(context).size.width - 212 - 24).clamp(0.0, double.infinity),
        ),
        child: toolbar!,  // toolbar 中含有 Spacer,而父容器宽度无界
      ),
    ],
  ),
),

ConstrainedBox 设置了 minWidth,但 SingleChildScrollView 为子组件提供的是 BoxConstraints(minWidth, Infinity) — 最大宽度无界。Spacer 尝试填充剩余空间 (即无限空间),触发 Flutter 的约束断言。

修复建议 方案 A:将 ConstrainedBox 改为固定宽度的 SizedBox,让 toolbar 占满 minWidth 并把 toolbar 的 Spacer 替换为 MainAxisAlignment.spaceBetweenSizedBox(width: X) 固定间距:

// data_table_card.dart - 去掉 SingleChildScrollView 横向滚动,
// 改用固定宽度容器,或让 toolbar Row 使用 mainAxisSize: MainAxisSize.min
SizedBox(
  width: (MediaQuery.of(context).size.width - 212 - 24).clamp(0.0, double.infinity),
  child: toolbar!,
)

方案 B:在各 Screen 的 toolbar Row 中将 Spacer() 替换为 SizedBox(width: 8) 并在 DataTableCard 中改用非横向滚动的固定宽度容器。