Building a To-Do App in React Native with Theming options and AsyncStorage
Introduction
In this guide, we will walk through creating a To-Do App in React Native. This app will feature:
- Adding, deleting, and marking tasks as completed
- Dark mode support using a custom switch
- Persistent storage with AsyncStorage
- Smooth UI with custom styling and animations
Let's get started! 🚀
Prerequisites
Before we begin, ensure you have the following installed:
Step 1: Set Up a New React Native Project
Run the following command to create a new React Native project using Expo:
npx create-expo-app todo-app
cd todo-app
npm install
Step 2: Install Required Dependencies
We will use AsyncStorage for local data storage and Expo Icons for UI icons.
npm install @react-native-async-storage/async-storage
npm install @expo/vector-icons
Step 3: Create the Main App Component
Create an App.js
file and paste the following code:
import React, { useState, useEffect } from 'react';
import {
View, Text, FlatList, TouchableOpacity, TextInput, StyleSheet, Modal, Image, StatusBar, SafeAreaView
} from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { AntDesign } from '@expo/vector-icons';
import CustomSwitch from './CustomSwitch';
const App = () => {
const [tasks, setTasks] = useState([]);
const [isModalVisible, setModalVisible] = useState(false);
const [input, setInput] = useState('');
const [darkMode, setDarkMode] = useState(false);
const toggleTheme = () => setDarkMode(!darkMode);
const addTask = () => {
if (!input.trim()) return;
const newTask = { id: Date.now(), text: input, completed: false };
setTasks([...tasks, newTask]);
setInput('');
setModalVisible(false);
};
const deleteTask = (id) => setTasks(tasks.filter((task) => task.id !== id));
const toggleCompleted = (id) => {
setTasks(tasks.map(task => task.id === id ? { ...task, completed: !task.completed } : task));
};
useEffect(() => {
const loadTasks = async () => {
try {
const storedTasks = await AsyncStorage.getItem('tasks');
if (storedTasks) setTasks(JSON.parse(storedTasks));
} catch (error) {
console.error('Error loading tasks:', error);
}
};
loadTasks();
}, []);
useEffect(() => {
const saveTasks = async () => {
try {
await AsyncStorage.setItem('tasks', JSON.stringify(tasks));
} catch (error) {
console.error('Error saving tasks:', error);
}
};
saveTasks();
}, [tasks]);
return (
<SafeAreaView style={[styles.container, darkMode ? styles.darkBackground : styles.lightBackground]}>
<StatusBar barStyle={darkMode ? "light-content" : "dark-content"} backgroundColor={darkMode ? "#b5b7f5" : "#9395d3"} />
<View style={[styles.header, darkMode ? styles.darkBackgroundAccent : styles.lightBackgroundAccent]}>
<Text style={[styles.headerText, darkMode ? styles.darkText : styles.lightText]}>Todo App</Text>
<CustomSwitch value={darkMode} onValueChange={toggleTheme} />
</View>
<FlatList
data={tasks}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<View style={[styles.task, darkMode ? styles.cardBackgroundDark : styles.cardBackgroundLight]}>
<Text style={[styles.taskText, item.completed && styles.completedTask, darkMode ? styles.darkText : styles.lightText]}>{item.text}</Text>
<View style={styles.taskActions}>
<TouchableOpacity onPress={() => toggleCompleted(item.id)}>
<Image style={styles.tickImage} source={require('./assets/Completed.png')} />
</TouchableOpacity>
<TouchableOpacity onPress={() => deleteTask(item.id)}>
<Image style={styles.deleteImage} source={require('./assets/Delete.png')} />
</TouchableOpacity>
</View>
</View>
)}
/>
<TouchableOpacity style={[styles.addButton, darkMode ? styles.darkBackgroundAccent : styles.lightBackgroundAccent]} onPress={() => setModalVisible(true)}>
<AntDesign name="plus" size={24} color="white" />
</TouchableOpacity>
</SafeAreaView>
);
};
export default App;
Step 4: Create the Custom Switch Component
Create a file called CustomSwitch.js
and paste the following code:
import React, { useRef, useEffect } from 'react';
import { TouchableOpacity, View, StyleSheet, Animated } from 'react-native';
import Feather from '@expo/vector-icons/Feather';
import Ionicons from '@expo/vector-icons/Ionicons';
const CustomSwitch = ({ value, onValueChange }) => {
const translateX = useRef(new Animated.Value(value ? 20 : 0)).current;
useEffect(() => {
Animated.timing(translateX, {
toValue: value ? 20 : 0,
duration: 200,
useNativeDriver: true,
}).start();
}, [value]);
return (
<TouchableOpacity style={[styles.switch, value ? styles.onBackground : styles.offBackground]} onPress={onValueChange}>
<Animated.View style={[styles.toggle, { transform: [{ translateX }] }]}>
{value ? <Ionicons name="moon" size={15} color="black" /> : <Feather name="sun" size={15} color="black" />}
</Animated.View>
</TouchableOpacity>
);
};
export default CustomSwitch;
Step 5: Run the App
Now, start your development server by running:
npx expo start
Scan the QR code with Expo Go on your phone to preview the app!
Conclusion
Congratulations! 🎉 You have successfully built a To-Do App with React Native featuring dark mode, persistent storage, and smooth UI. Happy coding! 🚀
Top comments (0)