# 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 1. **`touch-action: none`**: Flutter sets `touch-action: none` on the host element to ensure Flutter receives all touch events for gesture recognition. 2. **`preventDefault()` on pointerdown**: The engine was calling `preventDefault()` on ALL pointerdown events, which prevents the browser from initiating native touch scrolling. 3. **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**: ```dart // 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: ```dart static const BasicMessageChannel _scrollChannel = BasicMessageChannel('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: ```dart 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: ```dart // 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 1. ✅ Touch scrolling inside Flutter embedded view works normally 2. ✅ When Flutter content is at boundary, continued swiping scrolls the host page 3. ✅ Flutter gesture recognition still works (tap, drag, etc.) 4. ✅ Works for both vertical and horizontal scrolling