Skip to content

[Bug]: BottomSheetFlatList gestures stop working after switching tabs when enableFreeze(true) is enabled #2695

Description

@lxr-mwhite

Version

v5

Reanimated Version

v3

Gesture Handler Version

v2

Platforms

iOS, Android

What happened?

When enableFreeze(true) from react-native-screens is enabled, BottomSheetFlatList gestures stop behaving correctly after navigating away from the tab containing the bottom sheet and then returning to it. The tabs are rendered using @react-navigation/bottom-tabs.

The bottom sheet initially works as expected when the app first launches. I can drag the sheet up and down by starting the gesture on the BottomSheetFlatList area.

However, after switching to another tab and then returning to the tab with the bottom sheet, gestures that start on the BottomSheetFlatList no longer drag the bottom sheet. The sheet can only be moved by dragging the handle.

If I drag the sheet to the highest snap point using the handle, the flat list becomes scrollable. But when I drag the sheet back down to a lower snap point, the list area stops responding correctly again and cannot be used to drag the sheet.

This only happens when enableFreeze(true) is enabled. Without enableFreeze(true), the bottom sheet and BottomSheetFlatList gestures work as expected after switching tabs.

bug.mov

Reproduction steps

  1. Create a React Native app with React Navigation native stack, React Navigation bottom tabs, react-native-screens, react-native-gesture-handler, react-native-reanimated, and @gorhom/bottom-sheet.
  2. Call enableFreeze(true) from react-native-screens.
  3. Render a native stack navigator with a bottom tab navigator
  4. Inside the bottom tab navigator, render two tab screens.
  5. In the first tab screen, render a BottomSheet with a BottomSheetFlatList.
  6. Launch the app.
  7. On initial launch, drag the bottom sheet up and down by starting the gesture on the BottomSheetFlatList area. This works as expected.
  8. Switch to the second tab.
  9. Switch back to the first tab.
  10. Try to drag the bottom sheet up or down again by starting the gesture on the BottomSheetFlatList area.
  11. Notice that the bottom sheet no longer moves from gestures started on the list area. It can only be moved using the handle.
  12. Drag the bottom sheet to the highest snap point using the handle.
  13. Notice that the BottomSheetFlatList can now scroll.
  14. Drag the bottom sheet back down to a lower snap point.
  15. Notice that the list area stops responding correctly again and cannot be used to drag the bottom sheet.

Example:

import React from 'react';
import { Button, StyleSheet, Text, View } from 'react-native';
import { enableFreeze } from 'react-native-screens';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import BottomSheet, { BottomSheetFlatList } from '@gorhom/bottom-sheet';

enableFreeze(true);

type RootStackParamList = {
  Tabs: undefined;
  Modal: undefined;
};

type TabParamList = {
  TabOne: undefined;
  TabTwo: undefined;
};

const Stack = createNativeStackNavigator<RootStackParamList>();
const Tab = createBottomTabNavigator<TabParamList>();

function TabOneScreen({ navigation }) {
  const data = React.useMemo(() => Array.from({ length: 100 }, (_, index) => `Item ${index + 1}`), []);

  const snapPoints = React.useMemo(() => [30, 450, 620], []);

  return (
    <View style={styles.screen1}>
      <Button title="Open Modal" onPress={() => navigation.getParent()?.navigate('Modal')} />
      <BottomSheet index={1} snapPoints={snapPoints} enableDynamicSizing={false}>
        <BottomSheetFlatList
          data={data}
          keyExtractor={(item) => item}
          contentContainerStyle={styles.listContent}
          renderItem={({ item }) => (
            <View style={styles.listItem}>
              <Text>{item}</Text>
            </View>
          )}
        />
      </BottomSheet>
    </View>
  );
}

function TabTwoScreen({ navigation }) {
  return (
    <View style={styles.screen2}>
      <Text>Tab Two Screen</Text>
      <Button title="Open Modal" onPress={() => navigation.getParent()?.navigate('Modal')} />
    </View>
  );
}

function TabsScreen() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="TabOne" component={TabOneScreen} />
      <Tab.Screen name="TabTwo" component={TabTwoScreen} />
    </Tab.Navigator>
  );
}

function ModalScreen({ navigation }) {
  return (
    <View style={styles.screen2}>
      <Text>Modal Screen</Text>
      <Button title="Close" onPress={() => navigation.goBack()} />
    </View>
  );
}

const App = () => {
  return (
    <GestureHandlerRootView style={styles.root}>
      <NavigationContainer>
        <Stack.Navigator>
          <Stack.Screen name="Tabs" component={TabsScreen} options={{ headerShown: false }} />
          <Stack.Screen
            name="Modal"
            component={ModalScreen}
            options={{
              presentation: 'modal',
              title: 'Modal'
            }}
          />
        </Stack.Navigator>
      </NavigationContainer>
    </GestureHandlerRootView>
  );
};

export default App;

const styles = StyleSheet.create({
  root: {
    flex: 1
  },
  screen1: {
    flex: 1,
    backgroundColor: '#f9f'
  },
  screen2: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    gap: 12
  },
  listContent: {
    paddingHorizontal: 16,
    paddingBottom: 24
  },
  listItem: {
    paddingVertical: 16,
    borderBottomWidth: 1,
    borderBottomColor: '#ddd'
  }
});

Reproduction sample

https://snack.expo.dev/@gorhom/bottom-sheet---issue-reproduction-template

Relevant log output

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions