Since there isn't a built in solution, you could achieve what you want by doing something like so:
(1) You will need to replace react-navigation's generated header with a custom one, like the following:
function LogoTitle() {
return (
<Image
style={{ width: 50, height: 50 }}
source={require('@expo/snack-static/react-native-logo.png')}
/>
);
}
function StackScreen() {
return (
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ headerTitle: (props) => <LogoTitle {...props} /> }}
/>
</Stack.Navigator>
);
}
Source: https://reactnavigation.org/docs/headers#replacing-the-title-with-a-custom-component
(2) Your custom component would have to look something like this:
export default function App() {
const cancelButtonOnPress = () => {
navigation.goBack();
}
return (
<View style={styles.container}>
<View style={styles.bar}></View>
<Text onPress={()=>{cancelButtonOnPress()}} style={styles.cancelButton}>Cancel</Text>
</View>
);
}
const styles = StyleSheet.create({
cancelButton: {
color: '#0573ad'
},
bar: {
alignSelf: 'center',
width: 40,
height: 7,
backgroundColor: 'gray',
borderRadius: 40,
},
container: {
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
padding: 8,
borderWidth: 1,
borderColor: 'black',
},
});
The above App looks like the following:

(3) Since you are passing your custom component to react-navigation, the gesture swipe to dismiss the modal should be retained, if for some reason it isn't, you could try setting fullScreenGestureEnabled to true in screenOptions.