Skip to content

[two_dimensional_scrollables] Fix pinned cells hit test#11777

Open
beroso wants to merge 8 commits into
flutter:mainfrom
beroso:fix/186876-pinned-cells-hit-test
Open

[two_dimensional_scrollables] Fix pinned cells hit test#11777
beroso wants to merge 8 commits into
flutter:mainfrom
beroso:fix/186876-pinned-cells-hit-test

Conversation

@beroso
Copy link
Copy Markdown
Contributor

@beroso beroso commented May 25, 2026

This fixes a bug where TableView trailing pinned cells lose hit tests to underlying cells.
That glitch makes one of the main use cases unworkable (action columns at the right edge of the data grid).

Fix flutter/flutter#186876

Pre-Review Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

Footnotes

  1. Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. 2

@google-cla
Copy link
Copy Markdown

google-cla Bot commented May 25, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@github-actions github-actions Bot added p: two_dimensional_scrollables Issues pertaining to the two_dimensional_scrollables package triage-framework Should be looked at in framework triage labels May 25, 2026
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request modifies RenderTableViewport to ensure hit testing correctly accounts for pinned areas by intersecting cell rectangles with section-specific clip rectangles. It introduces a _sectionClipRectFor method to calculate these bounds and adds regression tests to verify hit-test precedence for pinned cells. Review feedback suggests optimizing hit testing by using dual contains checks to avoid unnecessary object allocations and caching viewportDimension in _sectionClipRectFor to improve readability and performance.

Comment on lines +587 to +590
final Rect clippedCellRect = cellRect.intersect(
_sectionClipRectFor(cellParentData.tableVicinity),
);
if (clippedCellRect.contains(position)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

To avoid unnecessary object allocation during hit testing, you can replace the intersect call with a combined contains check. Rect.intersect creates a new Rect object, which is not needed here since you only need to verify if the position is within both rectangles. This is a common optimization in hit testing paths to reduce pressure on the garbage collector.

      if (cellRect.contains(position) &&
          _sectionClipRectFor(cellParentData.tableVicinity).contains(position)) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done!

// a cell belongs to (leading-pinned, non-pinned, or trailing-pinned for each
// axis). Intersecting the raw cell rect with this rect in hitTestChildren
// prevents scrollable cells from capturing taps inside pinned areas.
Rect _sectionClipRectFor(TableVicinity vicinity) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

In _sectionClipRectFor, viewportDimension is accessed many times across different branches. Consider caching it in a local variable (e.g., final Size viewportSize = viewportDimension;) at the start of the method to improve readability and avoid redundant property accesses.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done!

@beroso beroso force-pushed the fix/186876-pinned-cells-hit-test branch from f3a106c to 782e898 Compare May 25, 2026 14:02
@beroso beroso force-pushed the fix/186876-pinned-cells-hit-test branch from b77e6e5 to ee38453 Compare May 25, 2026 14:32
@stuartmorgan-g stuartmorgan-g requested a review from Piinks May 26, 2026 18:28
@Piinks Piinks added the CICD Run CI/CD label Jun 4, 2026
Copy link
Copy Markdown
Contributor

@Piinks Piinks left a comment

Choose a reason for hiding this comment

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

Hey @beroso thanks so much for the fix!
It looks like there may be an edge case this does not account for. The implementation of _sectionClipRectFor calculates clip boundaries without factoring in the viewport alignment offsets (_hAlignmentOffset and _vAlignmentOffset).

When the table is smaller than the viewport and configured with a custom alignment (e.g., alignment: Alignment.center), cells and clip rects are shifted by the alignment offsets. For hit testing, cells are shifted (via their paintOffset), but _sectionClipRectFor returns an un-shifted clip rect. This means hit testing on pinned cells in aligned tables fails, as the tap position falls outside of the un-shifted clip rect.

I wrote a regression test to include and verify:

Details

Add the new regression test verifying hit-testing on aligned pinned columns:

    testWidgets('Tapping on a pinned column in an aligned table (with _hAlignmentOffset) registers correctly', (
      WidgetTester tester,
    ) async {
      TableVicinity? lastTapped;

      await tester.pumpWidget(
        MaterialApp(
          home: Scaffold(
            body: SizedBox(
              height: 400,
              width: 400,
              child: TableView.builder(
                cacheExtent: 0.0,
                columnCount: 3,
                rowCount: 1,
                pinnedColumnCount: 1,
                alignment: Alignment.center,
                columnBuilder: (_) => const TableSpan(extent: FixedTableSpanExtent(100)),
                rowBuilder: (_) => const TableSpan(extent: FixedTableSpanExtent(100)),
                cellBuilder: (_, TableVicinity vicinity) {
                  return TableViewCell(
                    child: GestureDetector(
                      behavior: HitTestBehavior.opaque,
                      onTap: () => lastTapped = vicinity,
                      child: const SizedBox.expand(),
                    ),
                  );
                },
              ),
            ),
          ),
        ),
      );

      // Pinned column 0 is at x = 50..150 due to Alignment.center. Tap at (100, 200).
      await tester.tapAt(const Offset(100, 200));
      await tester.pumpAndSettle();

      expect(
        lastTapped,
        TableVicinity.zero,
        reason: 'Tapping at x=100 (inside aligned pinned column 0) should register a tap on column 0.',
      );
    });

@github-actions github-actions Bot removed the CICD Run CI/CD label Jun 4, 2026
@beroso
Copy link
Copy Markdown
Contributor Author

beroso commented Jun 4, 2026

Hi @Piinks thanks for the feedback!
Landed a fix about the viewport alignment offsets and add some regression tests, including yours.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

p: two_dimensional_scrollables Issues pertaining to the two_dimensional_scrollables package triage-framework Should be looked at in framework triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[two_dimensional_scrollables] TableView trailing pinned columns lose hit tests to underlying cells

2 participants