Skip to content

[CP-stable] fixes a status bar tap crash#182691

Merged
auto-submit[bot] merged 4 commits intoflutter:flutter-3.41-candidate.0from
LongCatIsLooong:reland-179643-stable
Feb 24, 2026
Merged

[CP-stable] fixes a status bar tap crash#182691
auto-submit[bot] merged 4 commits intoflutter:flutter-3.41-candidate.0from
LongCatIsLooong:reland-179643-stable

Conversation

@LongCatIsLooong
Copy link
Contributor

@LongCatIsLooong LongCatIsLooong commented Feb 20, 2026

This pull request is manually created because there were merge conflicts.

Click here for the conflicts
diff --cc packages/flutter/test/widgets/interactive_viewer_test.dart
index e4ef3492f0d,63b1c353d72..00000000000
--- a/packages/flutter/test/widgets/interactive_viewer_test.dart
+++ b/packages/flutter/test/widgets/interactive_viewer_test.dart
@@@ -1044,25 -956,22 +1044,43 @@@ void main() 
          var calledStart = false;
          var calledUpdate = false;
          var calledEnd = false;
+         const sizedBox = SizedBox(width: 200.0, height: 200.0);
          await tester.pumpWidget(
++<<<<<<< HEAD
 +          MaterialApp(
 +            home: Scaffold(
 +              body: Center(
 +                child: InteractiveViewer(
 +                  transformationController: transformationController,
 +                  scaleEnabled: false,
 +                  onInteractionStart: (ScaleStartDetails details) {
 +                    calledStart = true;
 +                  },
 +                  onInteractionUpdate: (ScaleUpdateDetails details) {
 +                    calledUpdate = true;
 +                  },
 +                  onInteractionEnd: (ScaleEndDetails details) {
 +                    calledEnd = true;
 +                  },
 +                  child: const SizedBox(width: 200.0, height: 200.0),
 +                ),
 +              ),
++=======
+           Center(
+             child: InteractiveViewer(
+               transformationController: transformationController,
+               scaleEnabled: false,
+               onInteractionStart: (ScaleStartDetails details) {
+                 calledStart = true;
+               },
+               onInteractionUpdate: (ScaleUpdateDetails details) {
+                 calledUpdate = true;
+               },
+               onInteractionEnd: (ScaleEndDetails details) {
+                 calledEnd = true;
+               },
+               child: sizedBox,
++>>>>>>> 91b2d41a66d (Reland #179643, only scroll hit-testable primary scroll views on status bar tap (#182391))
              ),
            ),
          );
@@@ -1119,25 -1028,22 +1137,43 @@@
          var calledStart = false;
          var calledUpdate = false;
          var calledEnd = false;
+         const sizedBox = SizedBox(width: 200.0, height: 200.0);
          await tester.pumpWidget(
++<<<<<<< HEAD
 +          MaterialApp(
 +            home: Scaffold(
 +              body: Center(
 +                child: InteractiveViewer(
 +                  transformationController: transformationController,
 +                  scaleEnabled: false,
 +                  onInteractionStart: (ScaleStartDetails details) {
 +                    calledStart = true;
 +                  },
 +                  onInteractionUpdate: (ScaleUpdateDetails details) {
 +                    calledUpdate = true;
 +                  },
 +                  onInteractionEnd: (ScaleEndDetails details) {
 +                    calledEnd = true;
 +                  },
 +                  child: const SizedBox(width: 200.0, height: 200.0),
 +                ),
 +              ),
++=======
+           Center(
+             child: InteractiveViewer(
+               transformationController: transformationController,
+               scaleEnabled: false,
+               onInteractionStart: (ScaleStartDetails details) {
+                 calledStart = true;
+               },
+               onInteractionUpdate: (ScaleUpdateDetails details) {
+                 calledUpdate = true;
+               },
+               onInteractionEnd: (ScaleEndDetails details) {
+                 calledEnd = true;
+               },
+               child: sizedBox,
++>>>>>>> 91b2d41a66d (Reland #179643, only scroll hit-testable primary scroll views on status bar tap (#182391))
              ),
            ),
          );

Issue Link:

What is the link to the issue this cherry-pick is addressing?

#182233
The bug was introduced in #179643 which was cherry picked to 3.41 beta (so the crash should exist on both current beta and stable).

Impact Description:

On iOS, the app may crash when the user taps on the status bar to scroll to top, when the app has a primary scroll view that has never been laid out (for example, in a route that has been obstructed by other routes).

Changelog Description:

  • flutter/182233 - Tapping on the status bar may crash the app on iOS when there's a primary scroll view that has never been laid out.

Additionally this patch only dispatches the "scroll to top" command to the topmost Scaffold which is generally better UX for most apps (this doesn't have to be in the changlog)

Workaround:

No known easy workarounds.

Risk:

What is the risk level of this cherry-pick?

  • Low
  • Medium
  • High

Test Coverage:

Are you confident that your fix is well-tested by automated tests?

  • Yes
  • No

Validation Steps:

What are the steps to validate that this fix works?

I've manually tested the fix on iOS/iPadOS simulators. There are also unit tests.

…on status bar tap (flutter#182391)

Reland flutter#179643, with
[changes](flutter@3f7d345)
to fix flutter#182233. The reland
closes flutter#177992, closes
flutter#175606.

The crash happened because `Scaffold.handleStatusBar` (and subsequently
`ScrollController.animateTo`) is called on a scaffold that [has never
been laid
out](flutter#182233 (comment)).
Before flutter#179643, the iOS embedder sends a fake touch down pointer event
at `(0, 0)` to the framework, followed by a touch up pointer event at
the same location. That ensured only the foreground `Scaffold` could
claim the tap gesture and handle the status bar tap.

The fix tries to restore the previous behavior (where the iOS behavior
sends fake touches instead of platform messages) by performing a
hit-test at `(0, 0)` before trying to scroll the primary controller of a
`Scaffold` to the top, this should make sure that only the foreground
scaffold (in theory it's still possible for more than one Scaffold to
receive the hittest and handle the status bar tap event, but the
foreground scaffold should block the hit-test on background ones instead
of relying on gesture disambiguation).

- [ ] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [ ] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [ ] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

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](https://developers.google.com/gemini-code-assist/docs/review-github-code).
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.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
@flutter-dashboard
Copy link

This pull request was opened from and to a release candidate branch. This should only be done as part of the official Flutter release process. If you are attempting to make a regular contribution to the Flutter project, please close this PR and follow the instructions at Tree Hygiene for detailed instructions on contributing to Flutter.

Reviewers: Use caution before merging pull requests to release branches. Ensure the proper procedure has been followed.

@github-actions github-actions bot added a: tests "flutter test", flutter_test, or one of our tests framework flutter/packages/flutter repository. See also f: labels. f: material design flutter/packages/flutter/material repository. f: cupertino flutter/packages/flutter/cupertino repository labels Feb 20, 2026
Copy link
Contributor

@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 addresses a crash on iOS when tapping the status bar by ensuring only the topmost, hit-testable scaffold responds to the tap. This also improves the user experience for apps with multiple nested scaffolds. The implementation correctly uses a hit-testable widget in the status bar area to determine if a scaffold is in the foreground. The test changes are good, with a new simulateStatusBarTap helper and tests covering the new behavior. My main feedback is on code duplication: the _HitTestableAtOrigin helper class is duplicated in both material/scaffold.dart and cupertino/page_scaffold.dart. This should be refactored into a shared utility to improve maintainability.

Comment on lines +3483 to +3519
final class _HitTestableAtOrigin extends StatelessWidget {
const _HitTestableAtOrigin(this.globalKey);

final GlobalKey globalKey;

/// Whether the render box of the [_HitTestableAtOrigin] widget associated
/// with the given global `key` is hit-testable at [Offset.zero].
///
/// This is used by the `handleStatusBarTap` implementation to avoid sending
/// status bar tap events to scroll views in offscreen subtrees.
static bool hitTestableAtOrigin(GlobalKey key) {
final context = key.currentContext as Element?;
if (context == null) {
assert(
false,
'BuildContext associated with $key is not mounted. '
'If you see this in a test, this is likely because the test was trying '
'to simulate status bar tap on a non-iOS platform',
);
return false;
}
final renderObject = context.renderObject! as RenderMetaData;
final int viewId = View.of(context).viewId;
final result = HitTestResult();
WidgetsBinding.instance.hitTestInView(result, Offset.zero, viewId);
return result.path.any((HitTestEntry entry) => entry.target == renderObject);
}

@override
Widget build(BuildContext context) {
return MetaData(
key: globalKey,
behavior: HitTestBehavior.translucent,
child: const SizedBox.expand(),
);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The _HitTestableAtOrigin class is nearly identical to the one in packages/flutter/lib/src/cupertino/page_scaffold.dart. Duplicating code like this makes maintenance harder and violates the DRY (Don't Repeat Yourself) principle.

To improve maintainability, this class should be extracted to a shared location (e.g., a new file under lib/src/widgets/) and made public. The slightly different assert messages in the two versions can be merged into a single, more descriptive one.

References
  1. The style guide advises to 'Avoid duplicating state', which by extension applies to code logic as well to maintain a single source of truth and improve maintainability. (link)

@LongCatIsLooong LongCatIsLooong marked this pull request as draft February 20, 2026 23:19
LongCatIsLooong and others added 3 commits February 20, 2026 15:20
…9643)" (flutter#182223)

This reverts commit f4c83de.

See b/482565401.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

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](https://developers.google.com/gemini-code-assist/docs/review-github-code).
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.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
…on status bar tap (flutter#182391)

Reland flutter#179643, with
[changes](flutter@3f7d345)
to fix flutter#182233. The reland
closes flutter#177992, closes
flutter#175606.

The crash happened because `Scaffold.handleStatusBar` (and subsequently
`ScrollController.animateTo`) is called on a scaffold that [has never
been laid
out](flutter#182233 (comment)).
Before flutter#179643, the iOS embedder sends a fake touch down pointer event
at `(0, 0)` to the framework, followed by a touch up pointer event at
the same location. That ensured only the foreground `Scaffold` could
claim the tap gesture and handle the status bar tap.

The fix tries to restore the previous behavior (where the iOS behavior
sends fake touches instead of platform messages) by performing a
hit-test at `(0, 0)` before trying to scroll the primary controller of a
`Scaffold` to the top, this should make sure that only the foreground
scaffold (in theory it's still possible for more than one Scaffold to
receive the hittest and handle the status bar tap event, but the
foreground scaffold should block the hit-test on background ones instead
of relying on gesture disambiguation).

- [ ] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [ ] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [ ] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

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](https://developers.google.com/gemini-code-assist/docs/review-github-code).
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.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
@LongCatIsLooong LongCatIsLooong marked this pull request as ready for review February 22, 2026 05:29
Copy link
Contributor

@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

The pull request addresses a critical crash on iOS related to status bar taps when a primary scroll view has not been laid out. The fix involves adding a hit-testable widget at the status bar's position to ensure that the scroll-to-top event is only dispatched to the foregrounded Scaffold or CupertinoPageScaffold. This is a good approach to prevent unintended scrolling behavior and crashes. The changes also include updates to tests to reflect this new behavior and a new simulateStatusBarTap helper in flutter_test for easier testing.

Comment on lines +2757 to +2768
if (primaryScrollController != null &&
primaryScrollController.hasClients &&
// TODO(LongCatIsLooong): the iOS embedder used to send status bar tap
// evets as fake touches at Offset.zero, such that at most one Scaffold
// (usually the foreground primary Scaffold) can handle the status bar
// tap event, thanks to hit-testing and gesture disambiguation.
// To keep that behavior, this widget performs an additional hit-test here
// to make sure the status bar tap is only handled if the scaffold is
// hit-testable (thus in the foreground)
// Switch to a better solution when available:
// https://github.com/flutter/flutter/issues/182403
_HitTestableAtOrigin.hitTestableAtOrigin(_statusBarKey)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

Similar to the CupertinoPageScaffold changes, this crucial condition ensures that the handleStatusBarTap event is only processed if the Scaffold is the foregrounded and hit-testable one. This directly addresses the crash by preventing scroll events from being dispatched to offscreen scroll views.

Comment on lines +710 to +743
'status bar tap only scrolls the foregrounded primary controller',
(WidgetTester tester) async {
final app = MaterialApp(
initialRoute: 'a',
onGenerateInitialRoutes: (initialRoute) {
return [
MaterialPageRoute(builder: (context) => _ScaffoldWithPrimaryScrollView()),
MaterialPageRoute(builder: (context) => _ScaffoldWithPrimaryScrollView()),
];
},
onGenerateRoute: (_) => throw UnimplementedError(),
);
}
await tester.pumpWidget(app);

// Finally stops at the top.
expect(scrollable.position.pixels, equals(0.0));
});
final Iterable<ScrollableState> scrollables = tester.stateList<ScrollableState>(
find.descendant(
of: find.byType(_ScaffoldWithPrimaryScrollView, skipOffstage: false),
matching: find.byType(Scrollable, skipOffstage: false),
skipOffstage: false,
),
);

final [ScrollableState scrollable1, ScrollableState scrollable2] = scrollables.toList();
expect(scrollable1.position.pixels, 1000);
expect(scrollable2.position.pixels, 1000);

tester.simulateStatusBarTap();
await tester.pumpAndSettle();

expect(scrollable1.position.pixels, 1000);
expect(scrollable2.position.pixels, 0);
},
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
);
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

This new test case is crucial for validating that the status bar tap only affects the foregrounded primary scroll controller in Material Design scaffolds. This directly confirms the effectiveness of the _HitTestableAtOrigin mechanism in preventing unintended scrolling behavior across multiple scaffolds.

Comment on lines +560 to 592
testWidgets('status bar tap only scrolls the foregrounded primary controller', (
WidgetTester tester,
) async {
final app = CupertinoApp(
initialRoute: 'a',
onGenerateInitialRoutes: (initialRoute) {
return [
CupertinoPageRoute(builder: (context) => _ScaffoldWithPrimaryScrollView()),
CupertinoPageRoute(builder: (context) => _ScaffoldWithPrimaryScrollView()),
];
},
onGenerateRoute: (_) => throw UnimplementedError(),
);
tester.binding.defaultBinaryMessenger.handlePlatformMessage(
SystemChannels.statusBar.name,
message,
(ByteData? data) {},
await tester.pumpWidget(app);

final Iterable<ScrollableState> scrollables = tester.stateList<ScrollableState>(
find.descendant(
of: find.byType(_ScaffoldWithPrimaryScrollView, skipOffstage: false),
matching: find.byType(Scrollable, skipOffstage: false),
skipOffstage: false,
),
);

final [ScrollableState scrollable1, ScrollableState scrollable2] = scrollables.toList();
expect(scrollable1.position.pixels, 1000);
expect(scrollable2.position.pixels, 1000);

tester.simulateStatusBarTap();
await tester.pumpAndSettle();

expect(scrollController.offset, 0.0);
expect(scrollable1.position.pixels, 1000);
expect(scrollable2.position.pixels, 0);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

This new test case is critical for verifying the core fix: ensuring that only the foregrounded primary scroll controller responds to a status bar tap. This directly validates the logic implemented in _HitTestableAtOrigin and the handleStatusBarTap methods.

Comment on lines +117 to +128
if (primaryScrollController != null &&
primaryScrollController.hasClients &&
// TODO(LongCatIsLooong): the iOS embedder used to send status bar tap
// evets as fake touches at Offset.zero, such that at most one Scaffold
// (usually the foreground CupertinoPageScaffold) can handle the status
// bar tap event, thanks to hit-testing and gesture disambiguation.
// To keep that behavior, this widget performs an additional hit-test here
// to make sure the status bar tap is only handled if this scaffold is
// hit-testable (thus in the foreground).
// Switch to a better solution when available:
// https://github.com/flutter/flutter/issues/182403
_HitTestableAtOrigin.hitTestableAtOrigin(_statusBarKey)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The added condition _HitTestableAtOrigin.hitTestableAtOrigin(_statusBarKey) is crucial for preventing crashes by ensuring that the status bar tap event is only handled if the CupertinoPageScaffold is in the foreground and hit-testable. The TODO comment clearly explains the rationale behind this temporary solution, which is good for future improvements.

Comment on lines 7 to 9
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart' show SystemChannels;
import 'package:flutter/src/services/message_codecs.dart';
import 'package:flutter_test/flutter_test.dart';

Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The removal of SystemChannels and JSONMethodCodec imports is appropriate because the new simulateStatusBarTap() helper method in flutter_test now encapsulates this platform channel interaction.

Comment on lines +1047 to +1062
const sizedBox = SizedBox(width: 200.0, height: 200.0);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: InteractiveViewer(
transformationController: transformationController,
scaleEnabled: false,
onInteractionStart: (ScaleStartDetails details) {
calledStart = true;
},
onInteractionUpdate: (ScaleUpdateDetails details) {
calledUpdate = true;
},
onInteractionEnd: (ScaleEndDetails details) {
calledEnd = true;
},
child: const SizedBox(width: 200.0, height: 200.0),
),
),
Center(
child: InteractiveViewer(
transformationController: transformationController,
scaleEnabled: false,
onInteractionStart: (ScaleStartDetails details) {
calledStart = true;
},
onInteractionUpdate: (ScaleUpdateDetails details) {
calledUpdate = true;
},
onInteractionEnd: (ScaleEndDetails details) {
calledEnd = true;
},
child: sizedBox,
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The removal of MaterialApp and Scaffold wrappers in this test case is appropriate because the InteractiveViewer's interaction callbacks should function independently of these higher-level widgets. This simplifies the test and focuses on the InteractiveViewer's behavior.

);

final Offset childOffset = tester.getTopLeft(find.byType(SizedBox));
final Offset childOffset = tester.getTopLeft(find.byWidget(sizedBox));
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Changing find.byType(SizedBox) to find.byWidget(sizedBox) is a more robust way to find the widget, especially when there might be multiple SizedBox widgets in the tree. This ensures the test targets the correct instance.

Comment on lines 1120 to 1135
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: InteractiveViewer(
transformationController: transformationController,
scaleEnabled: false,
onInteractionStart: (ScaleStartDetails details) {
calledStart = true;
},
onInteractionUpdate: (ScaleUpdateDetails details) {
calledUpdate = true;
},
onInteractionEnd: (ScaleEndDetails details) {
calledEnd = true;
},
child: const SizedBox(width: 200.0, height: 200.0),
),
),
Center(
child: InteractiveViewer(
transformationController: transformationController,
scaleEnabled: false,
onInteractionStart: (ScaleStartDetails details) {
calledStart = true;
},
onInteractionUpdate: (ScaleUpdateDetails details) {
calledUpdate = true;
},
onInteractionEnd: (ScaleEndDetails details) {
calledEnd = true;
},
child: sizedBox,
),
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Similar to the previous test, removing MaterialApp and Scaffold simplifies the test setup, focusing solely on the InteractiveViewer's interaction callbacks.

);

final Offset childOffset = tester.getTopLeft(find.byType(SizedBox));
final Offset childOffset = tester.getTopLeft(find.byWidget(sizedBox));
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Changing find.byType(SizedBox) to find.byWidget(sizedBox) improves the specificity and robustness of the widget finder in this test.

Comment on lines +1034 to +1046
/// Sends a 'handleScrollToTop' message to the test application via the
/// [SystemChannels.statusBar] channel, to simulate an iOS status bar tap
/// event.
void simulateStatusBarTap() {
final ByteData message = const JSONMethodCodec().encodeMethodCall(
const MethodCall('handleScrollToTop'),
);
binding.defaultBinaryMessenger.handlePlatformMessage(
SystemChannels.statusBar.name,
message,
(ByteData? data) {},
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The new simulateStatusBarTap() method is a valuable addition to WidgetController. It encapsulates the platform-specific details of sending a status bar tap event, making tests more readable and less prone to errors. This directly supports the new hit-testing logic in Scaffold and CupertinoPageScaffold.

@vashworth
Copy link
Contributor

The changes are all on the framework side, so reassigning to @chunhtai for review

@vashworth vashworth requested a review from chunhtai February 23, 2026 22:42
Copy link
Contributor

@chunhtai chunhtai left a comment

Choose a reason for hiding this comment

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

RSLGTM

@reidbaker reidbaker requested a review from chunhtai February 24, 2026 15:40
@reidbaker
Copy link
Contributor

@chunhtai I need more than a rubber stamp approval since this pr had conflicts and had to be manually modified. Please review the conflicts and add your approval or comments.

Copy link
Contributor

@chunhtai chunhtai left a comment

Choose a reason for hiding this comment

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

still LGTM, the merge conflict is on the test file, and the code looks fine.

@reidbaker reidbaker added the autosubmit Merge PR when tree becomes green via auto submit App label Feb 24, 2026
@auto-submit auto-submit bot merged commit 449e274 into flutter:flutter-3.41-candidate.0 Feb 24, 2026
141 checks passed
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 2, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 3, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 3, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: tests "flutter test", flutter_test, or one of our tests autosubmit Merge PR when tree becomes green via auto submit App f: cupertino flutter/packages/flutter/cupertino repository f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants