Skip to content

fix(grid): stretch cells with a min height to drop the 2px overflow (#141)#142

Merged
anilcancakir merged 2 commits into
masterfrom
fix/grid-stretch-2px-overflow
Jul 1, 2026
Merged

fix(grid): stretch cells with a min height to drop the 2px overflow (#141)#142
anilcancakir merged 2 commits into
masterfrom
fix/grid-stretch-2px-overflow

Conversation

@anilcancakir

@anilcancakir anilcancakir commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #141, a follow-up to #139. grid ... items-stretch no longer crashes (thanks to #139's WindEqualHeightRow), but it emitted a residual RenderFlex overflowed by 2.0 pixels on the bottom on the row that actually gets stretched.

Cause

WindEqualHeightRow re-laid each cell to a tight height equal to the loose-measured row max. A cell whose flex flex-col content needs a hair more under the tight re-lay than the loose pass reported (sub-pixel text/flex rounding on real rendering, e.g. CanvasKit) then overflowed by ~2px.

Fix

Stretch with a min height instead of a tight one: each cell is laid out with minHeight = row target and an unbounded (or the incoming) max, so it is never forced below its own content, and the row takes the tallest resulting height. Overflow is impossible by construction; the common integer-metric case still yields perfectly equal cells. (This also folds the previous separate measure/relay passes into one positioning pass.)

Tests / reproducibility

The 2px overflow is specific to fractional glyph metrics (CanvasKit) and is not reproducible in the flutter_test binding (integer test-font metrics), so I could not write a red→green test for the exact pixel. The added guard (grid_stretch_test.dart #141 case) asserts no exception + equal heights on the repro shape, and the fix is correct by construction (a min-height stretch cannot squeeze content below its own height, so RenderFlex cannot overflow).

Definition of done

  • dart analyze: clean. dart format: no diff.
  • flutter test: all pass, coverage 94.4% ≥ 90% (wind_equal_height_row.dart fully covered; no conditional re-lay branch left uncovered).
  • Post-change sync: doc/layout/grid.md, skill (tokens.md, SKILL.md version 2.8.2), CHANGELOG.md. WindEqualHeightRow stays internal (README unchanged).

Summary by CodeRabbit

  • Bug Fixes

    • Improved equal-height grid rows so stretched cells stay aligned without triggering small overflow warnings.
    • Fixed cases with uneven content in nested flex layouts to render cleanly while keeping matching row heights.
  • Documentation

    • Clarified grid and layout docs to describe the updated stretch behavior and its impact on overflow warnings.
  • Tests

    • Added coverage to verify stretched unequal cells render without overflow exceptions and remain the same height.

…141)

Follow-up to #139. WindEqualHeightRow re-laid each cell to a TIGHT height
equal to the loose-measured row max. A cell whose flex flex-col content
needs a hair more under the tight re-lay than the loose measure reported
(sub-pixel text/flex rounding on real rendering, e.g. CanvasKit) then
overflowed by ~2px ("RenderFlex overflowed by 2.0 pixels on the bottom") on
the stretched row.

Stretch with a MIN height instead of a tight one: a cell is laid out with
minHeight = the row target and an unbounded (or the incoming) max, so it is
never forced below its own content, and the row takes the tallest resulting
height. Overflow is impossible by construction; the common (integer-metric)
case still yields perfectly equal cells.

Note: the 2px overflow is specific to fractional glyph metrics (CanvasKit)
and is not reproducible in the flutter_test binding, so the guard test
asserts no exception + equal heights; the fix is by construction (min-height
never squeezes content).

Post-change sync: grid_stretch_test #141 no-overflow case; doc/layout/grid.md,
skill (tokens.md, version 2.8.2), CHANGELOG.

Closes #141
Copilot AI review requested due to automatic review settings July 1, 2026 16:19
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@anilcancakir, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5945c548-06e7-454f-8c6b-c57f5a619f84

📥 Commits

Reviewing files that changed from the base of the PR and between 2304537 and cb477b7.

📒 Files selected for processing (2)
  • lib/src/widgets/w_div.dart
  • lib/src/widgets/wind_equal_height_row.dart
📝 Walkthrough

Walkthrough

Modifies WindEqualHeightRow's second layout pass to relay stretched cells with a minimum height constraint instead of a tight height constraint, preventing residual sub-pixel RenderFlex overflow. Adds a regression test, updates related documentation comments, tokens reference, changelog, and skill version metadata.

Changes

Equal-Height Row Min-Height Fix

Layer / File(s) Summary
Min-height relayout implementation
lib/src/widgets/wind_equal_height_row.dart
Pass 2 now clamps a target height, initializes rowHeight from it, and relays children with fixed width and minHeight: target instead of a tight height constraint, then raises rowHeight if a child grows beyond target.
Regression test
test/widgets/w_div/grid_stretch_test.dart
Adds a widget test for issue #141 confirming no overflow exception occurs and that stretched unequal flex-col cells end up with matching heights.
Documentation and metadata updates
lib/src/widgets/w_div.dart, doc/layout/grid.md, skills/wind-ui/references/tokens.md, CHANGELOG.md, skills/wind-ui/SKILL.md
Updates comments and docs to describe minimum-height relayout instead of tight relayout, references issue #141, and bumps skill version to 2.8.2.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

Sequence Diagram(s)

sequenceDiagram
  participant Grid as WDiv Grid
  participant Row as WindEqualHeightRow
  participant Cell as Cell Widget

  Grid->>Row: performLayout (Pass 1: loose measure)
  Row->>Cell: layout with loose height
  Cell-->>Row: measured height
  Row->>Row: compute target = clamp(max measured height)
  Row->>Cell: layout with minHeight=target (Pass 2)
  Cell-->>Row: final height (>= target)
  Row->>Row: rowHeight = max(target, child heights)
  Row-->>Grid: positioned cells, no overflow
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main fix: using min-height stretching to eliminate the 2px overflow.
Linked Issues check ✅ Passed The layout change and new test address #141 by re-laying stretched unequal cells with a minimum height and verifying no overflow.
Out of Scope Changes check ✅ Passed The changes stay focused on the overflow fix plus supporting docs, changelog, comments, and test coverage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/grid-stretch-2px-overflow

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
lib/src/widgets/wind_equal_height_row.dart (1)

122-139: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Earlier-processed cells aren't re-stretched to the final rowHeight.

If a later cell in the loop needs more than target (the sub-pixel case #141 targets), rowHeight is raised, but cells already laid out earlier in the same pass keep their smaller target-height layout. The row container ends up rowHeight tall, yet the earlier cell's own render box stays at target, leaving a visible gap under it — reintroducing the "ragged row" problem (#126) for exactly the scenario this fix addresses, just without an overflow exception. The code comment above ("a grown cell simply sets a marginally taller row") acknowledges this but doesn't correct it.

Consider a small normalization step: after the main loop, if rowHeight > target, re-layout any child whose size.height < rowHeight with minHeight: rowHeight so every cell in the row actually matches the final height.

♻️ Proposed fix: normalize cells to the final rowHeight
     double rowHeight = target;
     double dx = 0;
+    final List<RenderBox> laidOutChildren = [];
     child = firstChild;
     while (child != null) {
       child.layout(
         BoxConstraints(
           minWidth: cellWidth,
           maxWidth: cellWidth,
           minHeight: target,
           maxHeight: constraints.maxHeight,
         ),
         parentUsesSize: true,
       );
       if (child.size.height > rowHeight) rowHeight = child.size.height;
+      laidOutChildren.add(child);
       (child.parentData as _EqualHeightParentData).offset = Offset(dx, 0);
       dx += cellWidth + _spacing;
       child = childAfter(child);
     }
+
+    // Rare sub-pixel case: a later cell needed more than `target`. Re-lay any
+    // cell still short of the final rowHeight so the row stays visually equal.
+    if (rowHeight > target) {
+      for (final c in laidOutChildren) {
+        if (c.size.height < rowHeight) {
+          c.layout(
+            BoxConstraints(
+              minWidth: cellWidth,
+              maxWidth: cellWidth,
+              minHeight: rowHeight,
+              maxHeight: constraints.maxHeight,
+            ),
+            parentUsesSize: true,
+          );
+        }
+      }
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/src/widgets/wind_equal_height_row.dart` around lines 122 - 139, The row
layout in `EqualHeightRow` leaves earlier children at the initial `target`
height when a later child increases `rowHeight`, so the cells in the same row
can end up visually uneven. Update the layout logic in the loop that walks
`firstChild`/`childAfter` to perform a second normalization pass after
`rowHeight` is finalized: if `rowHeight` exceeds `target`, re-layout any child
whose height is still smaller using the final `rowHeight` so every child in the
row matches the container height.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@lib/src/widgets/wind_equal_height_row.dart`:
- Around line 122-139: The row layout in `EqualHeightRow` leaves earlier
children at the initial `target` height when a later child increases
`rowHeight`, so the cells in the same row can end up visually uneven. Update the
layout logic in the loop that walks `firstChild`/`childAfter` to perform a
second normalization pass after `rowHeight` is finalized: if `rowHeight` exceeds
`target`, re-layout any child whose height is still smaller using the final
`rowHeight` so every child in the row matches the container height.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d70d9eb-6b2a-4303-9fa1-80095977fa18

📥 Commits

Reviewing files that changed from the base of the PR and between 4fe3f80 and 2304537.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • doc/layout/grid.md
  • lib/src/widgets/w_div.dart
  • lib/src/widgets/wind_equal_height_row.dart
  • skills/wind-ui/SKILL.md
  • skills/wind-ui/references/tokens.md
  • test/widgets/w_div/grid_stretch_test.dart

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a residual RenderFlex overflowed by ~2px warning in grid ... items-stretch by changing WindEqualHeightRow to stretch children using a minimum height constraint rather than a tight height, preventing content from being forced below its own layout height (the #141 follow-up to #139).

Changes:

  • Update WindEqualHeightRow to re-layout cells with minHeight = target (not a tight height) and let the row grow to the tallest resulting child.
  • Add a regression test covering the #141 repro shape (asserts no exception + equal heights in the test environment).
  • Sync documentation surfaces (doc page, skill reference, skill version bump) and add a CHANGELOG entry describing the fix.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
lib/src/widgets/wind_equal_height_row.dart Changes equal-height row stretching logic to use min-height constraints to avoid residual overflows.
lib/src/widgets/w_div.dart Updates Dartdoc describing how items-stretch grid rows are implemented.
test/widgets/w_div/grid_stretch_test.dart Adds a widget test for the #141 regression shape (no exception + equal heights).
doc/layout/grid.md Updates user docs to describe the min-height (non-tight) stretch behavior and overflow avoidance.
CHANGELOG.md Adds a detailed Fixed entry for the #141 overflow warning regression.
skills/wind-ui/references/tokens.md Updates token reference text to reflect the new min-height stretch semantics.
skills/wind-ui/SKILL.md Bumps the skill version to 2.8.2 and updates the header comment accordingly.

Comment on lines +122 to 126
double rowHeight = target;
double dx = 0;
child = firstChild;
while (child != null) {
child.layout(
Comment thread lib/src/widgets/w_div.dart Outdated
Comment on lines 1199 to 1202
// WindEqualHeightRow measures each cell with a real (loose-height) layout
// and re-lays it tight to the row max, instead of IntrinsicHeight's
// and re-lays it to at least the row max (min height), instead of the
// intrinsic query IntrinsicHeight would run,
// intrinsic query. A `flex flex-col` cell (which carries a LayoutBuilder)
- Restore equal heights: after the min-height measure pass settles the true
  row height, a third pass re-lays every cell to that height (still via a MIN
  constraint, never tight), so a cell that measured shorter than a sibling
  which grew is brought up to match. All cells share the row height, and none
  is ever squeezed below its content (overflow-free).
- Fix the duplicated 'intrinsic query ... intrinsic query.' fragment in the
  _buildStretchGrid dartdoc.
@anilcancakir

Copy link
Copy Markdown
Collaborator Author

Both addressed:

  • Equal heights: added a settle pass — after the min-height measure pass determines the true row height, every cell is re-laid to that height (still a MIN constraint, never tight). So if any cell grows past the target, the shorter siblings are brought up to match; all cells share the row height and none is squeezed below its content (still overflow-free).
  • Dup comment: fixed the duplicated intrinsic query … intrinsic query. fragment in the _buildStretchGrid dartdoc.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd wind_equal_height_row.dart
echo "---"
cat -n lib/src/widgets/wind_equal_height_row.dart | sed -n '1,200p'
echo "---div---"
rg -n "_buildStretchGrid" -A 30 lib/src/widgets/w_div.dart | head -80

Length of output: 10670


@anilcancakir Thanks, I confirmed both fixes in lib/src/widgets/wind_equal_height_row.dart:

  • The new Pass 3 settle pass re-lays every cell with minHeight: rowHeight (the max height discovered in Pass 2) and positions them, so any cell that grew past the initial target correctly pulls its siblings up to match — equal heights preserved without ever squeezing a cell below its own content (still overflow-free since it's a min, not tight, constraint).
  • The duplicated "intrinsic query … intrinsic query." fragment in the _buildStretchGrid dartdoc is gone; the comment now reads cleanly.

This looks solid. Triggering a fresh review now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@anilcancakir anilcancakir merged commit 7dcb980 into master Jul 1, 2026
9 checks passed
@anilcancakir anilcancakir deleted the fix/grid-stretch-2px-overflow branch July 1, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Follow-up to #139: items-stretch emits a 2px bottom overflow on the stretched cell

2 participants