Skip to content

OverlayPortal overlay content allows click-through to underlying widgets when ensureSemantics() is enabled #182493

Description

@JankoLancer

When SemanticsBinding.instance.ensureSemantics() is active (which is the default behavior when a screen reader is active or on many Web configurations), hit-testing on content rendered via OverlayPortal behaves incorrectly. Pointer events seem to "pierce" or pass through the overlay content and trigger widgets situated physically behind the overlay.

This issue does not occur if ensureSemantics() is not called (i.e., when the semantics tree is not actively built).

Steps to reproduce

  • Run the code sample on a platform where semantics can be enabled (e.g., Chrome/Web, or macOS/Windows with ensureSemantics() call).
  • Click the text "Press to show/hide tooltip" to open the yellow overlay.
  • Observe that the yellow overlay ("Tooltip") appears covering part of the screen.
  • Click strictly on the yellow overlay area (ensure you are clicking the overlay, not the text behind it).

Expected results

The click should land on the yellow overlay (consuming the event or doing nothing). It should not trigger the TextButton underneath.

Actual results

The click passes through the yellow overlay and triggers the TextButton's onPressed callback (toggling the overlay again or executing the button's action).

Code sample

Code sample
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

/// Flutter code sample for [OverlayPortal].
void main() => runApp(const OverlayPortalExampleApp());

class OverlayPortalExampleApp extends StatelessWidget {
  const OverlayPortalExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('OverlayPortal Example')),
        body: const Center(child: ClickableTooltipWidget()),
      ),
    );
  }
}

class ClickableTooltipWidget extends StatefulWidget {
  const ClickableTooltipWidget({super.key});

  @override
  State<StatefulWidget> createState() => ClickableTooltipWidgetState();
}

class ClickableTooltipWidgetState extends State<ClickableTooltipWidget> {
  final OverlayPortalController _tooltipController = OverlayPortalController();
  
  @override
  void initState() {
     // Triggering semantics causes the hit-test issue
     SemanticsBinding.instance.ensureSemantics();
     super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        TextButton(
          onPressed: _tooltipController.toggle,
          child: DefaultTextStyle(
            style: DefaultTextStyle.of(context).style.copyWith(fontSize: 50),
            child: OverlayPortal(
              controller: _tooltipController,
              overlayChildBuilder: (BuildContext context) {
                return Positioned(
                  left: 100,
                  top: 60,
                  // This box appears over the UI, but clicks pass through it 
                  // to the button behind it when semantics are enabled.
                  child: ColoredBox(
                    color: Colors.amberAccent,
                    child: const SizedBox(
                      width: 200, 
                      height: 100, 
                      child: Center(child: Text('Tooltip (Click me)')),
                    ),
                  ),
                );
              },
              child: const Text('Press to show/hide tooltip'),
            ),
          ),
        ),
      ],
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration
With ensureSemantics (Bug) Without ensureSemantics (Correct)
Screen.Recording.2026-02-17.at.15.48.23.mov
Screen.Recording.2026-02-17.at.15.49.05.mov

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.35.4, on macOS 26.1 25B78 darwin-arm64, locale en-US) [710ms]
    • Flutter version 3.35.4 on channel stable at /Users/djanko/fvm/versions/3.35.4
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision d693b4b9db (5 months ago), 2025-09-16 14:27:41 +0000
    • Engine revision c298091351
    • Dart version 3.9.2
    • DevTools version 2.48.0
    • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android,
      enable-ios, cli-animations, enable-lldb-debugging

[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0) [3.0s]
    • Android SDK at /Users/djanko/Library/Android/sdk
    • Emulator version 36.1.9.0 (build_id 13823996) (CL:N/A)
    • Platform android-36, build-tools 35.0.0
    • ANDROID_HOME = /Users/djanko/Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
      This is the JDK bundled with the latest Android Studio installation on this machine.
      To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment (build 21.0.8+-14196175-b1038.72)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 26.1.1) [1,734ms]
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 17B100
    • CocoaPods version 1.16.2

[✓] Chrome - develop for the web [7ms]
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2025.2) [6ms]
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 21.0.8+-14196175-b1038.72)

[✓] VS Code (version 1.108.2) [5ms]
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.128.0

[✓] Connected device (2 available) [7.6s]
    • macOS (desktop) • macos  • darwin-arm64   • macOS 26.1 25B78 darwin-arm64
    • Chrome (web)    • chrome • web-javascript • Google Chrome 144.0.7559.134

[✓] Network resources [315ms]
    • All expected network resources are available.

• No issues found!

Metadata

Metadata

Assignees

Labels

P1High-priority issues at the top of the work listframeworkflutter/packages/flutter repository. See also f: labels.platform-webWeb applications specificallyteam-webOwned by Web platform teamtriaged-webTriaged by Web platform team

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions