My Inspiration for VeLi (Vegetarian Living)
The idea for VeLi came from observing the growing movement toward plant-based lifestyles and the need for a simple, beautiful app that celebrates vegetarian food. I wanted to create something that feels fresh, organic, and inviting—just like the vegetables and plant-based meals we're showcasing.
The name "VeLi" is short for "Vegetarian Living," representing a lifestyle choice rather than just a diet. The app aims to inspire users to explore the colorful world of plant-based foods, discover new recipes, and connect with a community that values health, sustainability, and compassion.
I chose a green color palette because green is universally associated with nature, growth, and freshness. The specific shades I selected—from the deep forest green (#1B5E20) to the vibrant leaf green (#4CAF50)—create a sense of vitality and earth connection. These aren't just random greens; they're carefully chosen to feel mature and trustworthy while still being energetic and inviting.
Now let me walk you through building VeLi from the ground up, piece by piece, so you can see exactly how everything comes together and test each step along the way.
Part 1: Setting Up Your Foundation
First, let's create your project structure. Open your terminal and navigate to where you want to create your project, then run these commands one at a time:
npx create-expo-app VeLi
cd VeLi
Wait for this to complete. You should see Expo create a new folder called VeLi with some starter files inside.
Now let's install the navigation libraries. These are the special tools that will let us create tabs and navigate between screens:
npm install @react-navigation/native @react-navigation/bottom-tabs
npx expo install react-native-screens react-native-safe-area-context
This installation might take a minute or two. When it finishes, you will have all the navigation power you need built into your project.
Part 2: Creating Your Project Structure
Now let's organize our project properly. Think of this like organizing a filing cabinet—everything has its place, making it easy to find what you need later. Inside your VeLi folder, create these new folders:
VeLi/
├── screens/
├── data/
└── assets/
└── foods/
You can create folders either through your code editor (VS Code has a "New Folder" option when you right-click in the file explorer) or through your terminal using these commands:
mkdir screens
mkdir data
mkdir -p assets/foods
The mkdir -p
command creates nested folders in one go, which is why we can create both assets and the foods folder inside it with a single command.
Part 3: Creating Your Data File
Before we build anything visual, we need data to display. Think of this as stocking your kitchen before you start cooking. Create a file called index.js
inside your data
folder and add this content:
// data/index.js
// This array holds all our vegetarian food items
// Each food is an object with properties that describe it
export const vegetarianFoods = [
{
id: 1,
name: "Avocado Salad",
primaryColor: "#2E7D32", // deep avocado green
type: "Salad",
image: require("./assets/foods/avocado.png")
},
{
id: 2,
name: "Vegan Burger",
primaryColor: "#e71414ff", // rich tomato red // earthy brown (bun-like, grounded)
type: "Main Dish",
image: require("./assets/foods/burger.png")
},
{
id: 3,
name: "Quinoa Bowl",
primaryColor: "#1B5E20", // dark leafy green
type: "Bowl",
image: require("./assets/foods/quinoa.png")
},
{
id: 4,
name: "Veggie Pizza",
primaryColor: "#e71414ff", // rich tomato red
type: "Pizza",
image: require("./assets/foods/pizza.png")
},
{
id: 5,
name: "Green Smoothie",
primaryColor: "#33691E", // forest green
type: "Drink",
image: require("./assets/foods/smoothie.png")
},
];
Get this images from the assets folder in my github repo: iws technical
image files. Each food has a unique color that we chose to match its natural appearance—greens for vegetables, orange for fruits. This creates visual harmony and helps users quickly recognize food categories.
Part 4: Understanding How Navigation Works
Before we write code, let me explain how React Navigation actually works, because understanding this will make everything else click into place.
Imagine your app as a building with multiple floors. In Stack Navigation (which you already know), moving between screens is like climbing stairs—you go up to a new screen, and to go back, you come down. You can only see one floor at a time.
Tab Navigation is different. Imagine instead that you have a building with three rooms all on the same floor, and you can instantly teleport between them using magic doors at the bottom of the building. Those magic doors are your tabs. Each tab is always there, waiting for you to tap it, and when you do, you instantly jump to that room (screen). Nothing is stacked—everything exists side by side.
React Navigation needs two main pieces to work:
The Navigation Container: This is like the building itself. It wraps everything and keeps track of where you are. Every navigation setup must start with this container wrapping your navigators.
The Tab Navigator: This is like the floor plan that defines your three rooms and the magic doors to access them. It knows which screens exist and how to display the tabs at the bottom.
Part 5: Building Your First Simple Screen
Let's start with something super simple so you can see results immediately. Create a file called HomeScreen.js
inside your screens
folder:
The flex: 1
property is crucial in React Native. It tells this View to expand and fill all available space. Without it, your View would shrink to just fit its content. The combination of justifyContent: 'center'
and alignItems: 'center'
creates perfect centering both vertically and horizontally.
Now create two more simple screens. Create FoodsScreen.js
:
And create ProfileScreen.js
:
These three screens are intentionally simple right now. We are building the navigation structure first, then we will come back and add the beautiful content. This is a smart way to develop—get the skeleton working first, then add the flesh.
Part 6: Creating Your Tab Navigator (The Magic Part!)
Now comes the exciting part where everything connects. Open your App.js
file (it should already exist in your project root from when Expo created the project) and replace everything in it with this code:
Let me explain what is happening here line by line, because this is the heart of your navigation system:
First, we import NavigationContainer
from React Navigation. This is a special component that wraps your entire navigation structure and manages the navigation state. Without it, none of the navigation would work. Think of it as the foundation of a house—invisible but absolutely essential.
Second, we import createBottomTabNavigator
, which is a function that creates a tab navigator object. When we call this function, it returns an object with two components inside: Navigator
and Screen
. That is why we can then use Tab.Navigator
and Tab.Screen
—they come from this object we created.
Inside our App component, we wrap everything in NavigationContainer
. This is mandatory—React Navigation requires this wrapper to function properly.
Inside the NavigationContainer, we place our Tab.Navigator
. This component creates the actual tab bar you will see at the bottom of the screen. It looks at all the Tab.Screen
components you put inside it and automatically creates a tab button for each one.
Each Tab.Screen
has two required props. The name
prop is like an ID—it is how you will refer to this screen when navigating (you will see this in action later). The component
prop tells React Navigation which component to render when this tab is active.
Now let's test it! Start your development server:
npx expo start
Press i
for iOS simulator, a
for Android emulator, or scan the QR code with your phone.
What you should notice immediately is that tapping different tabs instantly switches between screens. There is no sliding animation, no back button needed—just instant switching. This is the power of Tab Navigation. Each screen exists simultaneously, and tapping a tab just changes which one is visible.
Part 7: Adding Icons to Your Tabs
Right now your tabs just show text labels, which is functional but not very beautiful or intuitive. Let's add icons to make your app feel professional. Icons provide instant visual recognition—users can identify tabs at a glance without reading.
Update your App.js
to add icons:
// App.js
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
// Import icon libraries from Expo
import { Ionicons, MaterialIcons, FontAwesome5 } from '@expo/vector-icons';
import HomeScreen from './screens/HomeScreen';
import FoodsScreen from './screens/FoodsScreen';
import ProfileScreen from './screens/ProfileScreen';
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
// screenOptions is a function that runs for each screen
// It receives information about the current route
screenOptions={({ route }) => ({
// tabBarIcon is a function that returns the icon component
// It receives focused state, color, and size from React Navigation
tabBarIcon: ({ focused, color, size }) => {
// We use different icons based on which tab this is
if (route.name === 'Home') {
// For Home, we use a leaf icon - perfect for a vegetarian app!
// When focused (active), show solid leaf; when not, show outline
return (
<Ionicons
name={focused ? 'leaf' : 'leaf-outline'}
size={size}
color={color}
/>
);
} else if (route.name === 'Foods') {
// Restaurant menu icon for the Foods tab
return (
<MaterialIcons
name="restaurant-menu"
size={size}
color={color}
/>
);
} else if (route.name === 'Profile') {
// User icon for Profile, changes style when focused
return (
<FontAwesome5
name={focused ? 'user-alt' : 'user'}
size={size}
color={color}
/>
);
}
},
})}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Foods" component={FoodsScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
Let me explain the screenOptions
function because it can seem confusing at first. React Navigation calls this function for each screen in your navigator. It passes you information about that screen (in the route
parameter), and you return an object of options for that screen.
The tabBarIcon
property inside our options is itself a function. React Navigation calls this function whenever it needs to render a tab icon. It passes you three pieces of information: whether this tab is currently focused (active), what color to use, and what size to make the icon. You then return a React component—in our case, an icon component.
The beauty of this system is that React Navigation automatically handles the color changes and size—you just need to provide the icon. When a tab is active, React Navigation passes a different color (which we will customize in the next step).
The focused
parameter is particularly useful. We use it to show different icon styles—solid icons for active tabs, outline icons for inactive tabs. This provides clear visual feedback about where the user currently is in the app.
Save your file and look at your app. You should now see beautiful icons on each tab!
Part 8: Customizing Tab Colors and Appearance
Now let's make your tabs match VeLi's brand identity with our green color scheme. We want active tabs to be a vibrant green and inactive tabs to be gray, just like professional apps do. Update your screenOptions
:
// App.js
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Ionicons, MaterialIcons, FontAwesome5 } from '@expo/vector-icons';
import HomeScreen from './screens/HomeScreen';
import FoodsScreen from './screens/FoodsScreen';
import ProfileScreen from './screens/ProfileScreen';
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
if (route.name === 'Home') {
return (
<Ionicons
name={focused ? 'leaf' : 'leaf-outline'}
size={size}
color={color}
/>
);
} else if (route.name === 'Foods') {
return (
<MaterialIcons
name="restaurant-menu"
size={size}
color={color}
/>
);
} else if (route.name === 'Profile') {
return (
<FontAwesome5
name={focused ? 'user-alt' : 'user'}
size={size}
color={color}
/>
);
}
},
// These color settings apply to all tabs
// Active tab uses bright, vibrant green - catches the eye
tabBarActiveTintColor: '#4CAF50',
// Inactive tabs use neutral gray - fades into background
tabBarInactiveTintColor: '#8E8E93',
// The tab bar itself has a white background with subtle border
tabBarStyle: {
backgroundColor: '#FFFFFF',
borderTopColor: '#EAEAEA',
borderTopWidth: 1,
},
})}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Foods" component={FoodsScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
The tabBarActiveTintColor
and tabBarInactiveTintColor
properties control the colors of both the icons and labels. When you set these, React Navigation automatically applies them to the color
parameter it passes to your tabBarIcon
function. This is why we use color={color}
in our icon components—we are passing through the color that React Navigation calculated for us.
The color #4CAF50
is a bright, energetic green that I chose because it evokes growth, health, and nature—perfect for a vegetarian app. It is bright enough to clearly indicate "this tab is active" but not so bright that it feels harsh or aggressive.
The gray #8E8E93
for inactive tabs is Apple's standard system gray. It is carefully calibrated to be visible enough to see but muted enough to recede into the background, naturally drawing attention to the active green tab.
Part 9: Customizing the Header Bar
Right now, each screen has a plain white header at the top. Let's style these headers to match our VeLi brand. We will give them a rich green background that complements our tabs:
// App.js - Update your screenOptions
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
// Set Home as the initial screen users see when app opens
initialRouteName="Home"
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
if (route.name === 'Home') {
return (
<Ionicons
name={focused ? 'leaf' : 'leaf-outline'}
size={size}
color={color}
/>
);
} else if (route.name === 'Foods') {
return (
<MaterialIcons
name="restaurant-menu"
size={size}
color={color}
/>
);
} else if (route.name === 'Profile') {
return (
<FontAwesome5
name={focused ? 'user-alt' : 'user'}
size={size}
color={color}
/>
);
}
},
tabBarActiveTintColor: '#4CAF50',
tabBarInactiveTintColor: '#8E8E93',
tabBarStyle: {
backgroundColor: '#FFFFFF',
borderTopColor: '#EAEAEA',
borderTopWidth: 1,
},
// Header styling applies to the top bar of each screen
headerStyle: {
backgroundColor: '#1B5E20', // Deep forest green background
},
headerTintColor: '#fff', // White text and icons
headerTitleStyle: {
fontWeight: 'bold', // Makes the title stand out
fontSize: 18,
},
})}
>
{/* We can customize individual screen titles */}
<Tab.Screen
name="Home"
component={HomeScreen}
options={{ title: 'VeLi' }} // Custom title for Home screen
/>
<Tab.Screen
name="Foods"
component={FoodsScreen}
options={{ title: 'Vegetarian Foods' }}
/>
<Tab.Screen
name="Profile"
component={ProfileScreen}
options={{ title: 'My Profile' }}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
The headerStyle
property controls the appearance of the header container. By setting its background to our deep forest green #1B5E20
, we create a strong brand presence at the top of every screen.
The headerTintColor
controls the color of text and icons in the header. We set it to white for perfect contrast against the dark green background, ensuring readability.
The options
prop on individual Tab.Screen
components lets us customize that specific screen. Here we are setting custom titles—the Home screen shows just "VeLi" as the app name, while the other screens show descriptive titles.
The initialRouteName
prop tells React Navigation which screen to show first when the app launches. Without this, it would default to the first screen in the list (Home in our case), but it is good practice to be explicit.
Same for the profile
Part 10: Adding a Notification Badge
Many apps show little red circles with numbers on tabs to indicate notifications or new content. Let's add this professional touch to the Home tab:
// App.js - Update just the Home Tab.Screen
<Tab.Screen
name="Home"
component={HomeScreen}
options={{
title: 'VeLi',
// Badge shows a small red circle with a number
// This could represent unread messages, new recipes, etc.
tabBarBadge: 5,
}}
/>
The tabBarBadge
property automatically creates a small red circle with your number inside it, positioned at the top right of the tab icon. This is a standard iOS design pattern that users immediately recognize as "you have 5 things to check here." In a real app, this number would come from your state or database indicating actual new items.
Part 11: Making the Badge Dynamic (Conditional)
Static badges are good for learning, but in real apps, badges should appear and disappear based on actual notifications. Let's make ours dynamic using state:
// App.js
import React, { useState } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Ionicons, MaterialIcons, FontAwesome5 } from '@expo/vector-icons';
import HomeScreen from './screens/HomeScreen';
import FoodsScreen from './screens/FoodsScreen';
import ProfileScreen from './screens/ProfileScreen';
const Tab = createBottomTabNavigator();
export default function App() {
// State to track if we have notifications
// In a real app, this would connect to your notification system
const [hasNotifications, setHasNotifications] = useState(true);
const [notificationCount, setNotificationCount] = useState(5);
return (
<NavigationContainer>
<Tab.Navigator
initialRouteName="Home"
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
if (route.name === 'Home') {
return (
<Ionicons
name={focused ? 'leaf' : 'leaf-outline'}
size={size}
color={color}
/>
);
} else if (route.name === 'Foods') {
return (
<MaterialIcons
name="restaurant-menu"
size={size}
color={color}
/>
);
} else if (route.name === 'Profile') {
return (
<FontAwesome5
name={focused ? 'user-alt' : 'user'}
size={size}
color={color}
/>
);
}
},
tabBarActiveTintColor: '#4CAF50',
tabBarInactiveTintColor: '#8E8E93',
tabBarStyle: {
backgroundColor: '#FFFFFF',
borderTopColor: '#EAEAEA',
borderTopWidth: 1,
},
headerStyle: {
backgroundColor: '#1B5E20',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
fontSize: 18,
},
})}
>
<Tab.Screen
name="Home"
component={HomeScreen}
options={{
title: 'VeLi',
// Only show badge if hasNotifications is true
// undefined means no badge at all
tabBarBadge: hasNotifications ? notificationCount : undefined,
}}
/>
<Tab.Screen
name="Foods"
component={FoodsScreen}
options={{ title: 'Vegetarian Foods' }}
/>
<Tab.Screen
name="Profile"
component={ProfileScreen}
options={{ title: 'My Profile' }}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
Here we introduced React state using the useState
hook. The hasNotifications
state determines whether to show a badge at all, and notificationCount
determines what number to display. The expression hasNotifications ? notificationCount : undefined
is a ternary operator—it means "if hasNotifications is true, use notificationCount, otherwise use undefined." When React Navigation receives undefined
for tabBarBadge, it simply does not render any badge.
In a production app, you would connect this state to your actual notification system. When new messages arrive, you would call setHasNotifications(true)
and setNotificationCount(newCount)
. When the user views all notifications, you would call setHasNotifications(false)
.
Part 12: Your Complete Navigation Structure
At this point, you have a fully function tab navigation system! Let me show you the complete, final App.js
with all features working together:
// App.js - Complete Version
import React, { useState } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Ionicons, MaterialIcons, FontAwesome5 } from '@expo/vector-icons';
import HomeScreen from './screens/HomeScreen';
import FoodsScreen from './screens/FoodsScreen';
import ProfileScreen from './screens/ProfileScreen';
const Tab = createBottomTabNavigator();
export default function App() {
const [hasNotifications, setHasNotifications] = useState(true);
const [notificationCount, setNotificationCount] = useState(5);
return (
<NavigationContainer>
<Tab.Navigator
initialRouteName="Home"
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
if (route.name === 'Home') {
return (
<Ionicons
name={focused ? 'leaf' : 'leaf-outline'}
size={size}
color={color}
/>
);
} else if (route.name === 'Foods') {
return (
<MaterialIcons
name="restaurant-menu"
size={size}
color={color}
/>
);
} else if (route.name === 'Profile') {
return (
<FontAwesome5
name={focused ? 'user-alt' : 'user'}
size={size}
color={color}
/>
);
}
},
tabBarActiveTintColor: '#4CAF50',
tabBarInactiveTintColor: '#8E8E93',
tabBarStyle: {
backgroundColor: '#FFFFFF',
borderTopColor: '#EAEAEA',
borderTopWidth: 1,
},
headerStyle: {
backgroundColor: '#1B5E20',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
fontSize: 18,
},
})}
>
<Tab.Screen
name="Home"
component={HomeScreen}
options={{
title: 'VeLi',
tabBarBadge: hasNotifications ? notificationCount : undefined,
}}
/>
<Tab.Screen
name="Foods"
component={FoodsScreen}
options={{ title: 'Vegetarian Foods' }}
/>
<Tab.Screen
name="Profile"
component={ProfileScreen}
options={{ title: 'My Profile' }}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
Congratulations! You now have a complete understanding of how Tab Navigation works in React Native. You have built the navigation structure step by step, tested each piece, and seen exactly how everything fits together. In the next tutorial, we will build the beautiful content for each screen—the food cards, profile information, and interactive elements that make VeLi come alive.
Part 13: Building the Beautiful Home Screen with Food Cards
Now that your navigation structure is solid, let's build the actual content that makes VeLi come alive. We'll start with the Home screen, transforming it from simple text into an engaging gallery of vegetarian foods.
Understanding the Home Screen Architecture
The Home screen needs to accomplish several things:
- Display a welcoming header with the app's mission
- Show a hero cover image that sets the mood
- Present food cards that users can tap to view details
- Pass data between screens when a food is selected
Let's build this step by step.
Step 1: Update HomeScreen.js [Exercise 1]
Replace the entire contents of screens/HomeScreen.js
with this code:
// screens/HomeScreen.js
import React from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
TouchableOpacity,
Image,
StatusBar
} from 'react-native';
import { vegetarianFoods } from '../data';
const HomeScreen = ({ navigation }) => {
// This function handles when a user taps a food card
// It navigates to the Foods screen and passes the selected food data
const handleFoodPress = (food) => {
navigation.navigate('Foods', { selectedFood: food });
};
return (
<ScrollView style={styles.container}>
{/* StatusBar controls the top system bar appearance */}
<StatusBar barStyle="light-content" backgroundColor="#1B5E20" />
{/* Header section with app mission */}
<View style={styles.header}>
<Text style={styles.headerTitle}>Vegetarian Living</Text>
<Text style={styles.headerSubtitle}>Healthy • Fresh • Plant-Based</Text>
</View>
{/* Hero cover image here*/}
{/* Featured foods section */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Featured Dishes</Text>
{vegetarianFoods.map(food => (
<TouchableOpacity
key={food.id}
style={[styles.foodCard, { backgroundColor: food.primaryColor }]}
onPress={() => handleFoodPress(food)}
>
<Image source={food.image} style={styles.foodImage} />
<Text style={styles.foodName}>{food.name}</Text>
<Text style={styles.foodType}>{food.type}</Text>
</TouchableOpacity>
))}
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#FAFAFA', // Very light gray background
},
header: {
padding: 20,
backgroundColor: '#fff',
borderBottomWidth: 1,
borderBottomColor: '#EAEAEA', // Subtle border for depth
},
headerTitle: {
fontSize: 24,
fontWeight: '600',
color: '#1B5E20', // Our deep forest green for brand consistency
},
headerSubtitle: {
fontSize: 15,
color: '#444',
marginTop: 4,
},
//WRITE STYLES HERE PLEASE [EXCERCISE]
});
export default HomeScreen;
Key Concepts Explained
ScrollView: Unlike View
, ScrollView
makes content scrollable when it exceeds screen height. Essential for content-heavy screens.
navigation prop: React Navigation automatically passes this to all screen components. It contains methods like navigate()
for moving between screens.
Dynamic backgroundColor: Notice { backgroundColor: food.primaryColor }
inside the style array. This applies each food's unique color from our data, creating visual variety.
TouchableOpacity: This wrapper makes any component tappable and adds a subtle fade effect on press—standard iOS interaction pattern.
Step 2: Add the Cover Image
You need to add a cover image to your project. Create the file assets/foods/cover.png
. For now, you can:
- Get the images from the assets in the github repo provided below
- Resize it to around 800x600px (optional)
- Save it as
cover.png
inassets/foods/
Or use this terminal command to create a placeholder:
# This creates an empty file - replace it with a real image
touch assets/foods/cover.png
Step 3: Test the Home Screen
Run your app:
npx expo start
Part 14: Building the Foods Screen with Selection Highlighting
Now let's build the Foods screen that displays all vegetarian options and highlights whichever food the user selected from the Home screen.
Update FoodsScreen.js
Replace screens/FoodsScreen.js
with:
// screens/FoodsScreen.js
import React from 'react';
import {
View,
Text,
ScrollView,
Image,
StyleSheet
} from 'react-native';
import { vegetarianFoods } from '../data';
const FoodsScreen = ({ route }) => {
// route.params contains data passed from other screens
// We use optional chaining (?.) because params might be undefined
const selectedFood = route.params?.selectedFood;
return (
<ScrollView style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>All Vegetarian Foods</Text>
{/* Only show this text if a food was selected */}
{selectedFood && (
<Text style={styles.highlightText}>
Selected: {selectedFood.name}
</Text>
)}
</View>
<View style={styles.section}>
{vegetarianFoods.map(food => (
<View
key={food.id}
style={[
styles.foodCard,
{ backgroundColor: food.primaryColor },
// Add selected style if this food matches the selected one
selectedFood?.id === food.id && styles.selectedFood
]}
>
<Image source={food.image} style={styles.foodImage} />
<Text style={styles.foodName}>{food.name}</Text>
<Text style={styles.foodType}>{food.type}</Text>
</View>
))}
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
//Write styles here as exercise
});
export default FoodsScreen;
Navigation Data Flow
This demonstrates React Navigation's parameter passing:
- Home screen calls
navigation.navigate('Foods', { selectedFood: food })
- React Navigation passes this data as
route.params
to FoodsScreen - FoodsScreen reads
route.params.selectedFood
and highlights that card
The conditional styling selectedFood?.id === food.id && styles.selectedFood
applies the selected border only to the matching food card.
Part 15: Building the Profile Screen with Avatar
Now let's create the Profile screen with a user avatar and interactive buttons.
Update ProfileScreen.js
Replace screens/ProfileScreen.js
with:
// screens/ProfileScreen.js
import React from 'react';
import {
View,
Text,
TouchableOpacity,
StyleSheet,
Image,
Alert
} from 'react-native';
const ProfileScreen = ({ navigation }) => {
return (
<View style={styles.container}>
<View style={styles.profileContent}>
{/* Profile card with avatar */}
<View style={styles.profileCard}>
{/* Avatar image */}
<View style={styles.avatarContainer}>
<Image
source={{ uri: 'https://i.pravatar.cc/150?img=12' }}
style={styles.avatar}
/>
</View>
<Text style={styles.profileName}>Fonyuy Gita</Text>
<Text style={styles.profileEmail}>gita@veggieapp.com</Text>
<Text style={styles.profileInfo}>Vegetarian since 2020 🌱</Text>
</View>
{/* Settings button */}
<TouchableOpacity
style={styles.button}
onPress={() => Alert.alert("Settings", "Profile settings pressed!")}
>
<Text style={styles.buttonText}>Settings</Text>
</TouchableOpacity>
{/* Go to Home button */}
<TouchableOpacity
style={[styles.button, styles.secondaryButton]}
onPress={() => navigation.navigate('Home')}
>
<Text style={[styles.buttonText, styles.secondaryButtonText]}>
Go to Home
</Text>
</TouchableOpacity>
</View>
</View>
);
};
const styles = StyleSheet.create({
//write styles
});
export default ProfileScreen;
Design Choices
Avatar URL: We're using pravatar.cc, a service that provides placeholder profile pictures. The ?img=12
parameter selects a specific avatar style.
Button Hierarchy: Notice two button styles:
- Primary button (Settings): Solid green background, white text—high emphasis
- Secondary button (Go to Home): White background, green border and text—lower emphasis
This ensures the badge displays correctly based on state.
Part 16: Complete App Testing Flow
Now test the complete user journey:
- Launch → See Home screen with badge
- Tap Fruit Bowl card → Navigate to Foods screen, see "Selected: Fruit Bowl" with highlighted card
- Tap Profile tab → See profile with avatar
- Tap "Go to Home" button → Return to Home screen
- Tap Foods tab directly → See all foods without any selection
Congratulations! You've built a complete tab navigation app with data flow between screens. You now understand how to structure a React Native app, create tab navigators, pass data between screens, and build beautiful, interactive UIs.
Next Steps: In the upcoming tutorial, we'll add authentication screens (onboarding, login, signup) and introduce Expo Router for file-based navigation.
Top comments (0)