Open In App

Create a Voice Notes App using React-Native

Last Updated : 05 Dec, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

We are going to build a Voice Notes app that will allow us to save our voice as a recording in our application. It is similar to a notes app but we will have our voice as notes. We can play the voice recordings, record them, and delete them. It leverages React-Native’s cross-platform capabilities to create a mobile application that facilitates recording voice and storing it. Using this application, the user will be able to save the audio on his/her device.

Preview of final output: Let us have a look at how the final output will look like.screenshot-1701571065091

Prerequisites & Technolgies Used:

Approach to create Voice Notes App

  • The application will have a single page.
  • We will create a recording button at the bottom of page.
  • On pressing the button, it will call the startRecording method. It asks for the permissions of recording the audio in the app.
  • Again during recording the audio, it will call the stopRecording method defined. Here we stop the recording and show the modal to ask for the name of recording.
  • After that we save a recoding, name in an array.
  • Then we will display the list of recordings.
  • After that we can click the play button of each list element and it will play the audio.

Steps to Create React Native Application:

Step 1: Create the project:

npx create-expo-app voice-notes-app

Step 2: Navigate to the project

cd voice-notes-app

Step 3: Install the packages as follows:

npx expo install expo-av
npx expo install @expo/vector-icons

Project Structure:

Screenshot-2023-12-03-081717

The updated dependencies in package.json file will look like:

"dependencies": {
    "expo": "~49.0.15",
    "expo-status-bar": "~1.6.0",
    "react": "18.2.0",
    "react-native": "0.72.6",
    "expo-av": "~13.4.1",
    "@expo/vector-icons": "^13.0.0"
}

Example: In this example we are following the above-explained approach.

Javascript




import { Audio } from 'expo-av';
import { StatusBar } from 'expo-status-bar';
import { useEffect, useState } from 'react';
import {
    Button, Modal, Pressable, StyleSheet,
    Text, TextInput, TouchableOpacity, View
} from 'react-native';
import Ionicons from '@expo/vector-icons/Ionicons';
 
export default function App() {
    const [recordings, setRecordings] = useState([]);
    const [recording, setRecording] = useState(null);
    const [recordingName, setRecordingName] = useState('');
    const [playing, setPlaying] = useState(-1)
    const [sound, setSound] = useState(null);
    const [isDialogVisible, setDialogVisible] = useState(false);
 
    async function startRecording() {
        try {
            await Audio.requestPermissionsAsync();
            await Audio.setAudioModeAsync({
                allowsRecordingIOS: true,
                playsInSilentModeIOS: true,
            });
 
            let { recording } =
                await Audio.Recording
                    .createAsync(
                        Audio.RecordingOptionsPresets
                            .HIGH_QUALITY
                    );
            audio = recording
            setRecording(recording);
        } catch (err) {
            console.error('Failed to start recording', err);
        }
    }
    async function stopRecording() {
        await recording.stopAndUnloadAsync();
        await Audio.setAudioModeAsync(
            {
                allowsRecordingIOS: false,
            }
        );
        setDialogVisible(true);
 
    }
    const handleSaveRecording = () => {
        if (recordingName.trim() !== '') {
            setRecordings([
                ...recordings,
                {
                    name: recordingName,
                    recording: recording,
                },
            ]);
            setRecording(undefined);
            setDialogVisible(false);
            setRecordingName('');
        }
    };
    useEffect(() => {
        return sound
            ? () => {
                sound.unloadAsync();
                console.log('Unloaded Sound');
            }
            : undefined;
    }, [sound]);
    return (
        <View style={styles.container}>
            <StatusBar style="auto" />
            <Text style={styles.heading}>
                Welcome to GeeksforGeeks
            </Text>
 
            <Modal visible={isDialogVisible}
                animationType="slide"
                style={styles.modal}>
                <View style={styles.column}>
                    <Text>
                        Enter Recording Name:
                    </Text>
                    <TextInput
                        style=
                        {
                            {
                                height: 40,
                                borderColor: 'gray',
                                borderWidth: 1,
                                marginBottom: 10,
                                padding: 10, width: 200,
                                borderRadius: 20
                            }
                        }
                        onChangeText={
                            (text) =>
                                setRecordingName(text)}
                        value={recordingName}
                    />
                    <Pressable
                        style={styles.button}
                        onPress={handleSaveRecording} >
                        <Text>Save</Text>
                    </Pressable>
                    <Pressable style={styles.button}
                        onPress={
                            () =>
                                setDialogVisible(false)} >
                        <Text>
                            Cancel
                        </Text>
                    </Pressable>
                </View>
            </Modal>
            <View style={styles.list}>
 
                {recordings.map((recording, index) => {
 
                    return (
                        <View key={index}>
 
                            <TouchableOpacity onPress={async () => {
                                const { sound } =
                                    await recording.
                                        recording.createNewLoadedSoundAsync(
                                            {
                                                isLooping: false,
                                                isMuted: false,
                                                volume: 1.0,
                                                rate: 1.0,
                                                shouldCorrectPitch: true,
                                            },
                                            (status) => {
                                                // console.log(status)
                                            },
                                            false
                                        );
                                setSound(sound);
                                setPlaying(index)
                                await sound.playAsync();
                                await
                                sound.setOnPlaybackStatusUpdate(
                                    async (status) => {
                                    if (status.didJustFinish) {
                                        setPlaying(-1)
                                        await sound.unloadAsync();
                                    }
                                }
                                );
                            }} style={styles.playButton}>
                                <Ionicons
                                    name={playing !== index ?
                                        "play" :
                                        "pause"}
                                    size={30}
                                    color="white" >
                                    <Text
                                        style={styles.recordingName}>
                                        {recording.name}
                                    </Text>
                                </Ionicons>
 
                                <Ionicons name="trash"
                                    size={30}
                                    color="white"
                                    onPress={() => {
                                        setRecordings(recordings
                                            .filter(
                                                (rec, i) =>
                                                    i !== index))
                                    }
                                    } />
                            </TouchableOpacity>
                        </View>
                    )
                })}
 
 
            </View>
            <View style={
                {
                    flex: 1,
                    flexDirection: "row",
                    justifyContent: "center",
                    alignSelf: "center",
                    bottom: 100,
                    position: "absolute",
                    padding: 10,
                }
            }>
                <Pressable style={styles.button}
                    onPress={recording ? stopRecording : startRecording}>
                    <Text style={{
                        textAlign: "center",
                    }}>{recording ? 'Stop Recording' : 'Start Recording'}</Text>
                </Pressable>
            </View>
        </View>
    );
}
 
const styles = StyleSheet.create({
    row: {
        flexDirection: "row",
        justifyContent: "space-evenly",
    },
    container: {
        backgroundColor: "#fff",
        height: "100%",
        marginTop: 50,
    }, contentContainer: {
        flex: 1,
    },
    column: {
        flex: 1,
        flexDirection: "column",
        justifyContent: "center",
        alignItems: "center",
 
    },
    heading: {
        color: "green",
        fontSize: 30,
        textAlign: "center",
        fontWeight: "bold",
    },
    list: {
        marginTop: 20,
        flex: 1,
        flexDirection: "column",
    },
    modal: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center",
    },
    button: {
        alignItems: "center",
        backgroundColor: "#DDDDDD",
        padding: 10,
        marginTop: 10,
        borderRadius: 20,
        width: 100,
        height: 40,
    },
    recordingName: {
        fontSize: 18,
        color: "white",
        fontWeight: 'bold',
    },
    playButton: {
        backgroundColor: 'gray',
        borderRadius: 50,
        padding: 10,
        margin: 10,
        flexDirection: "row",
        justifyContent: "space-between",
    },
});


Step 4: Navigate to the terminal or command prompt and type the required command there to run the React native application.

npx expo start
  • To run on Android:
npx react-native run-android
  • To run on IOS:
npx react-native run-ios

Output:

screenrec_32



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads