Repository navigation
Duplicate Multi-touch Events in Android Platform Views #176574
Copy link
Copy link
Closed
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work lista: platform-viewsEmbedding Android/iOS views in Flutter appsEmbedding Android/iOS views in Flutter appsf: gesturesflutter/packages/flutter/gestures repository.flutter/packages/flutter/gestures repository.found in release: 3.35Found to occur in 3.35Found to occur in 3.35found in release: 3.37Found to occur in 3.37Found to occur in 3.37frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onplatform-androidAndroid applications specificallyAndroid applications specificallyr: fixedIssue is closed as already fixed in a newer versionIssue is closed as already fixed in a newer versionteam-androidOwned by Android platform teamOwned by Android platform teamtriaged-androidTriaged by Android platform teamTriaged by Android platform team
Description
Steps to reproduce
- Create a Flutter app with an
AndroidViewinsideGestureDetectorwith long press callbacks: - In the native Android view, log all
MotionEventactions with timestamps and pointer counts - Perform a pinch/scale gesture
Expected results
I/flutter (28829): Flutter: Long press CANCEL
D/NativeView(28829): Action: ACTION_DOWN, Pointers: 1, Time: 580981037
D/NativeView(28829): Action: ACTION_POINTER_DOWN(1), Pointers: 2, Time: 580981037
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981198
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981235
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981251
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981268
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981285
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981301
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981315
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981335
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981351
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981368
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981385
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981401
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981418
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981432
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981468
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981485
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981501
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981518
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981535
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981551
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981557
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981585
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981601
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981618
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981632
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981678
D/NativeView(28829): Action: ACTION_POINTER_UP(0), Pointers: 2, Time: 580981786Actual results
I/flutter (28829): Flutter: Long press CANCEL
D/NativeView(28829): Action: ACTION_DOWN, Pointers: 1, Time: 580981037
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981198
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981235
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981251
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981268
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981285
D/NativeView(28829): Action: ACTION_POINTER_DOWN(1), Pointers: 2, Time: 580981037
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981198
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981235
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981251
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981268
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981285
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981301
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981315
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981335
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981351
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981368
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981385
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981401
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981418
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981432
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981468
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981485
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981501
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981518
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981535
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981551
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981557
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981585
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981601
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981618
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981632
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981678
D/NativeView(28829): Action: ACTION_POINTER_UP(0), Pointers: 2, Time: 580981786Code sample
Code sample
GestureDetector(
behavior: HitTestBehavior.translucent,
onLongPressStart: (dt) => _sendLongPressEvent("Long press START"),
onLongPressCancel: () => _sendLongPressEvent("Long press CANCEL"),
onLongPressMoveUpdate: (details) => _sendLongPressEvent("Long press UPDATE"),
onLongPressEnd: (details) => _sendLongPressEvent("Long press END"),
child: AndroidView(
viewType: 'native-view',
layoutDirection: TextDirection.ltr,
),
)Code sample
Sample project
Logs
Logs
I/flutter (28829): Flutter: Long press CANCEL
D/NativeView(28829): Action: ACTION_DOWN, Pointers: 1, Time: 580981037
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981198
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981235
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981251
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981268
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981285
D/NativeView(28829): Action: ACTION_POINTER_DOWN(1), Pointers: 2, Time: 580981037
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981198
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981235
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981251
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981268
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981285
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981301
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981315
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981335
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981351
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981368
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981385
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981401
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981418
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981432
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981468
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981485
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981501
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981518
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981535
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981551
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981557
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981585
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981601
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981618
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981632
D/NativeView(28829): Action: ACTION_MOVE, Pointers: 2, Time: 580981678
D/NativeView(28829): Action: ACTION_POINTER_UP(0), Pointers: 2, Time: 580981786Flutter Doctor output
Doctor output
[✓] Flutter (Channel stable, 3.35.5, on macOS 26.0.1 25A362 darwin-arm64, locale en-US) [204ms]
• Flutter version 3.35.5 on channel stable at /Users/romanlaitarenko/Developer/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision ac4e799d23 (10 days ago), 2025-09-26 12:05:09 -0700
• Engine revision d3d45dcf25
• 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-swift-package-manager,
enable-lldb-debugging
[!] Android toolchain - develop for Android devices (Android SDK version 36.0.0) [759ms]
• Android SDK at /Users/romanlaitarenko/Library/Android/sdk
• Emulator version 35.5.10.0 (build_id 13402964) (CL:N/A)
• Platform android-36, build-tools 36.0.0
• ANDROID_HOME = /Users/romanlaitarenko/Library/Android/sdk
• Java binary at: /Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home/bin/java
This JDK is specified in your Flutter configuration.
To change the current JDK, run: `flutter config --jdk-dir="path/to/jdk"`.
• Java version OpenJDK Runtime Environment Temurin-17.0.16+8 (build 17.0.16+8)
! Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses
[✓] Xcode - develop for iOS and macOS (Xcode 26.0.1) [454ms]
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 17A400
• CocoaPods version 1.16.2
[✗] Chrome - develop for the web (Cannot find Chrome executable at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome) [8ms]
! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.
[✓] Android Studio (version 2024.3) [8ms]
• 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.6+-13368085-b895.109)
[✓] Connected device (4 available) [5.9s]
• sdk gphone16k arm64 (mobile) • emulator-5554 • android-arm64 • Android 15 (API 35) (emulator)
• Roman’s iPhone (2) (wireless) (mobile) • 00008110-00110D1614F9801E • ios • iOS 26.0.1 23A355
• iPhone 16 Pro (mobile) • CCF1D21D-4E0C-423A-870B-005E7F4F2EBD • ios • com.apple.CoreSimulator.SimRuntime.iOS-18-6 (simulator)
• macOS (desktop) • macos • darwin-arm64 • macOS 26.0.1 25A362 darwin-arm64
[✓] Network resources [213ms]
• All expected network resources are available.
! Doctor found issues in 2 categories.Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work lista: platform-viewsEmbedding Android/iOS views in Flutter appsEmbedding Android/iOS views in Flutter appsf: gesturesflutter/packages/flutter/gestures repository.flutter/packages/flutter/gestures repository.found in release: 3.35Found to occur in 3.35Found to occur in 3.35found in release: 3.37Found to occur in 3.37Found to occur in 3.37frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onplatform-androidAndroid applications specificallyAndroid applications specificallyr: fixedIssue is closed as already fixed in a newer versionIssue is closed as already fixed in a newer versionteam-androidOwned by Android platform teamOwned by Android platform teamtriaged-androidTriaged by Android platform teamTriaged by Android platform team
Activity
tirth-patel-nc commentedon Oct 7, 2025
@evil159 thanks for the report. Please confirm whether #96640 is related to your case or For us to further investigate this issue, please provide a completed and minimal reproducible code sample that doesn’t include 3rd party plugins or complex production code. Thank you!
evil159 commentedon Oct 7, 2025
Hi @tirth-patel-nc, thank you for looking into it. I don't believe this issue is related to #96640, this issue is present only on Android and I believe is related to Android-specific motion event replay after gesture arena contest. I have attached a minimal reproducible sample project to the description of the ticket. Please let me know if I can help in any way.
tirth-patel-nc commentedon Oct 8, 2025
Thanks for the report. I'm able to reproduce the same behavior as shown in the actual results with the latest SDK versions using provided code sample and steps. I'm not very familiar with PlatformViews and their gesture handling, so I'll leave this to the team for further investigation.
Logs
evil159 commentedon Oct 8, 2025
@tirth-patel-nc Thank you for confirming this! One thing to note is that I believe this has started happening in Flutter SDK
3.19.jason-simmons commentedon Nov 1, 2025
The embedder's
AndroidTouchProcessorconverts the original Android touch event into multiple pointer data messages that are sent to the framework.The framework will handle the messages and route them to the platform view widget. The framework's
_AndroidMotionEventConverterthen needs to map the messages back to Android-style events that can be sent to the embedder and forwarded to the Android native view.The app is seeing duplicates because
_AndroidMotionEventConverteris not recognizing that some messages should be merged into one Android event._AndroidMotionEventConverter.toAndroidMotionEventdetermines whether a message is part of a batch by comparing the message's pointer index against the_AndroidMotionEventConverter's internalpointerPositionsmap of known pointers. If the index is not the last expected index based on the size ofpointerPositions, then the_AndroidMotionEventConverterskips that message so that only one event is sent for each batch.However,
_AndroidMotionEventConverter.pointerPositionscan be out of sync with the state of the Android embedder.If the
AndroidTouchProcessorreceives an Android event with two pointers, then it will send a message for pointer index 0 and another message for pointer index 1.But if
_AndroidMotionEventConverterhas not yet seen a message that references pointer index 1, thenpointerPositionswill only contain one pointer._AndroidMotionEventConverter.toAndroidMotionEventwill think that pointer index 0 is the last message of the batch, and it will send the message to the embedder.Later, when
_AndroidMotionEventConvertersees the message about pointer index 1 that was part of the same Android event, it will add that pointer topointerPositions._AndroidMotionEventConverter.toAndroidMotionEventwill now think that pointer index 1 is the last message of the batch, and it will also send that message to the embedder.The embedder thus receives two messages that originated from the same Android event. The embedder's
MotionEventTrackerwill map both messages back to the sameMotionEvent, and the Android native view will receive that event twice.One option for fixing this would be adding the number of pointers in the original Android touch event to the message sent to the framework. That would make it unnecessary for the
_AndroidMotionEventConverterto infer it from its internal state.The message already has a
pointerDatafield thatAndroidTouchProcessoruses to send a batching flag in other scenarios. For this class of events,pointerDatacould encode the Android event's pointer count.[Android] Encode the original pointer count in messages that represen…
[Android] Encode the original pointer count in messages that represen…
[Android] Encode the original pointer count in messages that represen…
[Android] Encode the original pointer count in messages that represen…
github-actions commentedon Nov 25, 2025
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.[Android] Encode the original pointer count in messages that represen…
[Android] Encode the original pointer count in messages that represen…
[Android] Encode the original pointer count in messages that represen…