1.react-native-freelifemakers-ui NPM 모듈 설치하기 / react-native-freelifemakers-ui NPM Module Instal
# npm install react-native-freelifemakers-ui
2.의존성 모듈 설치 / peerModule install
— FlmBottomSheet를 사용하기 위해서 반드시 설치해야 하는 모듈입니다.
This is a module that must be installed to use FlmBottomSheet.
1)React-Native Expo 🌍
— ❗ react-native-freelifemakers-ui 이 모듈을 사용하기 위해서는 아래 두개의 모듈을 반드시 설치해야 합니다.
To use this module, you must install the following two modules: react-native-freelifemakers-ui
— ❗ 리액트 네이티브 엑스포는 반드시 react-native-reanimated@3.17.4 버전을 설치 합니다.
React Native Expo requires react-native-reanimated@3.17.4 to be installed.
— ❗ 엑스포 프로젝트와 3.17.4버전이 가장 호환이 잘되며 이것보다 상위버전 설치시 바텀시트가 동작하지 않을 수 있습니다.
Expo Project is most compatible with version 3.17.4, and the bottom sheet may not work when installing a higher version.
# npm install react-native-gesture-handler
# npm install react-native-reanimated@3.17.4
— babel.config.js 🌍
–> 이 파일이 없으면 파일을 직접 만듧니다.
If this file does not exist, create it yourself.
–> 프로젝트 폴더 내 package.json파일과 동일한 위치에 만들면 됩니다.
Just create it in the same location as the package.json file in your project folder.
–> 아래에 reanimated모듈을 위해 다음과 같은 설정을 적용합니다.
Apply the following settings for the reanimated module below:
// babel.config.js
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
'react-native-reanimated/plugin', //react-native-reanimated module setting
],
};
};
2)React-Native CLI 🌍
— ❗ react-native-freelifemakers-ui 이 모듈을 사용하기 위해서는 아래 두개의 모듈을 반드시 설치해야 합니다.
To use this module, you must install the following two modules: react-native-freelifemakers-ui
— ❗ 안드로이드에서는 react-native-reanimated모듈이 안정적으로 동작하지 않는 경우가 있습니다.
On Android, the react-native-reanimated module may not work reliably.
— IOS에서는 정상적으로 작동합니다.
It works fine on iOS.
— 리액트네이티브 CLI에서는 react-native-reanimated 최선버전을 설치해도 됩니다.
For React Native CLI, you can install the latest version of react-native-reanimated.
— 현재 3.18.0버전 설치해서 테스트 해본 결과 잘 작동 합니다.
I have currently installed version 3.18.0 and tested it and it works well.
# npm install react-native-gesture-handler
# npm install react-native-reanimated
— babel.config.js 🌍
–> 이 파일이 없으면 파일을 직접 만듧니다.
If this file does not exist, create it yourself.
–> 프로젝트 폴더 내 package.json파일과 동일한 위치에 만들면됩니다.
Just create it in the same location as the package.json file in your project folder.
–> 아래에 reanimated모듈을 위해 다음과 같은 설정을 적용합니다.
Apply the following settings for the reanimated module below:
// babel.config.js
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: ['react-native-reanimated/plugin'], //react-native-reanimated module setting
};
3.사용하기 / Usage
— 버튼 클릭시 바텀시트 보이며 바탕화면 탭하거나 버튼 클릭시 바텀시트 닫힙니다.
When you click the button, the bottom sheet appears, and when you tap the desktop or click the button, the bottom sheet closes.
— 바텀시트 드래그로 닫을 수 있습니다.
Bottom sheet can be closed by dragging
— 아래의 코드를 복사해서 실행해보고 잘 작동하면 수정해서 사용하시면 됩니다.
Please copy and run the code below. If it works well, you can modify it and use it.
// module import
import React, { useRef, useState, useEffect } from "react";
import { SafeAreaView, View, StyleSheet, ScrollView } from "react-native";
// Required modules
import { GestureHandlerRootView } from "react-native-gesture-handler";
import {FlmBottomSheet, FlmButton, FlmText} from 'react-native-freelifemakers-ui';
exprt default function App(){
// 바텀 시트 상태 설정 / bottom sheet status settings
const bottomSheetRef1 = useRef(null);
const [isVisible1, setIsVisible1] = useState(false);
// 바텀 시트 열기 / Open the bottom sheet
const openSheet1 = () => {
setIsVisible1(true);
};
// 바텀 시트 닫기 (useEffect에서 isVisible1 상태 변화에 따라 호출됨)
// Close the bottom sheet (called in useEffect when isVisible1 state changes)
const closeSheet1 = () => {
// isVisible 상태를 변경하여 useEffect가 close()를 호출하도록 유도
// Change the isVisible state to force useEffect to call close().
setIsVisible1(false);
};
// 바텀 시트 onClose 콜백 (바텀 시트 애니메이션 완료 후 호출됨)
// bottom sheet onClose callback (called after bottom sheet animation completes)
const onSheet1Close = () => setIsVisible1(false);
// openSheet1실행될때,바텀시트열기 클릭 할때 / When OpenSheet1 is executed, click Open Bottom Sheet
// 바텀 시트 오픈 / Bottom sheet open
useEffect(() => {
if (isVisible1) {
bottomSheetRef1.current?.open(0);
} else if (bottomSheetRef1.current) {
bottomSheetRef1.current.close();
}
}, [isVisible1]);
// UI area
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<SafeAreaView style={styles.container}>
<ScrollView contentContainerStyle={styles.scrollContent}>
{/* 바텀 시트 버튼 / First bottom sheet button */}
<FlmButton
title="첫 번째 시트 열기/Open first sheet"
onPress={openSheet1}
/>
</ScrollView>
{/* 바텀시트는 ScrollView 외부에서 렌더링 / Bottom sheet is rendered outside ScrollView */}
{isVisible1 && (
<FlmBottomSheet
ref={bottomSheetRef1}
snapPoints={["30%", "60%"]} // 시트위치 / sheet location
// 시트 시작위치(화면아래) / Sheet start position (bottom of screen)
// -1 : 랜더링시 자동으로 시트 열지 않음 / Do not automatically open sheets when rendering
// 0 : snapPoints 30% 실행 / 30% of snapPoints are executed
// 1 : snapPoints 60% 실행 / snapPoints 60% running
initialSnapIndex={-1}
enablePanDownToClose={true} // 아래로 드래그해서 닫기 기능 / Drag down to close feature
onOpen={() => console.log("첫 번째 시트 열림/First sheet opened")}
onClose={onSheet1Close} // 시트닫기 / sheet close
>
<ScrollView contentContainerStyle={styles.sheetContent}>
<FlmText style={styles.sheetText}>
첫 번째 바텀 시트 내용입니다.
</FlmText>
<FlmText style={styles.sheetText}>
Here is the first bottom sheet content.
</FlmText>
<FlmButton title="닫기/Close" onPress={closeSheet1} />
</ScrollView>
</FlmBottomSheet>
)}
</SafeAreaView>
</GestureHandlerRootView>
);
}
} // App()
4.예제코드 / Example code
import React, { useRef, useState, useEffect } from "react";
import { SafeAreaView, View, StyleSheet, ScrollView } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import {
FlmBottomSheet,
FlmButton,
FlmText,
} from "react-native-freelifemakers-ui";
export default function App() {
// 첫 번째 바텀 시트 상태 / First bottom sheet status
const bottomSheetRef1 = useRef(null);
const [isVisible1, setIsVisible1] = useState(false);
// 두 번째 바텀 시트 상태 / Second Bottom Sheet Status
const bottomSheetRef2 = useRef(null);
const [isVisible2, setIsVisible2] = useState(false);
// 세 번째 바텀 시트 상태 / Third Bottom Sheet State
const bottomSheetRef3 = useRef(null);
const [isVisible3, setIsVisible3] = useState(false);
//======= 첫번째 바텀시트 / First bottom sheet =======
// 첫 번째 바텀 시트 열기 / Open the first bottom sheet
const openSheet1 = () => {
setIsVisible1(true);
};
// 첫 번째 바텀 시트 닫기 (useEffect에서 isVisible1 상태 변화에 따라 호출됨)
// Close the first bottom sheet (called in useEffect when isVisible1 state changes)
const closeSheet1 = () => {
// isVisible 상태를 변경하여 useEffect가 close()를 호출하도록 유도
// Change the isVisible state to force useEffect to call close().
setIsVisible1(false);
};
// 첫 번째 바텀 시트 onClose 콜백 (바텀 시트 애니메이션 완료 후 호출됨)
// First bottom sheet onClose callback (called after bottom sheet animation completes)
const onSheet1Close = () => setIsVisible1(false);
useEffect(() => {
if (isVisible1) {
// snapPoints의 첫 번째 지점 (가장 높은 퍼센테이지/픽셀 높이)으로 열기
// Open to the first point of snapPoints (highest percentage/pixel height)
bottomSheetRef1.current?.open(0);
} else if (bottomSheetRef1.current) {
bottomSheetRef1.current.close();
}
}, [isVisible1]);
//======= 두번째 바텀시트 / second bottom sheet =======
// 두 번째 바텀 시트 열기 / Open the second bottom sheet
const openSheet2 = () => {
setIsVisible2(true);
};
// 두 번째 바텀 시트 닫기 (useEffect에서 isVisible2 상태 변화에 따라 호출됨)
// Close the second bottom sheet (called in useEffect when isVisible2 state changes)
const closeSheet2 = () => {
// isVisible 상태를 변경하여 useEffect가 close()를 호출하도록 유도
// Change the isVisible state to force useEffect to call close().
setIsVisible2(false);
};
// 두 번째 바텀 시트 onClose 콜백 (바텀 시트 애니메이션 완료 후 호출됨)
// Second bottom sheet onClose callback (called after bottom sheet animation completes)
const onSheet2Close = () => setIsVisible2(false);
useEffect(() => {
if (isVisible2) {
// snapPoints의 첫 번째 지점 (가장 높은 퍼센테이지/픽셀 높이)으로 열기
// Open to the first point of snapPoints (highest percentage/pixel height)
bottomSheetRef2.current?.open(0);
} else if (bottomSheetRef2.current) {
bottomSheetRef2.current.close();
}
}, [isVisible2]);
//======= 세번째 바텀시트 / Third bottom sheet =======
// 세 번째 바텀 시트 열기
// Open the third bottom sheet
const openSheet3 = () => {
setIsVisible3(true);
};
// 세 번째 바텀 시트 닫기
// Close the third bottom sheet
const closeSheet3 = () => {
setIsVisible3(false);
};
// 세 번째 바텀 시트 onClose 콜백
// Third bottom sheet onClose callback
const onSheet3Close = () => setIsVisible3(false);
useEffect(() => {
if (isVisible3) {
// 고정 높이 600px로 열기 / Open with fixed height 600px
bottomSheetRef3.current?.open(0);
} else if (bottomSheetRef3.current) {
bottomSheetRef3.current.close();
}
}, [isVisible3]);
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<SafeAreaView style={styles.container}>
<ScrollView contentContainerStyle={styles.scrollContent}>
{/*
바텀 시트는 모든 UI보다 아래에 위체 해야 합니다.
그렇지 않으면 UI가 바텀시트나 바텀시트 배경보다 위에 올 수 있습니다.
The bottom sheet must be positioned below all UI.
Otherwise, UI may be positioned above the bottom sheet or the bottom sheet background.
*/}
<FlmText style={styles.title}>Flm 다이내믹 바텀 시트</FlmText>
<FlmText style={styles.title}>Flm dynamic bottom seat</FlmText>
{/* 첫 번째 바텀 시트 버튼 / First bottom sheet button */}
<FlmButton
title="첫 번째 시트 열기/Open first sheet"
onPress={openSheet1}
/>
{/* 버튼 간 간격 / Spacing between buttons */}
<View style={styles.spacer} />
{/* 두 번째 바텀 시트 버튼 / Second bottom sheet button */}
<FlmButton
title="두 번째 시트 열기/Open second sheet"
onPress={openSheet2}
/>
{/* 버튼 간 간격 / Spacing between buttons */}
<View style={styles.spacer} />
{/* 세 번째 바텀 시트 버튼 / Third bottom sheet button */}
<FlmButton
title="세 번째 시트 열기/Open third sheet"
onPress={openSheet3}
/>
</ScrollView>
{/* 바텀시트는 ScrollView 외부에서 렌더링 / Bottom sheet is rendered outside ScrollView */}
{isVisible1 && (
<FlmBottomSheet
ref={bottomSheetRef1}
snapPoints={["30%", "60%"]} // 시트위치 / sheet location
// 시트 시작위치(화면아래) / Sheet start position (bottom of screen)
// -1 : 랜더링시 자동으로 시트 열지 않음 / Do not automatically open sheets when rendering
// 0 : snapPoints 30% 실행 / 30% of snapPoints are executed
// 1 : snapPoints 60% 실행 / snapPoints 60% running
initialSnapIndex={-1}
enablePanDownToClose={true} // 아래로 드래그해서 닫기 기능 / Drag down to close feature
onOpen={() => console.log("첫 번째 시트 열림/First sheet opened")}
onClose={onSheet1Close} // 시트닫기 / sheet close
>
<ScrollView contentContainerStyle={styles.sheetContent}>
<FlmText style={styles.sheetText}>
첫 번째 바텀 시트 내용입니다.
</FlmText>
<FlmText style={styles.sheetText}>
Here is the first bottom sheet content.
</FlmText>
<FlmButton title="닫기/Close" onPress={closeSheet1} />
</ScrollView>
</FlmBottomSheet>
)}
{isVisible2 && (
<FlmBottomSheet
ref={bottomSheetRef2}
snapPoints={["40%", "60%"]} // 시트위치 / sheet location
initialSnapIndex={-1}
enablePanDownToClose={true}
onOpen={() => console.log("두 번째 시트 열림/Second sheet open")}
onClose={onSheet2Close}
// 스타일 추가 / Add style
sheetStyle={{
backgroundColor: "#e6e6fa",
borderTopLeftRadius: 10,
borderTopRightRadius: 10,
}}
handleIndicatorStyle={{ backgroundColor: "#6a5acd", width: 50 }}
overlayStyle={{ backgroundColor: "rgba(0,100,0,0.3)" }}
>
<ScrollView contentContainerStyle={styles.sheetContent}>
<FlmText style={styles.sheetText}>
두 번째 바텀 시트 내용입니다. 다른 스타일이 적용되었습니다!
</FlmText>
<FlmText style={styles.sheetText}>
Here`s the second bottom sheet content, with a different style
applied!
</FlmText>
<FlmButton title="닫기/Close" onPress={closeSheet2} />
</ScrollView>
</FlmBottomSheet>
)}
{/* 세 번째 바텀 시트 - 고정 높이 600px / Third bottom sheet - fixed height 600px */}
{isVisible3 && (
<FlmBottomSheet
ref={bottomSheetRef3}
// 고정 높이 설정 / Set fixed height
snapPoints={[600]}
initialSnapIndex={-1}
enablePanDownToClose={true}
onOpen={() => console.log("세 번째 시트 열림/Third sheet open")}
onClose={onSheet3Close}
// 시트가 화면 아래서 부터 시작되도록 셋팅
// Set the sheet to start from the bottom of the screen
sheetStyle={{
backgroundColor: "#ffe4e1",
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
// 시트 높이 강제 지정 / Force seat height
height: 600,
position: "absolute",
// 화면 아래에서 올라오도록 설정 아래에 여백을 주지 않음.
// Set to rise from the bottom of the screen without leaving any margin below.
bottom: 0,
width: "100%", // 화면 너비 전체 사용 / Use full screen width
}}
handleIndicatorStyle={{ backgroundColor: "#ff69b4", width: 40 }}
overlayStyle={{ backgroundColor: "rgba(255,192,203,0.4)" }}
>
<ScrollView contentContainerStyle={styles.sheetContent}>
<FlmText style={styles.sheetText}>
세 번째 바텀 시트입니다. 고정 높이 600px입니다.
</FlmText>
<FlmText style={styles.sheetText}>
This is the third bottom sheet. It has a fixed height of 600px.
</FlmText>
<FlmButton title="닫기" onPress={closeSheet3} />
</ScrollView>
</FlmBottomSheet>
)}
</SafeAreaView>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
scrollContent: {
padding: 20,
alignItems: "center",
justifyContent: "center",
},
title: {
fontSize: 24,
marginBottom: 20,
},
spacer: {
height: 20,
},
sheetContent: {
padding: 20,
alignItems: "center",
},
sheetText: {
fontSize: 18,
marginBottom: 15,
textAlign: "center",
},
});
5.스크린샷/ScreenShot