[web] Adjust parent scroll handling
diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart
index bc602f7..4704efe 100644
--- a/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart
+++ b/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart
@@ -118,6 +118,8 @@
   external DomNavigator get navigator;
   external DomVisualViewport? get visualViewport;
   external DomPerformance get performance;
+  @JS('scrollBy')
+  external void scrollBy([num? x, num? y]);
 
   /// The parent window of this window.
   /// Returns null if this is the top-level window, or the same window
@@ -2646,18 +2648,26 @@
   external double get y;
 }
 
-/// Scrolls the parent/host window by the given delta using postMessage.
+/// Scrolls the parent/host window by the given delta.
 ///
-/// Used when Flutter is embedded in an iframe and needs to scroll the parent
-/// page. This uses postMessage for cross-origin safety - the host page must
-/// add a message listener to handle the scroll request.
+/// When same-origin, scrolls the parent directly via `scrollBy`. If access is
+/// blocked (cross-origin), falls back to postMessage so a host listener can
+/// handle it.
 ///
-/// This fixes GitHub issue #156985 (scroll bubbling) and #157435 (touch scroll).
+/// This addresses scroll bubbling (#156985) and touch scroll handoff (#157435)
+/// without requiring host changes in same-origin embeds.
 void scrollParentWindow(double deltaX, double deltaY) {
   try {
     final DomWindow? parent = domWindow.parent;
     if (parent != null && !identical(parent, domWindow)) {
-      // Use postMessage for cross-origin safety
+      // Try direct scroll (same-origin or allowed access).
+      try {
+        parent.scrollBy(deltaX, deltaY);
+        return;
+      } catch (_) {
+        // Fall back to postMessage for cross-origin.
+      }
+
       final JSAny message = <String, dynamic>{
         'type': 'flutter-scroll',
         'deltaX': deltaX,
diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart
index f11e509..356c419 100644
--- a/engine/src/flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart
+++ b/engine/src/flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart
@@ -641,6 +641,22 @@
         // further effect after this point.
         _defaultRouteName = '/';
         return;
+
+      case 'flutter/scroll':
+        // Handle scroll propagation to parent/host window.
+        // Used for nested scrolling when Flutter is embedded in a host page.
+        // Fixes GitHub issue #157435 (touch scroll not propagating to host page).
+        const codec = StandardMessageCodec();
+        final dynamic decoded = codec.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, codec.encodeMessage(true));
+        } else {
+          replyToPlatformMessage(callback, codec.encodeMessage(false));
+        }
+        return;
     }
 
     if (pluginMessageCallHandler != null) {
diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/pointer_binding.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/pointer_binding.dart
index 1486248..24bc870 100644
--- a/engine/src/flutter/lib/web_ui/lib/src/engine/pointer_binding.dart
+++ b/engine/src/flutter/lib/web_ui/lib/src/engine/pointer_binding.dart
@@ -1074,7 +1074,16 @@
         // rendered the next input element, leading to the focus incorrectly returning to
         // the main Flutter view instead.
         // A zero-length timer is sufficient in all tested browsers to achieve this.
-        event.preventDefault();
+        //
+        // DON'T prevent default for touch events when embedded in an iframe.
+        // This allows browser's native touch scrolling to work, which provides
+        // smooth momentum scrolling and enables scroll propagation to parent page.
+        // See: https://github.com/flutter/flutter/issues/157435
+        final isTouch = event.pointerType == 'touch';
+        final bool isInIframe = _isEmbeddedInIframe();
+        if (!isTouch || !isInIframe) {
+          event.preventDefault();
+        }
         Timer(Duration.zero, () {
           EnginePlatformDispatcher.instance.requestViewFocusChange(
             viewId: _view.viewId,
diff --git a/packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart b/packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart
index 96d77f6..317aacf 100644
--- a/packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart
+++ b/packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart
@@ -11,18 +11,27 @@
 
 import 'dart:math' as math;
 
+import 'package:flutter/foundation.dart';
 import 'package:flutter/gestures.dart';
 import 'package:flutter/physics.dart';
 import 'package:flutter/rendering.dart';
+import 'package:flutter/services.dart';
 
 import 'basic.dart';
-import 'framework.dart';
 import 'scroll_activity.dart';
 import 'scroll_context.dart';
 import 'scroll_notification.dart';
 import 'scroll_physics.dart';
 import 'scroll_position.dart';
 
+/// Platform channel for scroll propagation to parent/host window on web.
+/// Used for nested scrolling when Flutter is embedded in a host page.
+/// Fixes GitHub issue #157435 (touch scroll not propagating to host page).
+const BasicMessageChannel<Object?> _scrollChannel = BasicMessageChannel<Object?>(
+  'flutter/scroll',
+  StandardMessageCodec(),
+);
+
 /// A scroll position that manages scroll activities for a single
 /// [ScrollContext].
 ///
@@ -128,7 +137,48 @@
   @override
   void applyUserOffset(double delta) {
     updateUserScrollDirection(delta > 0.0 ? ScrollDirection.forward : ScrollDirection.reverse);
+
+    // Check if we're at boundary BEFORE applying the scroll.
+    // This is needed to detect overscroll for parent page propagation.
+    final bool wasAtMin = pixels <= minScrollExtent;
+    final bool wasAtMax = pixels >= maxScrollExtent;
+
     setPixels(pixels - physics.applyPhysicsToUserOffset(this, delta));
+
+    // On web, propagate scroll to parent when at boundary.
+    // This enables touch scroll propagation to the host page when Flutter
+    // is embedded in an iframe.
+    // See: https://github.com/flutter/flutter/issues/157435
+    if (kIsWeb) {
+      // Scrolling down (negative delta = finger moving up = content moving up)
+      final bool shouldPropagateDown = delta < 0 && (wasAtMax || pixels >= maxScrollExtent);
+      // Scrolling up (positive delta = finger moving down = content moving down)
+      final bool shouldPropagateUp = delta > 0 && (wasAtMin || pixels <= minScrollExtent);
+
+      if (shouldPropagateDown || shouldPropagateUp) {
+        _propagateOverscrollToParent(delta);
+      }
+    }
+  }
+
+  /// Sends overscroll delta to the engine to scroll the parent/host window.
+  void _propagateOverscrollToParent(double overscroll) {
+    // Convert overscroll to x/y delta based on axis direction
+    var deltaX = 0.0;
+    var deltaY = 0.0;
+    switch (axisDirection) {
+      case AxisDirection.up:
+        deltaY = overscroll;
+      case AxisDirection.down:
+        deltaY = -overscroll;
+      case AxisDirection.left:
+        deltaX = overscroll;
+      case AxisDirection.right:
+        deltaX = -overscroll;
+    }
+
+    // Send to engine via platform channel (fire-and-forget)
+    _scrollChannel.send(<String, dynamic>{'deltaX': deltaX, 'deltaY': deltaY});
   }
 
   @override