Fix: NewWrapper footer position dengan SafeAreaView

Problem:
- Footer component tertutup atau scroll melebihi layar
- Tabs tidak bisa diklik karena footer floating
- Height OS_HEIGHT tidak konsisten di berbagai device

Solution:
 Footer menggunakan position: absolute untuk stay di bawah
 Konten ScrollView/FlatList mendapat paddingBottom: OS_HEIGHT
 SafeAreaView hanya untuk area aman, bukan height control
 Footer tetap visible di semua ukuran layar

Changes:
- Add styles.footerContainer dengan position: absolute
- Add paddingBottom ke contentContainerStyle (ScrollView & FlatList)
- Remove height: OS_HEIGHT dari SafeAreaView
- Footer stay di bottom dengan proper safe area handling

Result:
- Footer selalu terlihat di bawah layar
- Konten tidak tertutup footer (ada padding)
- Tabs bisa diklik dengan baik
- Konsisten di iOS & Android
- Respect safe area insets

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-03-26 12:28:07 +08:00
parent 16decd89c8
commit 31948f71db

View File

@@ -84,7 +84,7 @@ const NewWrapper = (props: NewWrapperProps) => {
return <View style={[GStyles.container, style]}>{content}</View>; return <View style={[GStyles.container, style]}>{content}</View>;
}; };
// 🔹 Mode Dinamis // 🔹 Mode Dinamis (FlatList)
if ("listData" in props) { if ("listData" in props) {
const listProps = props as ListModeProps; const listProps = props as ListModeProps;
@@ -96,7 +96,7 @@ const NewWrapper = (props: NewWrapperProps) => {
{headerComponent && ( {headerComponent && (
<View style={GStyles.stickyHeader}>{headerComponent}</View> <View style={GStyles.stickyHeader}>{headerComponent}</View>
)} )}
<View style={[GStyles.container, style]}> <View style={[GStyles.container, style, { flex: 1 }]}>
<FlatList <FlatList
data={listProps.listData} data={listProps.listData}
renderItem={listProps.renderItem} renderItem={listProps.renderItem}
@@ -108,29 +108,36 @@ const NewWrapper = (props: NewWrapperProps) => {
return `fallback-${index}-${JSON.stringify(item)}`; return `fallback-${index}-${JSON.stringify(item)}`;
} }
// Gabungkan ID dengan indeks untuk mencegah duplikasi
return `${String(item.id)}-${index}`; return `${String(item.id)}-${index}`;
}) })
} }
refreshControl={refreshControl} // ✅ dari BaseProps refreshControl={refreshControl}
onEndReached={listProps.onEndReached} onEndReached={listProps.onEndReached}
onEndReachedThreshold={0.5} onEndReachedThreshold={0.5}
ListHeaderComponent={listProps.ListHeaderComponent} ListHeaderComponent={listProps.ListHeaderComponent}
ListFooterComponent={listProps.ListFooterComponent} ListFooterComponent={listProps.ListFooterComponent}
ListEmptyComponent={listProps.ListEmptyComponent} ListEmptyComponent={listProps.ListEmptyComponent}
contentContainerStyle={{ flexGrow: 1 }} contentContainerStyle={{
flexGrow: 1,
paddingBottom: footerComponent && !hideFooter ? OS_HEIGHT : 0
}}
keyboardShouldPersistTaps="handled" keyboardShouldPersistTaps="handled"
/> />
</View> </View>
{footerComponent ? ( {/* Footer dengan position absolute untuk stay di bawah */}
<SafeAreaView {footerComponent && !hideFooter && (
edges={Platform.OS === "ios" ? edgesFooter : ["bottom"]} <View style={styles.footerContainer}>
style={{ backgroundColor: MainColor.darkblue, height: OS_HEIGHT }} <SafeAreaView
> edges={Platform.OS === "ios" ? edgesFooter : ["bottom"]}
{footerComponent} style={{ backgroundColor: MainColor.darkblue }}
</SafeAreaView> >
) : hideFooter ? null : ( {footerComponent}
</SafeAreaView>
</View>
)}
{!footerComponent && !hideFooter && (
<SafeAreaView <SafeAreaView
edges={["bottom"]} edges={["bottom"]}
style={{ backgroundColor: MainColor.darkblue }} style={{ backgroundColor: MainColor.darkblue }}
@@ -144,7 +151,7 @@ const NewWrapper = (props: NewWrapperProps) => {
); );
} }
// 🔹 Mode Statis // 🔹 Mode Statis (ScrollView)
const staticProps = props as StaticModeProps; const staticProps = props as StaticModeProps;
return ( return (
@@ -158,7 +165,10 @@ const NewWrapper = (props: NewWrapperProps) => {
<View style={{ flex: 0 }} collapsable={false}> <View style={{ flex: 0 }} collapsable={false}>
<ScrollView <ScrollView
contentContainerStyle={{ flexGrow: 1 }} contentContainerStyle={{
flexGrow: 1,
paddingBottom: footerComponent && !hideFooter ? OS_HEIGHT : 0
}}
keyboardShouldPersistTaps="handled" keyboardShouldPersistTaps="handled"
refreshControl={refreshControl} refreshControl={refreshControl}
> >
@@ -168,24 +178,19 @@ const NewWrapper = (props: NewWrapperProps) => {
</ScrollView> </ScrollView>
</View> </View>
{/* <ScrollView {/* Footer dengan position absolute untuk stay di bawah */}
contentContainerStyle={{ flexGrow: 0 }} {footerComponent && !hideFooter && (
keyboardShouldPersistTaps="handled" <View style={styles.footerContainer}>
refreshControl={refreshControl} // ✅ sekarang valid <SafeAreaView
> edges={Platform.OS === "ios" ? edgesFooter : ["bottom"]}
<TouchableWithoutFeedback onPress={Keyboard.dismiss}> style={{ backgroundColor: MainColor.darkblue }}
{renderContainer(staticProps.children)} >
</TouchableWithoutFeedback> {footerComponent}
</ScrollView> */} </SafeAreaView>
</View>
)}
{footerComponent ? ( {!footerComponent && !hideFooter && (
<SafeAreaView
edges={Platform.OS === "ios" ? edgesFooter : ["bottom"]}
style={{ backgroundColor: MainColor.darkblue, height: OS_HEIGHT }}
>
{footerComponent}
</SafeAreaView>
) : hideFooter ? null : (
<SafeAreaView <SafeAreaView
edges={["bottom"]} edges={["bottom"]}
style={{ backgroundColor: MainColor.darkblue }} style={{ backgroundColor: MainColor.darkblue }}
@@ -199,4 +204,15 @@ const NewWrapper = (props: NewWrapperProps) => {
); );
}; };
// Styles untuk footer dengan position absolute
const styles = {
footerContainer: {
position: "absolute" as const,
bottom: 0,
left: 0,
right: 0,
backgroundColor: MainColor.darkblue,
},
};
export default NewWrapper; export default NewWrapper;