Skip to content

[Android] Stop nested scroll when the root starts intercepting - #4592

Merged
m-bert merged 2 commits into
mainfrom
@mbert/intercept-sweep-stop-nested-scroll
Oct 14, 2026
Merged

m-bert merged 2 commits into
mainfrom
@mbert/intercept-sweep-stop-nested-scroll

Conversation

@m-bert

@m-bert m-bert commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Description

When a gesture activates during a pull-to-refresh on an RN FlatList with an RN RefreshControl, the spinner parks half-pulled and onRefresh fires only on the next touch. Reported with ReanimatedSwipeable rows in #4563.

RN drives the pull through nested scrolling and SwipeRefreshLayout finishes it only in onStopNestedScroll, which Android triggers from View.dispatchTouchEvent by calling stopNestedScroll() after the final UP or CANCEL. When the root view starts intercepting, cancelTouchesInInterceptedViews feeds ACTION_CANCEL straight to onTouchEvent, so that cleanup never runs. Same omission #4492 fixed on the NativeViewGestureHandler path.

The sweep now calls stopNestedScroll() after onTouchEvent, as View.dispatchTouchEvent does. It is a no-op without an open nested scroll. An interrupted pull now snaps back, or fires onRefresh if it was past the threshold.

Test plan

Repro
import { useCallback, useState } from 'react';
import { FlatList, RefreshControl, StyleSheet, Text, View } from 'react-native';
import { GestureDetector, GestureHandlerRootView, usePanGesture } from 'react-native-gesture-handler';

const DATA = Array.from({ length: 30 }, (_, i) => `Row ${i}`);

function Row({ item }: { item: string }) {
  const pan = usePanGesture({ activeOffsetX: [-10, 10] });
  return (
    <GestureDetector gesture={pan}>
      <View style={styles.row}>
        <Text>{item}</Text>
      </View>
    </GestureDetector>
  );
}

export default function App() {
  const [refreshing, setRefreshing] = useState(false);
  const onRefresh = useCallback(() => {
    setRefreshing(true);
    setTimeout(() => setRefreshing(false), 1000);
  }, []);

  return (
    <GestureHandlerRootView style={styles.container}>
      <FlatList
        data={DATA}
        keyExtractor={(item) => item}
        refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
        renderItem={({ item }) => <Row item={item} />}
      />
    </GestureHandlerRootView>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  row: { padding: 20, backgroundColor: 'white', borderBottomWidth: 1, borderColor: '#eee' },
});
Details

Copilot AI balanced review requested due to automatic review settings October 13, 2026 10:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7d5862d4-7334-4ce0-87f0-e6226152232c
📥 Commits

Reviewing files that changed from the base of the PR and between 2989d4a and 0e80dd4.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c58bd0ba-3f88-4bc9-840e-0f2f9d589519
📥 Commits

Reviewing files that changed from the base of the PR and between 53da095 and 2989d4a.

📒 Files selected for processing (1)
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.

📝 Summary

Summary by CodeRabbit

  • Bug Fixes
    • Nested scrolling now stops when an intercepted view receives a touch cancellation, except for views that continue handling native gestures.

The Android gesture handler now stops nested scrolling on eligible intercepted views after sending them ACTION_CANCEL.

Changes: Android touch cancellation

Layer / File(s) Summary
Stop nested scrolling after cancellation
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt
After sending ACTION_CANCEL, the method calls stopNestedScroll() on each intercepted view, except for the wrapper view and views driven by native gestures that survive arbitration.

Suggested reviewers: j-piasecki

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 2989d

The change adds cleanup for interrupted Android nested scrolling, and the supplied evidence identifies no remaining concrete merge risk.

Pre-merge checks: Passed 3
Passed Passed checks (3 passed)
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title clearly and concisely describes the main Android change: stopping nested scrolling when the root view starts intercepting touches.
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@m-bert
m-bert merged commit 6c2a8df into main Oct 14, 2026
5 checks passed
@m-bert
m-bert deleted the @mbert/intercept-sweep-stop-nested-scroll branch October 14, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants