如何在React Native中通过按钮关闭模态框





5.00/5 (4投票s)
在 React Native 中添加一个关闭按钮,并通过点击它旁边的区域来关闭模态框。
引言
最近我一直在使用 React Native,并且遇到了一些需要添加到基本组件中的未实现的功能。 其中一个组件是 React Native Modal 组件,用于在一个视图之上显示另一个视图。 它没有承诺比这更多的功能,但它对于实现您自己的弹出窗口非常有用。
另一方面,由于它只是一个简单的模态框,它不包含您对弹出组件的期望,例如关闭它的 X,或者在您点击它旁边时关闭模态框。 我将向您介绍如何在使用 Modal 组件开始时添加这两个功能。
如果您赶时间,也可以在我的 GitHub 上找到最终结果,网址为 https://github.com/CindyPotvin/react-native-popup。
如何关闭模态框
让我们从一个显示弹出窗口的基本 App.js 开始。 我还添加了一些样式以产生阴影,以便弹出窗口可见,并且添加了大量边距,以便稍后我们可以测试通过单击它旁边来关闭弹出窗口。
import React, { useState } from 'react';
import { StyleSheet, Modal, Text, View } from 'react-native';
export default function App() {
const [modalVisible, setModalVisible] = useState(true);
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</text>
<Modal visible={modalVisible}>
<View style={styles.modal}>
<Text>
Popup content.
</Text>
</View>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "white",
alignItems: "center",
justifyContent: "center",
},
modal: {
flex: 1,
margin: 50,
padding: 5,
backgroundColor: "white",
shadowColor: "black",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
});
Modal 是根据其可见属性显示的。 现在,状态中的值始终为 true
,因此弹出窗口将始终可见。
即使没有按钮或更改状态的另一种方法,也可以使用后退按钮在 Android 上关闭 Modal(Apple TV 上的菜单按钮应该具有相同的效果,根据文档,但我没有一个可以测试)。
按下按钮会调用在 modal 的 onRequestClose
属性中指定的函数,因此让我们更改状态以隐藏弹出窗口,当按下时,这将显示应用程序的主屏幕
<Modal
visible={modalVisible}
onRequestClose={() => setModalVisible(false)}>
如何向 React Native Modal 添加关闭按钮
首先,我们必须添加按钮本身,然后才能使用它来关闭弹出窗口。 在这种情况下,我想在弹出窗口的右上角添加一个小 X,但它可以在其他任何地方。
考虑到定位方式,有两种选择
- 将按钮绝对定位在右上角。 然后,您可以对内容的其余部分做任何您想做的事情,但是您会冒着内容与 X overlapping 的风险,因为它超出了正常的布局流程。 如果您想同时使用按钮旁边和下方的空间,那几乎是不可能的,这导致了第二种选择。
- 使用 flexbox 定位按钮,为按钮左侧的标题留出空间。 然后,您可以分别填写标题和底部的内容。 如果您做的事情旨在成为一个弹出窗口,拥有一个标题也是一个非常标准的功能。
以下是添加 X 后的组件现在的外观
import React, { useState } from 'react';
import { StyleSheet, Modal, Text, View, TouchableOpacity } from 'react-native';
export default function App() {
const [modalVisible, setModalVisible] = useState(true);
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<Modal visible={modalVisible} onRequestClose={() => setModalVisible(false)}>
<View style={styles.modal}>
<View style={styles.modalHeader}>
<View style={styles.modalHeaderContent}><Text>Other header content</Text></View>
<TouchableOpacity><Text style={styles.modalHeaderCloseText}>X</Text>
</TouchableOpacity>
</View>
<View style={styles.modalContent}>
<Text>
Popup content.
</Text>
</View>
</View>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "white",
alignItems: "center",
justifyContent: "center",
},
modal: {
flex: 1,
margin: 50,
padding: 5,
backgroundColor: "white",
shadowColor: "black",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
/* The content of the modal takes all the vertical space not used by the header. */
modalContent: {
flex: 1
},
modalHeader: {
flexDirection: "row",
},
/* The header takes up all the vertical space not used by the close button. */
modalHeaderContent: {
flexGrow: 1,
},
modalHeaderCloseText: {
textAlign: "center",
paddingLeft: 5,
paddingRight: 5
}
});
标题是一个 flex 容器,显示为一行,其中 flexGrow:1
表示它应该占用所有剩余空间。
弹出窗口中其余的内容是一个 flex:1
项目,因此它占用了所有剩余的高度。
此时剩下的唯一一件事是将按钮连接起来,以便它像我们在之前的 onRequestClose
事件中设置的那样关闭弹出窗口
<TouchableOpacity onPress={() => setModalVisible(false)}>
<Text style={styles.modalHeaderCloseText}>X</Text>
</TouchableOpacity>
如何通过点击外部区域关闭 Modal
要通过点击外部区域也关闭模态框,我们需要一个额外的组件来捕捉这些点击。
另一方面,我们不希望此组件捕捉旨在用于子组件的点击:点击弹出窗口本身不应该关闭它。 通过检查 event.target == event.currentTarget
,我们验证所选项目是组件本身而不是其子项之一。
我使用了 Pressable
组件,因为我不希望在触摸弹出窗口外部时出现与 TouchableOpacity
相关的点击时产生的“变暗”效果。 这个新的 Pressable 组件包裹了我们之前在 modal 中定义的所有组件。
这是已完成的弹出窗口,带有一些额外的边框以显示标题和弹出窗口内容的位置
import React, { useState } from 'react';
import { StyleSheet, Modal, Text, View, Pressable, TouchableOpacity } from 'react-native';
export default function App() {
const [modalVisible, setModalVisible] = useState(true);
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<Modal
visible={modalVisible}
onRequestClose={() => setModalVisible(false)}>
<Pressable style={styles.outsideModal}
onPress={(event) => { if (event.target == event.currentTarget) {
setModalVisible(false); } }} >
<View style={styles.modal}>
<View style={styles.modalHeader}>
<View style={styles.modalHeaderContent}>
<Text>Other header content</Text></View>
<TouchableOpacity onPress={() => setModalVisible(false)}>
<Text style={styles.modalHeaderCloseText}>X</Text>
</TouchableOpacity>
</View>
<View style={styles.modalContent}>
<Text>
Popup content.
</Text>
</View>
</View>
</Pressable>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "white",
alignItems: "center",
justifyContent: "center",
},
modal: {
flex: 1,
margin: 50,
padding: 5,
backgroundColor: "white",
shadowColor: "black",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
/* The content of the modal takes all the vertical space not used by the header. */
modalContent: {
flex: 1,
borderWidth: 1,
borderColor: "black"
},
modalHeader: {
flexDirection: "row",
borderWidth: 1,
borderColor: "black"
},
/* The header takes up all the vertical space not used by the close button. */
modalHeaderContent: {
flexGrow: 1,
},
modalHeaderCloseText: {
textAlign: "center",
paddingLeft: 5,
paddingRight: 5
},
outsideModal: {
backgroundColor: "rgba(1, 1, 1, 0.2)",
flex: 1,
}
});
请注意,这有一个小小的限制:Pressable
或 TouchableOpacity
的背景颜色不能设置为透明或半透明值,因此弹出窗口下的内容将不再可见。
要使其更好的下一步是,将 Modal 组件及其所有内容打包到一个新组件中,该组件可以在您的应用程序中重复使用,以便您可以在弹出窗口中插入任何标题或内容,但这超出了当前文章的范围。
如果您想自己执行最终版本并进行尝试,您可以在 GitHub 上查看它,网址为 https://github.com/CindyPotvin/react-native-popup,也可以在 Expo Snack 上查看,网址为 https://snack.expo.dev/@cindyptn/react-native-popup-with-x-button。
历史
- 2021年9月11日:初始版本