Prompt boxes are an inherent part of the web, and their utility varies with their use. They are used to display messages, warnings, alerts, and confirm consent.
Through a reminder dialog, the user is usually given the option to agree, disagree, and cancel buttons. Sometimes alerts are also used to record user input, but this depends on the platform.
In this article, we’ll learn how to create a custom alert dialog in React Native to suit the needs of your project.
Introduction to the
React Native provides an Alert API that can be used to display local Alert dialogs on Android and iOS. But the local alert dialog has some limitations.
For example, on Android we can’t display more than three buttons, and there’s no option to capture user input. While iOS allows us to have lots of buttons and let users enter data, we still can’t show pictures, charts, or any form of customization other than text.
To handle these limitations, we need to create custom alert dialog boxes. A custom alert dialog can act like a mode and can support components.
Properties of the alert box
Before customizing any native components, we should have a clear understanding of their architecture and event handling.
For example, a button has multiple properties, such as a label and inflation. A button also has events such as press, hold, release, hover, etc. When we customize it, we need to take all these attributes into account. Otherwise, we lose the look, feel and function.
A local alert box has the following properties.
- Title – A text title to indicate the purpose of the alert. Android and iOS support.
- Message – a text message that explains a notification or warning. Android and iOS support
- Buttons – Android supports up to three buttons, while iOS supports unlimited buttons.
- External Knock – Android alerts can be turned off by tapping on the outside of the alarm.
onPress
– The ability to make a phone call at the touch of a button. Android and iOS support this featureonDismiss
– The ability to call when the alarm is off. Only Android supports this feature- Prompt – allows the user to enter data in the input field. Only iOS supports this feature
- Back Button – By default in Android, alerts are turned off when the back button is pressed.
We need to take all of these attributes into account when customizing the alert box.
The user interface and architecture of the alert dialog
Let’s take a look at what the local Alert looks like and where the different elements are on it. Android and iOS Alert look and feel different.
Android specifications
Following the Material Design concept, the layout and color of the Android alert dialog is as follows.
The element | category | attribute | The value of |
The container | Maximum width Border radius The background color |
280 2 #fafafa |
|
Title text | On the surface | color The opacity Font weight The size of the case |
# 000000 100% The bold 22 Sentence case |
Supporting text | On the surface | color The opacity Font weight The size of the case |
# 000000 100% Regular 15 Sentence Case |
Button text | primary | color The opacity Font weight The size of the case |
#387ef5 100% 500 16 The capital letters |
scratches | On the surface | color The opacity |
#232F34 32% |
Other attributes, such as height, width, padding, margins, and so on, are shown below.
Used for small button actions.
Source: material. IO
For long button actions.
Source: material. IO
Other attributes include elevation – 24dp position – center from both sides of the minimum margin – 48dp
The specifications of the iOS
Also, for iOS, we have the following specs.
The element | category | attribute | The value of |
The container | The surface of the | Border radius Maximum width The background color z-index |
13px 270px #f8f8f8 10 |
Title text | On the surface | color Padding Margin Top Align Font Size Case |
# 000000 12px 16px 7px 8px Center 600 17px Sentence Case |
Supporting text | On the surface | color Align Padding Font Size Case |
# 000000 center 0px 16px 21px conventional 13 Sentence case |
Button in the container | Common properties The OK button Single row and multiple buttons – ordinary – Non-OK button |
The right margin The minimum height The top margin The font size color Line height Font weight Minimum width Right margin |
0.55 px. 44px 0.55 px solid # DBDBDF 17px #387ef5 20px 700 50% 0.55 px solid # DBDBDF |
shear | On the surface | color The opacity |
# 000000 30% |
This information is collected from the Ionic AlertController documentation.
Custom alert libraries and packages
There are libraries on GitHub that let you create custom alert boxes. Some of these are react-native awesome-alerts, react-native Dialog, and react-native Modal. You can try these libraries out, but in this article, we’ll customize the alert boxes without them.
The difference between alarms and modes
Alert and Modal are semantically the same thing, but differ in complexity and usability.
Reminder dialogs are designed to display short information in the simplest way possible, which is why they are limited in their functionality.
Modes, on the other hand, are used for complex displays. They require us to define the whole thing ourselves. By default, they provide event listeners, such as back button handlers.
Customize the alert dialog
Let’s take a look at what we’re going to customize. We should remember that alerts are used to display important information. This could be an error, warning, or notification. They are not for displaying pictures or filling out forms. For this, you should use templates.
In Alert, we will customize.
- Dialog box background color
- Font color, size, weight, etc. for headings and messages.
- The font color, background color, and border style of the button
Because React Native calls the Android and iOS Native Alert components, it doesn’t provide a direct way to customize them. Alerts are fixed components that have a clear purpose and are therefore not customizable in Android and iOS. Android developers use the Dialog class to do this.
In our case, we will use the React Native Modal API. The benefits of using this API are as follows.
- We don’t need to worry about the location of our custom alerts. It will stay at the top of the entire application
- Android’s back button and Apple TV’s menu button will be handled automatically.
React Native Modal
The React Native Modal API provides a container that is displayed in its surrounding View. A Boolean item visible is passed to the Modal component to show or hide it. There are other items, but we don’t care about them because they don’t work with Alert.
So that’s how Modal works.
import React, { useState } from 'react'; import { Modal, Text, Pressable, View } from 'react-native'; const App = () => { const [modalVisible, setModalVisible] = useState(false); return ( <View> <Modal animationType="fade" transparent={true} visible={modalVisible} onRequestClose={() => { setModalVisible(! modalVisible); }} > <View> /** * Anything here will display on Modal. * We need to create background overlay and Alert box. */ </View> </Modal> <View> /** * Main App content. This will get hidden when modal opens up */ </View> </View> ); }; export default App;Copy the code
ModalVisible is the state variable used to show or hide Modal. You can keep the modes on one page, or wrap the entire application around its parent View. It’s best to use a storage and state management library like Redux, as it can help you change modalVisible variables anywhere in your application.
Create a button to open itAlert
First, we will need an event to change the value of modalVisible. In our code, we will use a button. When this button is pressed, modalVisible becomes true and displays a Modal. To create this button, we will use the Pressable component.
import React, { useState } from 'react'; import { Modal, Text, Pressable, View } from 'react-native'; const App = () => { const [modalVisible, setModalVisible] = useState(false); return ( <View> <Modal animationType="fade" transparent={true} visible={modalVisible} onRequestClose={() => { setModalVisible(! modalVisible); }} > <View> /** * Anything here will display on Modal. * We need to create background overlay and Alert box. */ </View> </Modal> <View> <Pressable style={[styles.button, styles.buttonOpen]} onPress={() => setModalVisible(true)} > <Text style={styles.textStyle}>Show Modal</Text> </Pressable> </View> </View> ); }; const styles = StyleSheet.create({ centeredView: { flex: 1, justifyContent: "center", alignItems: "center", marginTop: 22 }, button: { borderRadius: 20, padding: 10, elevation: 2 }, buttonOpen: { backgroundColor: "#F194FF", }, textStyle: { color: "white", fontWeight: "bold", textAlign: "center" }, }); export default App;Copy the code
In this code, we added some styling to improve the look and feel of the buttons and other parts of the application. Currently, it will display a button. Clicking this button will open the mode. The rendered output will look like this.
Create a user interface for the alert dialog box
The next step is to create a modal UI. But first, we need to define a way to distinguish between the default UIs of the two operating systems. IOS has a different view than Android. React Native provides platform apis to identify operating systems.
Let’s start by creating the background. As we discussed in the UI and Architecture sections, the background color and opacity on Android are #232F34 and 0.32, respectively. For iOS, these values are #000000 and 0.3, respectively.
import React, { useState } from 'react'; import { Modal, Text, Pressable, View, Platform } from 'react-native'; const App = () => { const [modalVisible, setModalVisible] = useState(false); return ( <View> <Modal animationType="fade" transparent={true} visible={modalVisible} onRequestClose={() => { setModalVisible(! modalVisible); }} > <Pressable style={[Platform.OS === "ios" ? styles.iOSBackdrop : styles.androidBackdrop, styles.backdrop]} onPress={() => setModalVisible(false)} /> <View> /** * Anything here will display on Modal. * We need to create background overlay and Alert box. */ </View> </Modal> <View> <Pressable style={[styles.button, styles.buttonOpen]} onPress={() => setModalVisible(true)} > <Text style={styles.textStyle}>Show Modal</Text> </Pressable> </View> </View> ); }; const styles = StyleSheet.create({ centeredView: { flex: 1, justifyContent: "center", alignItems: "center", marginTop: 22 }, button: { borderRadius: 20, padding: 10, elevation: 2 }, buttonOpen: { backgroundColor: "#F194FF", }, textStyle: { color: "white", fontWeight: "bold", textAlign: "center" }, iOSBackdrop: { backgroundColor: "#000000", opacity: Opacity opacity backdrop backdrop backdrop backdrop backdrop backdrop: {position: 'absolute', top: 0, left: 0, opacity backdrop backdrop: {position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, } }); export default App;Copy the code
The rendered output will look something like this.
Note that we used the Pressable component to create the background. This is because we wanted to add the ability to turn off the modes when the background wall is pressed.
Next we will create an alert dialog box on the background panel. But first, we should put Modal in a separate component. This will help to call our custom Alert in a different style.
import React, { useState } from "react"; import { Alert, Modal, StyleSheet, Text, Pressable, View, Platform } from "react-native"; const CustomAlert = (props) => { return ( <Modal animationType="fade" transparent={true} visible={props.modalVisible} onRequestClose={() => { props.setModalVisible(false); }} > <Pressable style={[Platform.OS === "ios" ? styles.iOSBackdrop : styles.androidBackdrop, styles.backdrop]} onPress={() => props.setModalVisible(false)} /> <View> </View> </Modal> ) } const App = () => { const [modalVisible, setModalVisible] = useState(false); return ( <View style={styles.centeredView}> <CustomAlert modalVisible={modalVisible} setModalVisible={setModalVisible} /> <View> <Pressable style={[styles.button, styles.buttonOpen]} onPress={() => setModalVisible(true)} > <Text style={styles.textStyle}>Show Modal</Text> </Pressable> </View> </View> ); }; const styles = StyleSheet.create({ centeredView: { flex: 1, justifyContent: "center", alignItems: "center", marginTop: 22 }, button: { borderRadius: 20, padding: 10, elevation: 2 }, buttonOpen: { backgroundColor: "#F194FF", }, textStyle: { color: "white", fontWeight: "bold", textAlign: "center" }, iOSBackdrop: { backgroundColor: "#000000", opacity: Opacity opacity backdrop backdrop backdrop backdrop backdrop backdrop: {position: 'absolute', top: 0, left: 0, opacity backdrop backdrop: {position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, } }); export default App;Copy the code
We’ve created a different component called CustomAlert and put our Modal in it.
ModalVisible is in app.js state, but keep in mind that a better approach is to use a central storage management library such as Redux. Redux will make modalVisible state accessible to all components throughout the application without the need to pass modalVisible as a prop.
Now it’s time to set the default value for Modal. These values will design a box similar to the local alert box on Android and iOS.
We have defined all values in the UI and schema sections. Let’s start with Android.
Android custom alert dialog
The default value is.
Style properties | value |
The box The background color Maximum width margin highly Border radius |
transparent 280 48 24 2 |
The title margin color The font size Font weight |
24 # 000000 22 bold |
information Margin on the left Margin on the right Margin on the bottom color The font size Font weight |
24 24 24 # 000000 15 normal |
Button group margin |
0 0 8 and 24 |
button The top margin The right margin fillers color The font size Font weight The text conversion The background color |
12 8 10 #387ef5 16 500 A capital transparent |
These default values are defined in the CustomAlert component. These values are used if the user does not provide them.
The different items for the CustomAlert component are.
support | The value of | use | |
modalVisible | true|false | Need to be | To show/hide the modes |
setModalVisible | function | The necessary | Change the value of modalVisible |
The title | symbol | optional | Sets the title of the alert box |
The message | symbol | optional | Set the message |
The android |
{ container: { backgroundColor: String }, title: { color: String, fontFamily: String, fontSize: Number, fontWeight: String, }, message: { color: String, fontFamily: String, fontSize: Number, fontWeight: String }, } |
All fields are optional | Styles your Android app’s prompt, title, and message |
ios |
{ container: { backgroundColor: String }, title: { color: String, fontFamily: String, fontSize: Number, fontWeight: String, }, message: { color: String, fontFamily: String, fontSize: Number, fontWeight: String }, } |
All fields are optional | Set the styles of the iOS app prompt, title, and message. |
button |
[ { text: String, func: Function, styles: { color: String, fontSize: Number, fontWeight: String, fontFamily: String, textTransform: String, backgroundColor: String } } ] |
All fields are optional | Sets the properties of the button. There are a few key points here.
|
Take a look at this code to see how CustomAlert defines the default values.
const CustomAlert = (props) => {
const [androidDefaults, setAndroidDefaults] = useState({
container: {
backgroundColor: (props.android && props.android.container && props.android.container.backgroundColor) || '#FAFAFA',
},
title: {
color: (props.android && props.android.title && props.android.title.color) || '#000000',
fontFamily: (props.android && props.android.title && props.android.title.fontFamily) || 'initial',
fontSize: (props.android && props.android.title && props.android.title.fontSize) || 22,
fontWeight: (props.android && props.android.title && props.android.title.fontWeight) || 'bold',
},
message: {
color: (props.android && props.android.message && props.android.message.color) || '#000000',
fontFamily: (props.android && props.android.message && props.android.message.fontFamily) || 'initial',
fontSize: (props.android && props.android.message && props.android.message.fontSize) || 15,
fontWeight: (props.android && props.android.message && props.android.message.fontWeight) || 'normal',
},
button: {
color: '#387ef5',
fontFamily: 'initial',
fontSize: 16,
fontWeight: '500',
textTransform: 'uppercase',
backgroundColor: 'transparent',
},
});
return (
<Modal
animationType="fade"
transparent={true}
visible={props.modalVisible}
onRequestClose={() => {
props.setModalVisible(false);
}}
>
<Pressable style={[Platform.OS === "ios" ? styles.iOSBackdrop : styles.androidBackdrop, styles.backdrop]} onPress={() => props.setModalVisible(false)} />
<View style={styles.alertBox}>
{
Platform.OS === "ios" ?
null
:
<View style={[styles.androidAlertBox, androidDefaults.container]}>
<Text style={[styles.androidTitle, androidDefaults.title]}>{props.title || 'Message'}</Text>
<Text style={[styles.androidMessage, androidDefaults.message]}>{props.message || ''}</Text>
</View>
}
</View>
</Modal>
)
}
Copy the code
Some styling is for layout, which is why we don’t provide options to change them. And these are declared in the StyleSheet object.
const styles = StyleSheet.create({ centeredView: { flex: 1, justifyContent: "center", alignItems: "center", marginTop: 22 }, button: { borderRadius: 20, padding: 10, elevation: 2 }, buttonOpen: { backgroundColor: "#F194FF", }, textStyle: { color: "white", fontWeight: "bold", textAlign: "center" }, iOSBackdrop: { backgroundColor: "#000000", opacity: Opacity opacity backdrop backdrop backdrop backdrop backdrop backdrop: {position: 'absolute', top: 0, left: 0, bottom: 0, right: 0 }, alertBox: { flex: 1, justifyContent: 'center', alignItems: 'center' }, androidAlertBox: { maxWidth: 280, width: '100%', margin: 48, elevation: 24, borderRadius: 2, }, androidTitle: { margin: 24, }, androidMessage: { marginLeft: 24, marginRight: 24, marginBottom: 24, }, androidButtonGroup: { marginTop: 0, marginRight: 0, marginBottom: 8, marginLeft: 24, }, androidButton: { marginTop: 12, marginRight: 8, }, androidButtonInner: { padding: 10, } });Copy the code
If we run this with a different setup, we get the following output.
It’s time to add the button to the alert dialog. Android comes in several specifications.
- The individual buttons are always OK
- The two buttons are CANCEL and OK
- The three buttons are ASK ME LATER, CANCEL and OK.
- A maximum of three buttons are supported
- Two buttons float on the right side of the box, while the third button floats on the left
- Long buttons are displayed on different lines
To satisfy all of these conditions, we decided to declare a separate component for a button group. Let’s call it AndroidButtonBox. Take a look at this code.
const AndroidButtonBox = () => {
const [buttonLayoutHorizontal, setButtonLayoutHorizontal] = useState(1);
const buttonProps = props.buttons && props.buttons.length > 0 ? props.buttons : [{}]
return (
<View style={[styles.androidButtonGroup, {
flexDirection: buttonLayoutHorizontal === 1 ? "row" : "column",
}]} onLayout={(e) => {
if(e.nativeEvent.layout.height > 60)
setButtonLayoutHorizontal(0);
}}>
{
buttonProps.map((item, index) => {
if(index > 2) return null;
const alignSelfProperty = buttonProps.length > 2 && index === 0 && buttonLayoutHorizontal === 1 ? 'flex-start' : 'flex-end';
let defaultButtonText = 'OK'
if(buttonProps.length > 2){
if(index === 0)
defaultButtonText = 'ASK ME LATER'
else if(index === 1)
defaultButtonText = 'CANCEL';
} else if (buttonProps.length === 2 && index === 0)
defaultButtonText = 'CANCEL';
return (
<View style={[styles.androidButton, index === 0 && buttonLayoutHorizontal === 1 ? {flex: 1} : {}]}>
<Pressable onPress={() => {
props.setModalVisible(false)
if(item.func && typeof(item.func) === 'function')
item.func();
}} style={[{
alignSelf: alignSelfProperty,
}]}>
<View style={[styles.androidButtonInner, {backgroundColor: (item.styles && item.styles.backgroundColor) || androidDefaults.button.backgroundColor}]}>
<Text
style={{
color: (item.styles && item.styles.color) || androidDefaults.button.color,
fontFamily: (item.styles && item.styles.fontFamily) || androidDefaults.button.fontFamily,
fontSize: (item.styles && item.styles.fontSize) || androidDefaults.button.fontSize,
fontWeight: (item.styles && item.styles.fontWeight) || androidDefaults.button.fontWeight,
textTransform: (item.styles && item.styles.textTransform) || androidDefaults.button.textTransform,
}}
>{item.text || defaultButtonText}</Text>
</View>
</Pressable>
</View>
)
})
}
</View>
);
}
Copy the code
In this code, we declare a state variable, buttonLayoutHorizontal. This will be used to change the layout of the button group from columns to rows. If all buttons are short, they will be displayed in a single line.
The onLayout event is used to determine if the state variable needs to be changed. We then run a loop over the array of buttons provided through props and create buttons with the appropriate style.
The entire code (for Android) looks like this.
import React, { useState } from "react";
import { Alert, Modal, StyleSheet, Text, Pressable, View, Platform } from "react-native";
const CustomAlert = (props) => {
const [androidDefaults, setAndroidDefaults] = useState({
container: {
backgroundColor: (props.android && props.android.container && props.android.container.backgroundColor) || '#FAFAFA',
},
title: {
color: (props.android && props.android.title && props.android.title.color) || '#000000',
fontFamily: (props.android && props.android.title && props.android.title.fontFamily) || 'initial',
fontSize: (props.android && props.android.title && props.android.title.fontSize) || 22,
fontWeight: (props.android && props.android.title && props.android.title.fontWeight) || 'bold',
},
message: {
color: (props.android && props.android.message && props.android.message.color) || '#000000',
fontFamily: (props.android && props.android.message && props.android.message.fontFamily) || 'initial',
fontSize: (props.android && props.android.message && props.android.message.fontSize) || 15,
fontWeight: (props.android && props.android.message && props.android.message.fontWeight) || 'normal',
},
button: {
color: '#387ef5',
fontFamily: 'initial',
fontSize: 16,
fontWeight: '500',
textTransform: 'uppercase',
backgroundColor: 'transparent',
},
});
const AndroidButtonBox = () => {
const [buttonLayoutHorizontal, setButtonLayoutHorizontal] = useState(1);
const buttonProps = props.buttons && props.buttons.length > 0 ? props.buttons : [{}]
return (
<View style={[styles.androidButtonGroup, {
flexDirection: buttonLayoutHorizontal === 1 ? "row" : "column",
}]} onLayout={(e) => {
if(e.nativeEvent.layout.height > 60)
setButtonLayoutHorizontal(0);
}}>
{
buttonProps.map((item, index) => {
if(index > 2) return null;
const alignSelfProperty = buttonProps.length > 2 && index === 0 && buttonLayoutHorizontal === 1 ? 'flex-start' : 'flex-end';
let defaultButtonText = 'OK'
if(buttonProps.length > 2){
if(index === 0)
defaultButtonText = 'ASK ME LATER'
else if(index === 1)
defaultButtonText = 'CANCEL';
} else if (buttonProps.length === 2 && index === 0)
defaultButtonText = 'CANCEL';
return (
<View style={[styles.androidButton, index === 0 && buttonLayoutHorizontal === 1 ? {flex: 1} : {}]}>
<Pressable onPress={() => {
props.setModalVisible(false)
if(item.func && typeof(item.func) === 'function')
item.func();
}} style={[{
alignSelf: alignSelfProperty,
}]}>
<View style={[styles.androidButtonInner, {backgroundColor: (item.styles && item.styles.backgroundColor) || androidDefaults.button.backgroundColor}]}>
<Text
style={{
color: (item.styles && item.styles.color) || androidDefaults.button.color,
fontFamily: (item.styles && item.styles.fontFamily) || androidDefaults.button.fontFamily,
fontSize: (item.styles && item.styles.fontSize) || androidDefaults.button.fontSize,
fontWeight: (item.styles && item.styles.fontWeight) || androidDefaults.button.fontWeight,
textTransform: (item.styles && item.styles.textTransform) || androidDefaults.button.textTransform,
}}
>{item.text || defaultButtonText}</Text>
</View>
</Pressable>
</View>
)
})
}
</View>
);
}
return (
<Modal
animationType="fade"
transparent={true}
visible={props.modalVisible}
onRequestClose={() => {
props.setModalVisible(false);
}}
>
<Pressable style={[Platform.OS === "ios" ? styles.iOSBackdrop : styles.androidBackdrop, styles.backdrop]} onPress={() => props.setModalVisible(false)} />
<View style={styles.alertBox}>
{
Platform.OS === "ios" ?
null
:
<View style={[styles.androidAlertBox, androidDefaults.container]}>
<Text style={[styles.androidTitle, androidDefaults.title]}>{props.title || 'Message'}</Text>
<Text style={[styles.androidMessage, androidDefaults.message]}>{props.message || ''}</Text>
<AndroidButtonBox />
</View>
}
</View>
</Modal>
)
}
const App = () => {
const [modalVisible, setModalVisible] = useState(false);
return (
<View style={styles.centeredView}>
<CustomAlert
modalVisible={modalVisible}
setModalVisible={setModalVisible}
title={'Alert Title'}
message={'This is some message'}
android={{
container: {
backgroundColor: 'yellow'
},
title: {
color: 'red',
fontFamily: 'Roboto',
fontSize: 26,
fontWeight: 'regular',
},
message: {
color: 'blue',
fontFamily: 'Roboto',
fontSize: 16,
fontWeight: 'regular',
},
}}
buttons={[{
text: 'no'
},{
text: 'Yes',
func: () => {console.log('Yes Pressed')},
styles: {
color: '#FFFFFF',
fontSize: 18,
fontWeight: 'bold',
fontFamily: 'Roboto',
textTransform: 'none',
backgroundColor: '#000000'
}
}]}
/>
<View>
<Pressable
style={[styles.button, styles.buttonOpen]}
onPress={() => setModalVisible(true)}
>
<Text style={styles.textStyle}>Show Modal</Text>
</Pressable>
</View>
</View>
);
};
const styles = StyleSheet.create({
centeredView: {
flex: 1,
justifyContent: "center",
alignItems: "center",
marginTop: 22
},
button: {
borderRadius: 20,
padding: 10,
elevation: 2
},
buttonOpen: {
backgroundColor: "#F194FF",
},
textStyle: {
color: "white",
fontWeight: "bold",
textAlign: "center"
},
iOSBackdrop: {
backgroundColor: "#000000",
opacity: 0.3
},
androidBackdrop: {
backgroundColor: "#232f34",
opacity: 0.4
},
backdrop: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0
},
alertBox: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
androidAlertBox: {
maxWidth: 280,
width: '100%',
margin: 48,
elevation: 24,
borderRadius: 2,
},
androidTitle: {
margin: 24,
},
androidMessage: {
marginLeft: 24,
marginRight: 24,
marginBottom: 24,
},
androidButtonGroup: {
marginTop: 0,
marginRight: 0,
marginBottom: 8,
marginLeft: 24,
},
androidButton: {
marginTop: 12,
marginRight: 8,
},
androidButtonInner: {
padding: 10,
}
});
export default App;
Copy the code
The rendered output for the different values of CustomAlert is.
IOS custom alert dialog box
The code for the iOS alert box will be similar to Android, with a change in style.
In iOS, all entities such as titles, messages, and buttons are centered. There can be any number of buttons. An iOS and Android CustomAlert box complete code is.
import React, { useState } from "react";
import { Alert, Modal, StyleSheet, Text, Pressable, View, Platform } from "react-native";
const CustomAlert = (props) => {
const [androidDefaults, setAndroidDefaults] = useState({
container: {
backgroundColor: (props.android && props.android.container && props.android.container.backgroundColor) || '#FAFAFA',
},
title: {
color: (props.android && props.android.title && props.android.title.color) || '#000000',
fontFamily: (props.android && props.android.title && props.android.title.fontFamily) || 'initial',
fontSize: (props.android && props.android.title && props.android.title.fontSize) || 22,
fontWeight: (props.android && props.android.title && props.android.title.fontWeight) || 'bold',
},
message: {
color: (props.android && props.android.message && props.android.message.color) || '#000000',
fontFamily: (props.android && props.android.message && props.android.message.fontFamily) || 'initial',
fontSize: (props.android && props.android.message && props.android.message.fontSize) || 15,
fontWeight: (props.android && props.android.message && props.android.message.fontWeight) || 'normal',
},
button: {
color: '#387ef5',
fontFamily: 'initial',
fontSize: 16,
fontWeight: '500',
textTransform: 'uppercase',
backgroundColor: 'transparent',
},
});
const [iOSDefaults, setIOSDefaults] = useState({
container: {
backgroundColor: (props.ios && props.ios.container && props.ios.container.backgroundColor) || '#F8F8F8',
},
title: {
color: (props.ios && props.ios.title && props.ios.title.color) || '#000000',
fontFamily: (props.ios && props.ios.title && props.ios.title.fontFamily) || 'initial',
fontSize: (props.ios && props.ios.title && props.ios.title.fontSize) || 17,
fontWeight: (props.ios && props.ios.title && props.ios.title.fontWeight) || '600',
},
message: {
color: (props.ios && props.ios.message && props.ios.message.color) || '#000000',
fontFamily: (props.ios && props.ios.message && props.ios.message.fontFamily) || 'initial',
fontSize: (props.ios && props.ios.message && props.ios.message.fontSize) || 13,
fontWeight: (props.ios && props.ios.message && props.ios.message.fontWeight) || 'normal',
},
button: {
color: '#387ef5',
fontFamily: 'initial',
fontSize: 17,
fontWeight: '500',
textTransform: 'none',
backgroundColor: 'transparent',
},
});
const AndroidButtonBox = () => {
const [buttonLayoutHorizontal, setButtonLayoutHorizontal] = useState(1);
const buttonProps = props.buttons && props.buttons.length > 0 ? props.buttons : [{}]
return (
<View style={[styles.androidButtonGroup, {
flexDirection: buttonLayoutHorizontal === 1 ? "row" : "column",
}]} onLayout={(e) => {
if(e.nativeEvent.layout.height > 60)
setButtonLayoutHorizontal(0);
}}>
{
buttonProps.map((item, index) => {
if(index > 2) return null;
const alignSelfProperty = buttonProps.length > 2 && index === 0 && buttonLayoutHorizontal === 1 ? 'flex-start' : 'flex-end';
let defaultButtonText = 'OK'
if(buttonProps.length > 2){
if(index === 0)
defaultButtonText = 'ASK ME LATER'
else if(index === 1)
defaultButtonText = 'CANCEL';
} else if (buttonProps.length === 2 && index === 0)
defaultButtonText = 'CANCEL';
return (
<View style={[styles.androidButton, index === 0 && buttonLayoutHorizontal === 1 ? {flex: 1} : {}]}>
<Pressable onPress={() => {
props.setModalVisible(false)
if(item.func && typeof(item.func) === 'function')
item.func();
}} style={[{
alignSelf: alignSelfProperty,
}]}>
<View style={[styles.androidButtonInner, {backgroundColor: (item.styles && item.styles.backgroundColor) || androidDefaults.button.backgroundColor}]}>
<Text
style={{
color: (item.styles && item.styles.color) || androidDefaults.button.color,
fontFamily: (item.styles && item.styles.fontFamily) || androidDefaults.button.fontFamily,
fontSize: (item.styles && item.styles.fontSize) || androidDefaults.button.fontSize,
fontWeight: (item.styles && item.styles.fontWeight) || androidDefaults.button.fontWeight,
textTransform: (item.styles && item.styles.textTransform) || androidDefaults.button.textTransform,
}}
>{item.text || defaultButtonText}</Text>
</View>
</Pressable>
</View>
)
})
}
</View>
);
}
const IOSButtonBox = () => {
const buttonProps = props.buttons && props.buttons.length > 0 ? props.buttons : [{}]
const [buttonLayoutHorizontal, setButtonLayoutHorizontal] = useState(buttonProps.length === 2 ? 1 : 0);
return (
<View style={[styles.iOSButtonGroup, {
flexDirection: buttonLayoutHorizontal === 1 ? "row" : "column",
}]} onLayout={(e) => {
if(e.nativeEvent.layout.height > 60)
setButtonLayoutHorizontal(0);
}}>
{
buttonProps.map((item, index) => {
let defaultButtonText = 'OK'
if(buttonProps.length > 2){
if(index === 0)
defaultButtonText = 'ASK ME LATER'
else if(index === 1)
defaultButtonText = 'CANCEL';
} else if (buttonProps.length === 2 && index === 0)
defaultButtonText = 'CANCEL';
const singleButtonWrapperStyle = {}
let singleButtonWeight = iOSDefaults.button.fontWeight;
if(index === buttonProps.length - 1){
singleButtonWeight = '700';
}
if(buttonLayoutHorizontal === 1){
singleButtonWrapperStyle.minWidth = '50%';
if(index === 0){
singleButtonWrapperStyle.borderStyle = 'solid';
singleButtonWrapperStyle.borderRightWidth = 0.55;
singleButtonWrapperStyle.borderRightColor = '#dbdbdf';
}
}
return (
<View style={[styles.iOSButton, singleButtonWrapperStyle]}>
<Pressable onPress={() => {
props.setModalVisible(false)
if(item.func && typeof(item.func) === 'function')
item.func();
}}>
<View style={[styles.iOSButtonInner, {backgroundColor: (item.styles && item.styles.backgroundColor) || iOSDefaults.button.backgroundColor}]}>
<Text
style={{
color: (item.styles && item.styles.color) || iOSDefaults.button.color,
fontFamily: (item.styles && item.styles.fontFamily) || iOSDefaults.button.fontFamily,
fontSize: (item.styles && item.styles.fontSize) || iOSDefaults.button.fontSize,
fontWeight: (item.styles && item.styles.fontWeight) || singleButtonWeight,
textTransform: (item.styles && item.styles.textTransform) || iOSDefaults.button.textTransform,
textAlign: 'center'
}}
>{item.text || defaultButtonText}</Text>
</View>
</Pressable>
</View>
)
})
}
</View>
);
}
return (
<Modal
animationType="fade"
transparent={true}
visible={props.modalVisible}
onRequestClose={() => {
props.setModalVisible(false);
}}
>
<Pressable style={[Platform.OS === "ios" ? styles.iOSBackdrop : styles.androidBackdrop, styles.backdrop]} onPress={() => props.setModalVisible(false)} />
<View style={styles.alertBox}>
{
Platform.OS === "ios" ?
<View style={[styles.iOSAlertBox, iOSDefaults.container]}>
<Text style={[styles.iOSTitle, iOSDefaults.title]}>{props.title || 'Message'}</Text>
<Text style={[styles.iOSMessage, iOSDefaults.message]}>{props.message || ''}</Text>
<IOSButtonBox />
</View>
:
<View style={[styles.androidAlertBox, androidDefaults.container]}>
<Text style={[styles.androidTitle, androidDefaults.title]}>{props.title || 'Message'}</Text>
<Text style={[styles.androidMessage, androidDefaults.message]}>{props.message || ''}</Text>
<AndroidButtonBox />
</View>
}
</View>
</Modal>
)
}
const App = () => {
const [modalVisible, setModalVisible] = useState(false);
return (
<View style={styles.centeredView}>
<CustomAlert
modalVisible={modalVisible}
setModalVisible={setModalVisible}
title={'Alert Title'}
message={'This is some message'}
android={{
container: {
backgroundColor: 'yellow'
},
title: {
color: 'red',
fontFamily: 'Roboto',
fontSize: 26,
fontWeight: 'regular',
},
message: {
color: 'blue',
fontFamily: 'Roboto',
fontSize: 16,
fontWeight: 'regular',
},
}}
ios={{
container: {
backgroundColor: 'yellow'
},
title: {
color: 'red',
fontFamily: 'Roboto',
fontSize: 26,
fontWeight: 'regular',
},
message: {
color: 'blue',
fontFamily: 'Roboto',
fontSize: 16,
fontWeight: 'regular',
},
}}
buttons={[{
text: 'no'
},{
text: 'Yes',
func: () => {console.log('Yes Pressed')},
styles: {
color: '#FFFFFF',
fontSize: 18,
fontWeight: 'bold',
fontFamily: 'Roboto',
textTransform: 'none',
backgroundColor: '#000000'
}
}]}
/>
<View>
<Pressable
style={[styles.button, styles.buttonOpen]}
onPress={() => setModalVisible(true)}
>
<Text style={styles.textStyle}>Show Modal</Text>
</Pressable>
</View>
</View>
);
};
const styles = StyleSheet.create({
centeredView: {
flex: 1,
justifyContent: "center",
alignItems: "center",
marginTop: 22
},
button: {
borderRadius: 20,
padding: 10,
elevation: 2
},
buttonOpen: {
backgroundColor: "#F194FF",
},
textStyle: {
color: "white",
fontWeight: "bold",
textAlign: "center"
},
iOSBackdrop: {
backgroundColor: "#000000",
opacity: 0.3
},
androidBackdrop: {
backgroundColor: "#232f34",
opacity: 0.4
},
backdrop: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0
},
alertBox: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
androidAlertBox: {
maxWidth: 280,
width: '100%',
margin: 48,
elevation: 24,
borderRadius: 2,
},
androidTitle: {
margin: 24,
},
androidMessage: {
marginLeft: 24,
marginRight: 24,
marginBottom: 24,
},
androidButtonGroup: {
marginTop: 0,
marginRight: 0,
marginBottom: 8,
marginLeft: 24,
},
androidButton: {
marginTop: 12,
marginRight: 8,
},
androidButtonInner: {
padding: 10,
},
iOSAlertBox: {
maxWidth: 270,
width: '100%',
zIndex: 10,
borderRadius: 13,
},
iOSTitle: {
paddingTop: 12,
paddingRight: 16,
paddingBottom: 7,
paddingLeft: 16,
marginTop: 8,
textAlign: "center",
},
iOSMessage: {
paddingTop: 0,
paddingRight: 16,
paddingBottom: 21,
paddingLeft: 16,
textAlign: "center"
},
iOSButtonGroup: {
marginRight: -0.55
},
iOSButton: {
borderTopColor: '#dbdbdf',
borderTopWidth: 0.55,
borderStyle: 'solid',
},
iOSButtonInner: {
minHeight: 44,
justifyContent: 'center'
}
});
export default App;
Copy the code
The render output for different CustomAlert values is.
Live demonstration
conclusion
In this article, we looked at alert dialogs in Android and iOS. We also see differences in their user interfaces, specifications, and attributes.
These alert dialogs have a clear purpose and should not be overused. Use information only when it is unavoidable to the user because it clogs the user interface.
There are many enhanced scopes in our custom alerts. You can change padding, margins, add background images, ICONS, SVG, and more. If you try this code out in your project, please let me know how it works in the comments. Thank you
The postHow to create a custom alert dialog in React Nativeappeared first onLogRocket Blog.