0

I'm trying to deactivate a bottom sheet by swiping it down using useState, but that state is in another component, that is the screen that contains that bottom sheet. I

Image of the screen with bottom sheet activated

Code of bottom sheet component:

import React, { useEffect } from "react";
import { View, StyleSheet, Dimensions } from "react-native";
import AppText from "./AppText";
import { GestureDetector, Gesture } from "react-native-gesture-handler";
import Animated, {
  useAnimatedStyle,
  useSharedValue,
  withSpring,
} from "react-native-reanimated";
import defaultStyles from "../config/styles";

const windowHeight = Dimensions.get("window").height;

const BottomSheet = ({ children, title }) => {
  const translateY = useSharedValue(0);
  const context = useSharedValue({ y: 0 });
  const gesture = Gesture.Pan()
    .onStart(() => {
      context.value = { y: translateY.value };
    })
    .onUpdate((event) => {
      translateY.value = event.translationY + context.value.y;
      translateY.value = Math.max(translateY.value, -windowHeight / 3);
    })
    .onEnd(() => {
      if (translateY.value > -windowHeight / 4) {
        translateY.value = withSpring(0, {
          damping: 15,
        });
      } else if (translateY.value < -windowHeight / 3.5) {
        translateY.value = withSpring(-windowHeight / 3, { damping: 15 });
      }
    });

  useEffect(() => {
    translateY.value = withSpring(-windowHeight / 3, { damping: 15 });
  }, []);
  const rBottomSheetStyle = useAnimatedStyle(() => {
    return { transform: [{ translateY: translateY.value }] };
  });
  return (
    <GestureDetector gesture={gesture}>
      <Animated.View style={[styles.bottomSheetContainer, rBottomSheetStyle]}>
        <View style={styles.line} />
        <AppText style={styles.title}>{title}</AppText>
        {children}
      </Animated.View>
    </GestureDetector>
  );
};

const styles = StyleSheet.create({
  bottomSheetContainer: {
    padding: 15,
    height: windowHeight,
    width: "100%",
    backgroundColor: defaultStyles.colors.white,
    position: "absolute",
    top: windowHeight,
    borderRadius: 25,
    elevation: 5,
  },
  title: {
    fontSize: 18,
    marginBottom: 5,
    fontWeight: "bold",
    textAlign: "center",
  },
  line: {
    width: 75,
    height: 3,
    backgroundColor: defaultStyles.colors.black,
    alignSelf: "center",
    marginVertical: 2,
    marginBottom: 10,
    borderRadius: 2,
  },
});

export default BottomSheet;

Code of component where I use the bottom sheet:

import React, { useState } from "react";
import { View, StyleSheet, Image, ScrollView, Dimensions } from "react-native";
import AppText from "../components/AppText";
import { ListDetails } from "../components/Lists";
import IconButton from "../components/Buttons/IconButton";
import BottomSheet from "../components/BottomSheet";
import OptionSelector from "../components/OptionSelector";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import defaultStyles from "../config/styles";

const windowHeight = Dimensions.get("window").height;
const cetaceans = [
  {
    id: 1,
    name: "Atlantic spotted Dolphin",
    details: {
      nomeCientífico: "Stenella frontalis",
      idade: "1",
      comprimento: "3m",
      peso: "650kg",
      localização: "Camâra de Lobos",
    },
    imageUrl: require("../assets/dolphins/Atlantic_spotted_dolphin.jpg"),
    introduction:
      "Ocorrem na Madeira durante todo o ano. Muito activas e lúdicas na superfície. Muitas vezes aproximam-se curiosamente de barcos e saltam, fazem proa e enfiam a cabeça fora de água. A população desta espécie na Madeira é constituída por dois ecótipos; o maior, do tipo offshore pelágico e o menor, do tipo costeiro, com esta última comunidade mesmo contendo grupos residentes.",
    history:
      "Os golfinhos roaz-corvineiro comuns recebem o seu nome do seu focinho curto e grosso (ou rostro). São geralmente de cor cinzenta. Podem variar entre cinzento claro e quase preto no topo perto da barbatana dorsal e cinzento claro até quase branco na barriga.",
    migration:
      "Os golfinhos roazes dos Estados Unidos migram para cima e para baixo na costa atlântica, dirigindo-se para norte na Primavera, e novamente para sul no Outono.",
  },
  
];

const index = 1;

const notifications = [
  { id: 1, title: "Quando estiver perto da minha localização" },
  { id: 2, title: "Quando estiver perto de um local personalizado" },
];

const CetaceanProfileScreen = (props) => {
  const [isFavorite, setIsFavorite] = useState(false);
  const [isBottomSheetActive, setBottomSheetActive] = useState(false);
  const [notificationActive, setNotificationActive] = useState(0);

  const handleFavoritePress = () => {
    setIsFavorite(!isFavorite);
  };

  const selectFavoriteIcon = () => {
    return isFavorite ? ["favorite", "red"] : ["favorite-outline", "black"];
  };

  const handleNotificationPress = () => {
    setBottomSheetActive(!isBottomSheetActive);
  };

  const selectNotificationIcon = () => {
    return !isBottomSheetActive ? "notifications-none" : "notifications";
  };
  const handleNotificationOptionPress = (id) => {
    setNotificationActive(id);
  };
  const selectNotificationOptionIcon = (id) => {
    return notificationActive == id
      ? ["check-circle", defaultStyles.colors.white]
      : ["add-circle-outline", defaultStyles.colors.black];
  };

  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <View style={styles.container}>
        <View style={styles.imageContainer}>
          <Image style={styles.image} source={cetaceans[index].imageUrl} />
        </View>

        <View style={styles.profileContainer}>
          <ScrollView showsVerticalScrollIndicator={false}>
            <View style={styles.header}>
              <View style={{ flex: 1 }}>
                <AppText numberOfLines={3} style={styles.cetaceanName}>
                  {cetaceans[index].name}
                </AppText>
              </View>
              <View style={styles.headerIcons}>
                <IconButton
                  onPress={handleFavoritePress}
                  name={selectFavoriteIcon()[0]}
                  color={selectFavoriteIcon()[1]}
                  size={32}
                />
                <IconButton
                  onPress={handleNotificationPress}
                  name={selectNotificationIcon()}
                  color={defaultStyles.colors.black}
                  size={32}
                />
              </View>
            </View>
            <AppText style={styles.title}>Detalhes</AppText>
            <ScrollView
              horizontal={true}
              showsHorizontalScrollIndicator={false}
            >
              <ListDetails details={cetaceans[index].details} />
            </ScrollView>
            <AppText style={styles.title}>Introdução</AppText>
            <AppText style={styles.text}>
              {cetaceans[index].introduction}.
            </AppText>
            <AppText style={styles.title}>História</AppText>
            <AppText style={styles.text}>{cetaceans[index].history}</AppText>
            <AppText style={styles.title}>Rota de migração</AppText>
            <AppText style={styles.text}>{cetaceans[index].migration}</AppText>
          </ScrollView>
        </View>
        {isBottomSheetActive ? (
          <>
            <View style={styles.transparentContainer}></View>
            <BottomSheet title="Notificações">
              {notifications.map((item, index) => (
                <OptionSelector
                  key={index}
                  id={item.id}
                  title={item.title}
                  optionActive={notificationActive}
                  onPress={() => handleNotificationOptionPress(item.id)}
                  name={selectNotificationOptionIcon(item.id)[0]}
                  color={selectNotificationOptionIcon(item.id)[1]}
                />
              ))}
            </BottomSheet>
          </>
        ) : (
          ""
        )}
      </View>
    </GestureHandlerRootView>
  );
};

const styles = StyleSheet.create({
  transparentContainer: {
    position: "absolute",
    width: "100%",
    height: "100%",
    flex: 1,
    backgroundColor: defaultStyles.colors.transparent,
  },
  container: {
    flex: 1,
    alignContent: "center",
    justifyContent: "center",
  },
  imageContainer: {
    width: "100%",
    height: windowHeight / 3,
    position: "absolute",
    top: 0,
    backgroundColor: defaultStyles.colors.primary,
    left: 0,
    right: 0,
  },
  image: { width: "100%", height: "100%", resizeMode: "cover" },
  profileContainer: {
    borderTopLeftRadius: 30,
    borderTopRightRadius: 30,
    backgroundColor: defaultStyles.colors.white,
    flex: 1,
    marginTop: windowHeight / 3.5,
    padding: 15,
  },
  header: {
    flexDirection: "row",
    alignItems: "flex-start",
  },
  headerIcons: {
    flexDirection: "row",
    width: 70,
    justifyContent: "space-between",
  },
  cetaceanName: {
    fontSize: 22,
    fontWeight: "bold",
    marginBottom: 15,
  },
  title: {
    fontSize: 18,
    fontWeight: "bold",
    marginTop: 15,
    marginBottom: 5,
  },
  text: {
    lineHeight: 22,
    textAlign: "justify",
  },

  optionInactive: {
    marginVertical: 5,
    flexDirection: "row",
    alignItems: "flex-start",
    justifyContent: "space-evenly",
    paddingVertical: 4,
    paddingHorizontal: 10,
  },

  optionActive: {
    marginVertical: 5,
    flexDirection: "row",
    alignItems: "flex-start",
    justifyContent: "space-evenly",
    backgroundColor: defaultStyles.colors.secondary,
    borderRadius: 50,
    paddingVertical: 4,
    paddingHorizontal: 10,
  },
});

export default CetaceanProfileScreen;

Also, when i swipe down the bottom sheet, it says like this, and it works fine in some android simulators

Image of the screen when i swipe the bottomsheet down

While it should go to the end of the screen, what am i doing wrong?

Trying a solution for the problem 1

const BottomSheet = ({ children, title , deactivateBottomSheet}) => {
  const translateY = useSharedValue(0);
  const context = useSharedValue({ y: 0 });
  const gesture = Gesture.Pan()
    .onStart(() => {
      context.value = { y: translateY.value };
    })
    .onUpdate((event) => {
      translateY.value = event.translationY + context.value.y;
      translateY.value = Math.max(translateY.value, -windowHeight / 3);
    })
    .onEnd(() => {
      if (translateY.value > -windowHeight / 4) {
        translateY.value = withSpring(0, {
          damping: 15,
        });
      deactivateBottomSheet();
      } else if (translateY.value < -windowHeight / 3.5) {
        translateY.value = withSpring(-windowHeight / 3, { damping: 15 });
      }
    });

I tried to call a function where the animation ends, that is when the translateY.value is 0, and pass it through props to the parent component, and define the function there, but the app crashed.

foytecna
  • 21
  • 2

0 Answers0