Repository navigation
Description
Steps to reproduce
- Run the attached minimal reproduction (main.dart) on a REAL physical device
- Tap once anywhere on the webview to reveal the button (starts a 5-second auto-hide countdown).
- Press and hold the button continuously so the 5-second countdown lapses WHILE your finger is still down (hold 6-10+ seconds).
- Release.
- Repeat 10-20+ times, mixing short taps and long holds.
- Watch the on-screen counters (visible directly in the app, no debug console needed): "btnDown", "btnUp", "btnCancel".
This is intermittent, not guaranteed every time — that's expected. What confirms the bug: over the course of 15-20+ repetitions, "btnDown" ends up permanently higher than "btnUp" + "btnCancel" combined, meaning at least one press was never followed by either a release or a cancel event.
Expected results
Every onPointerDown on the button is eventually followed by a matching onPointerUp (if released normally) or onPointerCancel (if the touch is interrupted) — regardless of what animation is running or what's rendered underneath.
Actual results
Intermittently, a btnDown is never followed by either event. On-screen counters (see code sample) show btnDown permanently ahead of btnUp + btnCancel combined, with no recovery, in the majority of test sessions with all four ingredients present.
Code sample
Code sample
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
void main() => runApp(const ReproApp());
class ReproApp extends StatelessWidget {
const ReproApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: ReproPage());
}
}
class ReproPage extends StatefulWidget {
const ReproPage({super.key});
@override
State<ReproPage> createState() => _ReproPageState();
}
class _ReproPageState extends State<ReproPage> {
Duration? _downAt;
int _globalDownCount = 0;
int _buttonDownCount = 0;
int _buttonUpCount = 0;
int _buttonCancelCount = 0;
String _lastEvent = '(none yet)';
int _rebuildTicks = 0;
Timer? _tickTimer;
int _webviewGeneration = 0;
Timer? _swapTimer;
// --- Auto-hide: reveal on tap, hide again 5s later regardless of an
// in-flight press — only reset by a NEW pointer-down, never refreshed
// continuously while held. ---
static const _autoHideDuration = Duration(seconds: 5);
bool _controlsVisible = false;
Timer? _hideTimer;
void _scheduleAutoHide() {
_hideTimer?.cancel();
_hideTimer = Timer(_autoHideDuration, () {
if (mounted) setState(() => _controlsVisible = false);
});
}
void _revealControls() {
if (!_controlsVisible) {
setState(() => _controlsVisible = true);
}
_scheduleAutoHide();
}
@override
void initState() {
super.initState();
// Mimics an app periodically rebuilding for unrelated reasons.
_tickTimer = Timer.periodic(const Duration(seconds: 1), (_) {
if (mounted) setState(() => _rebuildTicks++);
});
// Mimics a platform view being destroyed/recreated periodically
// (e.g. content changing underneath, unrelated to user interaction).
_swapTimer = Timer.periodic(const Duration(seconds: 20), (_) {
if (mounted) setState(() => _webviewGeneration++);
});
}
@override
void dispose() {
_tickTimer?.cancel();
_swapTimer?.cancel();
_hideTimer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Listener(
behavior: HitTestBehavior.translucent,
onPointerDown: (event) {
_revealControls();
setState(() {
_globalDownCount++;
_lastEvent = 'GLOBAL down at ${event.position}';
});
},
child: InAppWebView(
key: ValueKey(_webviewGeneration),
initialUrlRequest: URLRequest(
url: WebUri('https://flutter.dev'),
),
initialSettings: InAppWebViewSettings(
disableContextMenu: true,
allowsLinkPreview: false,
),
),
),
Positioned(
right: 24,
top: 0,
bottom: 0,
child: Center(
// Three ingredients together: BackdropFilter, IgnorePointer
// actually changing hit-testability, AND an active
// AnimatedOpacity transition, over a platform view.
// NOTE: no InkWell/Material here at all - this is enough
// on its own to reproduce the loss.
child: IgnorePointer(
ignoring: !_controlsVisible,
child: AnimatedOpacity(
opacity: _controlsVisible ? 1 : 0,
duration: const Duration(milliseconds: 200),
child: Listener(
behavior: HitTestBehavior.translucent,
onPointerDown: (event) {
_downAt = event.timeStamp;
setState(() {
_buttonDownCount++;
_lastEvent = 'BUTTON down';
});
},
onPointerUp: (event) {
final heldMs =
(event.timeStamp - _downAt!).inMilliseconds;
setState(() {
_buttonUpCount++;
_lastEvent = 'BUTTON up (heldMs=$heldMs)';
});
},
onPointerCancel: (event) {
final heldMs =
(event.timeStamp - _downAt!).inMilliseconds;
setState(() {
_buttonCancelCount++;
_lastEvent = 'BUTTON cancel (heldMs=$heldMs)';
});
},
child: SizedBox(
width: 64,
height: 64,
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 1, sigmaY: 1),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Colors.black.withValues(alpha: 0.2),
),
child: const Center(
child: Icon(
Icons.chevron_right_rounded,
color: Colors.white,
size: 36,
),
),
),
),
),
),
),
),
),
),
),
// On-screen counters — verify purely by looking at the device,
// no terminal/log access required.
Positioned(
left: 0,
right: 0,
top: 0,
child: IgnorePointer(
child: Container(
color: Colors.black87,
padding: const EdgeInsets.all(12),
child: Text(
'globalDown=$_globalDownCount '
'btnDown=$_buttonDownCount '
'btnUp=$_buttonUpCount '
'btnCancel=$_buttonCancelCount '
'ticks=$_rebuildTicks '
'webviewGen=$_webviewGeneration\n'
'visible=$_controlsVisible '
'last: $_lastEvent',
style: const TextStyle(color: Colors.yellow, fontSize: 16),
),
),
),
),
],
),
);
}Screenshots or Video
Screenshots / Video demonstration
Actual result:RPReplay_Final1786951196.MP4
Logs
Logs
[Paste your logs here]Flutter Doctor output
Doctor output
Flutter 3.35.6 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 9f455d2486 (2025-10-08 14:55:31 -0500)
Engine • hash a5f2c36e367c13f868cfe98db5806f562c52c35e (revision d2913632a4)
Tools • Dart 3.9.2 • DevTools 2.48.0Also reproduced separately on Flutter 3.41.4 / Dart 3.11.1 (production app).
Reproduced on: physical iPad running iOS 16.7.11. flutter_inappwebview: ^6.1.5 (flutter_inappwebview_ios: 1.1.2).
Activity
mbcorona commentedon Aug 17, 2026
HI @E2-Athish , thanks for reporting.
The repro depends on flutter_inappwebview, a third party package.
Could you reproduce with a first-party platform view (e.g. webview_flutter, or a minimal UiKitView with no third-party plugin) and on the latest stable (3.47.0)? If it still drops the events there, that points at Flutter, and we'll route it properly.
E2-Athish commentedon Aug 18, 2026
Update: reproduces with webview_flutter on Flutter 3.47.0 stable
Hi @mbcorona, Confirmed. This reproduces the same way using
webview_flutterinstead offlutter_inappwebview, on the latest stable release (3.47.0).Code sample (webview_flutter version)
Code sample
mbcorona commentedon Aug 19, 2026
Hi @E2-Athish, thanks for the repro.
Reproduced the lost onPointerUp/onPointerCancel on a physical iPhone. This looks like the same iOS platform-view touch loss as #191267, which already isolates the cause to the engine's ForwardingGestureRecognizer
Routing to the iOS team.
hellohuanlin commentedon Aug 20, 2026
@E2-Athish Does the problem exist for regular flutter widget (e.g. a box) rather than a BackdropFilter?
E2-Athish commentedon Aug 31, 2026
@hellohuanlin Tested. No, doesn't reproduce with a regular widget. Swapped
BackdropFilterfor a plainContainer(same size/shape/color), kept everything else identical, stays clean across repeated testing.4 remaining items
flutter-triage-bot commentedon Sep 3, 2026
This issue is missing a priority label. Please set a priority label when adding the
triaged-ioslabel.okorohelijah commentedon Sep 10, 2026
from triage: cc @hellohuanlin
[ios]disable user interaction for visual effect view (flutter#192599)
github-actions commentedon Nov 5, 2026
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.