Open In App

Create an Expense Tracker using React-Native

Last Updated : 23 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Expense Tracker application using React-Native is an application where users can manage the transactions made and then visualize the expenditure with the help of a pie chart. The users can add transactions while providing the category of expenditure. The application will run on any device and is the scope of a much larger application. The application is cross-platform which means it can run on any application.

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

Screenshot-2023-09-23-171810

Prerequisites and Technologies Used

Approach

  • The application will have a single screen.
  • The application will have a pie chart and an add button.
  • On pressing the add button, a form will open. The form contains the expense name, amount and category from a select option.
  • Finally, on pressing add, the expense list and chart are updated.
  • The expenses are displayed in the form of a scrollable list.
  • Each expense tile will also have a delete button to remove it from the list.
  • We are using react-native-picker for a dropdown.
  • We used react-native-chart-kit

Steps to create React-Native Application

Step 1: Create the project

npx create-expo-app expense-tracker

Step 2: Navigate to the project

cd expense-tracker

Step 3: Install the required plugins

  • react-native-chart-kit: To generate a chart of expenses
  • @react-native-picker/picker: To create a dropdown
npx expo install react-native-chart-kit @react-native-picker/picker

Step 4: Create the following required files:

  • add_expense.js: For add expense form
  • expense_component.js :To display the list of form
  • styles.js: Stylesheet of the application

Project Structure:

Screenshot-2023-09-23-170301

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

"dependencies": {
"expo": "~49.0.11",
"expo-status-bar": "~1.6.0",
"react": "18.2.0",
"react-native": "0.72.4",
"react-native-chart-kit": "^6.12.0",
"@react-native-picker/picker": "2.4.10"
},
"devDependencies": {
"@babel/core": "^7.20.0"
}

Example: We will place the state variables in the App.js and load all the components declared in the respective files here. The code is divided into different files for making it readable.

App.js

Javascript




import { StatusBar } from "expo-status-bar";
import { useEffect, useState } from "react";
import { Button, SafeAreaView, Text, View } from "react-native";
import { PieChart } from "react-native-chart-kit";
import styles from "./styles";
import Addform from "./add_expense";
import ExpenseComponent from "./expense_component";
 
export default function App() {
    // Define state variables using the useState hook
    const [name, setName] = useState("");
    const [amount, setAmount] = useState("");
    const [category, setCategory] = useState("Food");
    const [expenses, setExpenses] = useState([]);
    const categories = ["Food", "Clothes", "Bills", "Others"];
    const [addForm, setAddForm] = useState(false);
 
    // Function to open the add expense form
    const addExpense = () => {
        setAddForm(true);
    };
 
    // Initialize the chart data with default values
    const [chartData, setChartData] = useState([
        {
            name: "Food",
            amount: 0,
            color: "#e62d20",
            legendFontColor: "#7F7F7F",
            legendFontSize: 15,
        },
        {
            name: "Clothes",
            amount: 0,
            color: "#27e620",
            legendFontColor: "#7F7F7F",
            legendFontSize: 15,
        },
        {
            name: "Bills",
            amount: 0,
            color: "#1c6bd9",
            legendFontColor: "#7F7F7F",
            legendFontSize: 15,
        },
        {
            name: "Others",
            amount: 0,
            color: "#5adbac",
            legendFontColor: "#7F7F7F",
            legendFontSize: 15,
        },
    ]);
 
    // Render the components and UI
    return (
        <SafeAreaView style={styles.container}>
            <StatusBar style="auto" />
            <Text style={styles.heading}>Welcome to GeeksforGeeks</Text>
            <Text style={styles.heading2}>
                Expense Tracker using React-Native
            </Text>
 
            {/* Render the PieChart component with data */}
            <PieChart
                data={chartData}
                width={300}
                height={200}
                chartConfig={{
                    backgroundGradientFrom: "#1E2923",
                    backgroundGradientTo: "#08130D",
                    color: (opacity = 1) => `rgba(26, 255, 146, ${opacity})`,
                }}
                accessor="amount"
                backgroundColor="transparent"
                paddingLeft="15"
                absolute
            />
 
            {/* Conditional rendering: If addForm is true,
                render the Addform component */}
            {addForm == true ? (
                <Addform
                    name={name}
                    setName={setName}
                    amount={amount}
                    setAmount={setAmount}
                    category={category}
                    setCategory={setCategory}
                    categories={categories}
                    setExpenses={setExpenses}
                    expenses={expenses}
                    chartData={chartData}
                    setChartData={setChartData}
                    setAddForm={setAddForm}
                />
            ) : (
                /* If addForm is false, render the "Add Expense" button */
                <View style={styles.row}>
                    <Button
                        onPress={addExpense}
                        color="green"
                        style={styles.addButton}
                        title="Add Expense"
                    />
                </View>
            )}
 
            {/* Render the ExpenseComponent */}
            <ExpenseComponent
                expenses={expenses}
                setExpenses={setExpenses}
                chartData={chartData}
                setChartData={setChartData}
            />
        </SafeAreaView>
    );
}


Javascript




// add_expense.js
 
import { Picker } from "@react-native-picker/picker";
import { Button, Text, TextInput, View } from "react-native";
import styles from "./styles";
 
// Define the Addform component which is used to add new expenses
export default function Addform({
    name,
    setName,
    amount,
    setAmount,
    category,
    setCategory,
    categories,
    setExpenses,
    expenses,
    chartData,
    setChartData,
    setAddForm,
}) {
    return (
        <View>
            <Text style={styles.heading3}>Add Form</Text>
 
            {/* Input field for expense name */}
            <Text style={styles.label}>Expense Name</Text>
            <TextInput
                onChangeText={(value) => setName(value)}
                value={name}
                style={styles.textInput}
                placeholder="Enter the expense name"
            />
 
            {/* Input field for expense amount */}
            <Text style={styles.label}>Amount</Text>
            <TextInput
                keyboardType="numeric"
                onChangeText={(value) => {
                    // Ensure only numeric values are entered for the amount
                    value = value.replace(/[^0-9]/g, "");
                    setAmount(value);
                }}
                value={amount}
                style={styles.textInput}
                placeholder="Amount"
            />
 
            {/* Dropdown to select expense category */}
            <Text style={styles.label}>Category</Text>
            <Picker
                style={styles.textInput}
                selectedValue={category}
                onValueChange={(itemValue, itemIndex) => setCategory(itemValue)}
            >
                {categories.map((category, index) => {
                    return (
                        <Picker.Item
                            key={index}
                            label={category}
                            value={category}
                        />
                    );
                })}
            </Picker>
 
            {/* Buttons to add or cancel expense */}
            <View style={styles.row}>
                {/* Add Expense button */}
                <Button
                    onPress={() => {
                        let amountNumber = parseInt(amount);
                        if (amountNumber <= 0 || name == "") {
                            // Validation: Ensure valid amount
                            // and name are entered
                            alert("Please enter a valid amount and name");
                            return;
                        }
 
                        // Add the new expense to the list of expenses
                        setExpenses([
                            ...expenses,
                            {
                                id: new Date().getTime(),
                                category,
                                name,
                                amount: amountNumber,
                            },
                        ]);
 
                        // Update the chart data to reflect the new expense
                        let newChartData = [...chartData];
                        let index = newChartData.findIndex(
                            (item) => item.name == category
                        );
                        newChartData[index].amount += amountNumber;
                        setChartData(newChartData);
 
                        // Reset form fields and close the form
                        setAddForm(false);
                        setName("");
                        setAmount("");
                        setCategory("Food");
                    }}
                    title="Add Expense"
                />
 
                {/* Cancel button to close the form
                    without adding an expense */}
                <Button
                    onPress={() => {
                        setAddForm(false);
                    }}
                    title="Cancel"
                />
            </View>
        </View>
    );
}


Javascript




// expense_component.js
 
import { Alert, Button, ScrollView, Text, View } from "react-native";
import styles from "./styles";
 
export default function ExpenseComponent({
    expenses,
    setExpenses,
    chartData,
    setChartData,
}) {
    return (
        <ScrollView
            style={{
                marginBottom: 80,
            }}
        >
            {expenses.map((expense) => {
                console.log(expense);
                return (
                    <ExpenseListTile
                        key={expense.id}
                        expense={expense}
                        chartData={chartData}
                        expenses={expenses}
                        setChartData={setChartData}
                        setExpenses={setExpenses}
                    />
                );
            })}
        </ScrollView>
    );
}
const ExpenseListTile = ({
    expense,
    expenses,
    setExpenses,
    chartData,
    setChartData,
}) => {
    return (
        <View style={styles.expenseTile}>
            <Text style={styles.expenseTileText}>{expense.name}</Text>
            <Text style={styles.expenseTileText}>{expense.category}</Text>
            <Text style={styles.expenseTileText}>{expense.amount}</Text>
            <Button
                onPress={() => {
                    Alert.alert("Delete", "Are you sure you want to delete?", [
                        {
                            text: "Yes",
                            onPress: () => {
                                let newExpenses = [...expenses];
                                let index = newExpenses.findIndex(
                                    (item) => item.id == expense.id
                                );
                                newExpenses.splice(index, 1);
                                setExpenses(newExpenses);
                                let newChartData = [...chartData];
                                let index2 = newChartData.findIndex(
                                    (item) => item.name == expense.category
                                );
                                newChartData[index2].amount -= expense.amount;
                                setChartData(newChartData);
                            },
                        },
                        {
                            text: "No",
                            onPress: () => {
                                console.log("No");
                            },
                        },
                    ]);
                }}
                title="Delete"
            />
        </View>
    );
};


Javascript




// styles.js
 
import { StyleSheet } from "react-native";
// styles sheet to store all the styles in one place
const styles = StyleSheet.create({
    row: {
        flexDirection: "row",
        justifyContent: "space-evenly",
    },
    container: {
        backgroundColor: "#fff",
        height: "100%",
        marginTop: 50,
    },
    heading: {
        color: "green",
        fontSize: 30,
        textAlign: "center",
        fontWeight: "bold",
    },
    addButton: {
        padding: 10,
        margin: 10,
    },
    heading2: {
        color: "black",
        fontSize: 25,
        textAlign: "center",
        fontWeight: "bold",
    },
    heading3: {
        color: "black",
        fontSize: 20,
        textAlign: "center",
    },
    label: {
        color: "black",
        fontSize: 16,
        textAlign: "left",
        fontWeight: "bold",
        marginLeft: 10,
    },
    expenseTile: {
        // column with 3 cells
        flexDirection: "row",
        justifyContent: "space-between",
        backgroundColor: "lightgrey",
        width: "95%",
        padding: 10,
        margin: 10,
    },
    expenseTileText: {
        fontSize: 20,
        width: "22%",
        textAlign: "center",
    },
    formAdd: {
        // display: "none",
    },
    textInput: {
        borderRadius: 12,
        borderColor: "black",
        borderWidth: 1,
        padding: 10,
        margin: 10,
    },
});
export default styles;


Steps to run the application:

Step 1: Run the application

npx expo start

Step optional: To run on Web, you need to install the following packages

npx expo install react-dom react-native-web @expo/webpack-config

Step 2: To run on web, press w on Terminal will application is running. For Android/IOS, install the Expo app and scan the QR code or enter the link of Metro in the Terminal.

Output:

screenrec_21

Explanation

  • The ExpenseComponent holds each of the expense element and displays them.
  • chartData is used to display the chart and expenses used to hold the individual expenses.
  • We toggle the form using addForm boolean variable.
  • On pressing Add Button, it is toggled so that we can show/hide the form.


Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads