Create an Expense Tracker using React-Native
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.
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:
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 w3wiki</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:
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.
Contact Us