JaRoMaster
JaRoMaster

Reputation: 181

Scrollview only scrollable inside parent view react-native

I've got a ScrollView nested in an absolute positioned View that is bigger then it's parent. The scrolling works fine if I press inside that parent, but when I go outside it, it's not handling the touches. How can I get around this?

enter image description here
I can't scroll if I touch the screen below the green line.

Code to reproduce:

<View style={{flex: 1, justifyContent: "center", alignItems: "center"}}>
  <View style={{width: "80%", height: 150, borderWidth: 1}}>
    <View style={{position: 'absolute', height: 400, width: "80%", backgroundColor: "green", alignSelf: "center"}}>
    <ScrollView>
    <View style={{height: 200, backgroundColor: "red"}} />
    <View style={{height: 200, backgroundColor: "blue"}} />
    <View style={{height: 200, backgroundColor: "yellow"}} />
    </ScrollView>
    </View>
  </View>
</View>

Expo Snack: https://snack.expo.dev/uV88qpP7f (Happens on android)

Upvotes: 1

Views: 1720

Answers (5)

Furkan KARABABA
Furkan KARABABA

Reputation: 27

Absolute position style causes this problem. I just remove it and set new style above.

export default function App() {
  return (
    <View style={{flex: 1, justifyContent: "center", alignItems: "center"}}>
      <View style={{width: "80%", height: 150, borderWidth: 1, backgroundColor: "green"}}>
        <ScrollView>
        <View style={{height: 70, backgroundColor: "red"}} />
        <View style={{height: 70, backgroundColor: "blue"}} />
        <View style={{height: 70, backgroundColor: "yellow"}} />
        </ScrollView>
      </View>
    </View>
  );
}

Upvotes: -3

Muhammad Numan
Muhammad Numan

Reputation: 25363

Demo

https://snack.expo.dev/bsHCCB1_l

Try declaring your Box above the scroll view otherwise the touch area below Box won't work

ScrollView with Position absolute should be below to Box to make touch in work

Code

import { View,ScrollView } from 'react-native';

const Box=()=>{
  return <View style={{ height: 150, borderWidth: 1}} />
}
const DropDown=()=>{
  return (
    <View style={{position: 'absolute', height: 400, width: "80%", backgroundColor: "green", alignSelf: "center"}}>
      <ScrollView>
      <View style={{height: 200, backgroundColor: "red"}} />
      <View style={{height: 200, backgroundColor: "blue"}} />
      <View style={{height: 200, backgroundColor: "yellow"}} />
      </ScrollView>
     </View>
  )
}

export default function App() {
  return (
    <View style={{flex: 1, justifyContent: "center", alignItems: "center"}}>
      <View style={{width: "80%"}}>

        <Box />

        <DropDown /> 

      </View>
    </View>
  );
}

Upvotes: 1

Xhirazi
Xhirazi

Reputation: 857

Issue is with your approach to design. You can achieve the desire functionality with same design by following code.

<View style={{flex: 1, justifyContent: "center", alignItems: "center"}}>
  <View style={{width: "80%"}}>
    <View style={{ height: 150, borderWidth: 1}}/>
    <View style={{position: 'absolute', height: 400, width: "80%", backgroundColor: "green", alignSelf: "center"}}>
    <ScrollView>
    <View style={{height: 200, backgroundColor: "red"}} />
    <View style={{height: 200, backgroundColor: "blue"}} />
    <View style={{height: 200, backgroundColor: "yellow"}} />
    </ScrollView>
    </View>
  </View>
</View>

Upvotes: 1

Cory Cunningham
Cory Cunningham

Reputation: 42

Best way I found was to move the ScrollView outside of the card container and get the top position of the ScrollView by calculating the area of the button and container it's supposed to be relating to. Sort of how tooltips often function.

import { useState } from 'react';
import {
  View,
  StyleSheet,
  Pressable,
  Text,
  ScrollView,
  FlatList,
} from 'react-native';
import Constants from 'expo-constants';
import { Feather } from '@expo/vector-icons';

const data = [
  {
    color: 'red',
  },
  {
    color: 'blue',
  },
  {
    color: 'orange',
  },
  {
    color: 'green',
  },
  {
    color: 'pink',
  },
];

export default function App() {
  const [value, setValue] = useState('Press me!');
  const [heightOfButton, setHeightOfButton] = useState(0);
  const [heightOfContainer, setHeightOfContainer] = useState(0);
  const [shouldShow, setShouldShow] = useState(true);
  // android handles the padding a bit strange but I feel like this is close to what you're looking for.
  const scrollPosition = heightOfContainer + 12 + heightOfButton

  return (
    <View style={styles.container}>
      <View
        onLayout={(e) =>
          setHeightOfContainer(e.nativeEvent.layout.y)
        }
        style={styles.card}>
        <Pressable
          onLayout={(e) =>
            setHeightOfButton(e.nativeEvent.layout.height)
          }
          onPress={() => setShouldShow(true)}
          style={styles.button}>
          <Text>{value}</Text>
          <Feather name="chevron-down" size={24} />
        </Pressable>
      </View>
      {shouldShow ? (
        <FlatList
          style={{
            position: 'absolute',
            top: scrollPosition,
            height: 400,
            width: '80%',
            backgroundColor: 'green',
            alignSelf: 'center',
          }}
          data={data}
          renderItem={({ item }) => (
            <Pressable
              onPress={() => {
                setValue(item.color);
                setShouldShow(false);
              }}
              style={{ height: 200, backgroundColor: item.color }}
            />
          )}
        />
      ) : null}
      {/*<ScrollView 
          style={{ 
            position: 'absolute',
            bottom: height,
            height: 400, 
            width: "80%", 
            backgroundColor: "green", 
            alignSelf: "center"
          }}>
        <View style={{height: 200, backgroundColor: "red"}} />
        <View style={{height: 200, backgroundColor: "blue"}} />
        <View style={{height: 200, backgroundColor: "yellow"}} />
        </ScrollView> */}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: 'lightblue',
    padding: 8,
  },
  card: {
    backgroundColor: 'white',
    height: '30%',
    borderRadius: 8,
    padding: 12,
  },
  button: {
    width: '100%',
    borderColor: 'black',
    borderRadius: 8,
    borderWidth: 1,
    padding: 12,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
  },
});

I moved your example to a FlatList as it seems like that was what you were hoping to do in the example.

Here's the snack https://snack.expo.dev/@corywritescode/frowning-carrot

Upvotes: 0

Farbod Shabani
Farbod Shabani

Reputation: 421

You can do this

import * as React from 'react';
import { Text, View, StyleSheet, ScrollView } from 'react-native';
import Constants from 'expo-constants';

// You can import from local files
import AssetExample from './components/AssetExample';

// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';

export default function App() {
const customHeight = 400; //change this based on your needs


return (
<View style={{flex: 1, justifyContent: "center", alignItems: "center"}}>
  <View style={{width: "80%", height: 150, borderWidth: 1}}>
    <View style={{position: 'absolute', height: customHeight, width: "80%", backgroundColor: "green", alignSelf: "center"}}>
    <ScrollView style={{backgroundColor: "grey", height: 600}}>
    <View style={{height: 200, backgroundColor: "red"}} />
    <View style={{height: 200, backgroundColor: "blue"}} />
    <View style={{height: 200, backgroundColor: "yellow"}} />
    </ScrollView>
    </View>
  </View>
</View>
);
}

Upvotes: -1

Related Questions