4.3 KiB
Issue #157435: Touch Scroll Not Propagating to Host Page (Embedded Mode)
Problem Description
When a Flutter web app is embedded in a host page using multi-view/custom element embedding (not iframe), touch scrolling inside the Flutter view does not propagate to the host page when the Flutter content reaches its scroll boundary.
User Experience Impact
- On mobile devices, users cannot scroll the host page by swiping on the Flutter embedded view
- Touch scrolling feels "stuck" at Flutter content boundaries
- Poor mobile UX for embedded Flutter content
Difference from Issue #156985
- Issue #156985 is about mouse wheel scrolling in iframe embedding
- Issue #157435 is about touch scrolling in custom element embedding
- Both require scroll propagation to parent, but through different mechanisms
Root Cause Analysis
-
touch-action: none: Flutter setstouch-action: noneon the host element to ensure Flutter receives all touch events for gesture recognition. -
preventDefault()on pointerdown: The engine was callingpreventDefault()on ALL pointerdown events, which prevents the browser from initiating native touch scrolling. -
No touch scroll propagation mechanism: Unlike wheel events, there was no way for Flutter to propagate touch scroll deltas to the host page when at boundary.
Solution
Engine Changes (pointer_binding.dart)
Don't preventDefault() for touch events on pointerdown:
// In pointerdown handler
if (event.pointerType != 'touch') {
event.preventDefault();
}
// Touch events: let browser handle scroll initiation
This allows the browser's touch scrolling to work, while Flutter still handles touch events first via touch-action: none.
Framework Changes (scroll_position_with_single_context.dart)
Added touch scroll propagation via platform channel:
static const BasicMessageChannel<Object?> _scrollChannel =
BasicMessageChannel<Object?>('flutter/scroll', JSONMessageCodec());
@override
void applyUserOffset(double delta) {
// Check if at boundary before applying scroll
final bool wasAtMin = pixels <= minScrollExtent;
final bool wasAtMax = pixels >= maxScrollExtent;
// Apply the scroll
setPixels(pixels - physics.applyPhysicsToUserOffset(this, delta));
// On web, propagate to parent when at boundary
if (kIsWeb) {
final bool shouldPropagateDown = delta < 0 && (wasAtMax || pixels >= maxScrollExtent);
final bool shouldPropagateUp = delta > 0 && (wasAtMin || pixels <= minScrollExtent);
if (shouldPropagateDown || shouldPropagateUp) {
_propagateOverscrollToParent(delta);
}
}
}
Engine Changes (platform_dispatcher.dart)
Added handler for flutter/scroll platform channel:
case 'flutter/scroll':
final dynamic decoded = messageCodec.decodeMessage(data);
if (decoded is Map) {
final double deltaX = (decoded['deltaX'] as num?)?.toDouble() ?? 0.0;
final double deltaY = (decoded['deltaY'] as num?)?.toDouble() ?? 0.0;
scrollParentWindow(deltaX, deltaY);
replyToPlatformMessage(callback, messageCodec.encodeMessage(true));
}
return;
CSS Changes (Embedding Strategies)
Added overscroll-behavior: contain to prevent any residual scroll chaining:
// In custom_element_embedding_strategy.dart & full_page_embedding_strategy.dart
setElementStyle(rootElement, 'overscroll-behavior', 'contain');
Files Changed
| File | Change |
|---|---|
engine/src/flutter/lib/web_ui/lib/src/engine/pointer_binding.dart |
Don't preventDefault on touch pointerdown |
packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart |
Touch scroll propagation via platform channel |
engine/src/flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart |
flutter/scroll channel handler |
engine/src/flutter/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/*.dart |
overscroll-behavior: contain CSS |
Demo
- Before Fix: https://issue-157435-before.web.app
- After Fix: https://issue-157435-after.web.app
Behavior After Fix
- ✅ Touch scrolling inside Flutter embedded view works normally
- ✅ When Flutter content is at boundary, continued swiping scrolls the host page
- ✅ Flutter gesture recognition still works (tap, drag, etc.)
- ✅ Works for both vertical and horizontal scrolling