-
Notifications
You must be signed in to change notification settings - Fork 0
NutriHub Mobile App Development Documentation
NutriHub is a mobile application designed to educate users on affordable food options, meal preparation, and nutrition. The app aims to help users find budget-friendly grocery options, learn nutritious meal preparation techniques, and connect with local food resources. Built with React Native and Expo, NutriHub offers a cross-platform solution for both iOS and Android devices.
- Food catalog with nutritional information and scoring
- Forum screen for posting recipes, nutritional tips and posts
- Login screen with user authentication
- Framework: React Native with Expo
- Language: TypeScript
src/
├── api/ # API service layer (planned)
├── assets/ # Static assets like images
├── components/ # Reusable UI components
│ ├── common/ # Generic UI components
│ ├── food/ # Food-specific components
│ └── forum/ # Forum-specific components
├── constants/ # Application constants
│ ├── foodConstants.ts # Food-related constants
│ ├── forumConstants.ts # Forum-related constants
│ └── theme.ts # Theme constants
├── context/ # React context providers
│ ├── AuthContext.tsx # Authentication context
│ └── ThemeContext.tsx # Theming context
├── hooks/ # Custom React hooks
│ ├── useForm.ts # Form handling hook
│ └── useFoodFilters.ts # Food filtering hook
├── navigation/ # Navigation configuration
│ ├── AppNavigator.tsx # Main app navigator
│ ├── MainTabNavigator.tsx # Tab navigation
│ └── types.ts # Navigation type definitions
├── screens/ # Application screens
│ ├── auth/ # Authentication screens
│ ├── food/ # Food-related screens
│ └── forum/ # Forum-related screens
├── services/ # Business logic services (planned)
├── types/ # TypeScript type definitions
│ └── types.ts # Common type definitions
└── utils/ # Utility functions
└── validation.ts # Form validation utilities
The app uses a hierarchical token-based design system defined in constants/theme.ts
:
-
Base Palette: Raw color values organized by color families:
export const PALETTE = { PRIMARY: { DEFAULT: '#0B7A5C', LIGHT: '#12A97F', DARK: '#06624A' }, SECONDARY: { DEFAULT: '#007AFF', LIGHT: '#4DA3FF', DARK: '#0055B3' }, // ...additional color definitions };
-
Semantic Tokens: Application-level tokens that map to UI contexts:
export const LIGHT_THEME: Theme = { background: PALETTE.AMBER.DEFAULT, surface: PALETTE.NEUTRAL.WHITE, text: PALETTE.NEUTRAL.GRAY_900, // ...additional semantic mappings };
-
Component-specific Values: Special purpose tokens for specific components.
The design system includes consistent definitions for:
- Colors (light and dark themes)
- Typography scales
- Spacing constants
- Border radius values
- Shadow styles
The ThemeContext
provides theme values and functionality throughout the app:
- Current theme type ('light' or 'dark')
- Theme colors based on current theme
- Pre-computed text styles
- Theme toggling functionality
A flexible button component with multiple variants, sizes, and states:
- Supports primary, secondary, outline, danger, success, and text variants
- Multiple size options (small, default, large)
- Optional icons with position control
- Loading state support
- Accessibility properties
<Button
title="Submit"
onPress={handleSubmit}
variant="primary"
size="large"
fullWidth
iconName="check"
iconPosition="left"
/>
A container component for displaying content with consistent styling:
- Optional header and footer sections
- Pressable capability
- Elevation/shadow options
- Customizable styling
<Card
header={<Text>Card Title</Text>}
footer={<Button title="Action" onPress={handleAction} />}
onPress={handleCardPress}
elevated
>
<Text>Card content</Text>
</Card>
A customizable text input component:
- Label and helper text support
- Error state display
- Icon support
- Password visibility toggle
- Clear button option
<TextInput
label="Email"
value={email}
onChangeText={setEmail}
error={errors.email}
iconName="email"
keyboardType="email-address"
/>
Displays food item information in multiple layout variants:
- List layout for compact display
- Grid layout for visual browsing
- Detailed layout for comprehensive information
- Shows nutrition score, dietary options, and price
- Supports touch interaction
<FoodItem
item={foodItem}
onPress={handleFoodPress}
variant="grid"
showNutritionScore
showDietaryOptions
showPrice
/>
Displays forum posts with interaction options:
- Author information and post date
- Content preview with "read more" functionality
- Tag badges
- Like, comment, and share actions
- Press interaction for post details
<ForumPost
post={post}
onPress={handlePostPress}
onLike={handlePostLike}
onComment={handlePostComment}
onShare={handlePostShare}
preview
showTags
/>
Handles user authentication:
- Email and password input with validation
- Error messaging
- Loading state display
- "Forgot password" and "Sign up" options
Displays the food catalog with filtering and sorting:
- Search functionality
- Layout toggle (grid/list)
- Sorting options
- Empty state handling
Shows the community forum:
- Post listing
- New post creation button
- Post interaction (like, comment, share)
Manages authentication state:
- User login and logout
- Registration (planned)
- Auth state persistence
- Loading and error states
Manages theme selection:
- Light/dark theme switching
- Theme persistence
- Theme value access
Handles form state, validation, and submission:
- Field values and errors
- Touched field tracking
- Validation rule application
- Form submission handling
const {
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isValid,
isSubmitting
} = useForm({
initialValues: { email: '', password: '' },
validationRules,
onSubmit: handleLogin
});
Manages food item filtering and sorting:
- Name filtering
- Category filtering
- Dietary option filtering
- Price range filtering
- Nutrition score filtering
- Sorting options
const {
filteredItems,
setNameFilter,
setCategoryFilter,
setDietaryOptions,
setPriceRange,
setSortOption
} = useFoodFilters(foodItems);
The app uses React Navigation with a structure consisting of:
-
AppNavigator: The root navigator that handles authentication state
- Shows LoginScreen when not logged in
- Shows MainTabNavigator when logged in
-
MainTabNavigator: Bottom tab navigation for main app sections
- Home tab
- Food tab
- Forum tab
Additional nested navigators will be implemented as needed for section-specific flows.
A structured API layer will be implemented to handle server communication:
- Base API client with authentication handling
- Endpoint-specific services
- Request/response typing
Planned screens to be implemented:
- Registration Screen
- Food Detail Screen
- Forum Post Detail Screen
- Forum Post Creation Screen
- User Profile Screen
Business logic will be extracted into dedicated service modules:
- Food Service (nutrition calculations, food filtering)
- Forum Service (post interactions, content filtering)
- User Service (profile management)
The application uses composition over inheritance for UI components:
- Base components (Button, Card, TextInput) are composed to create more complex components
- Component props allow for customization and extension
A consistent approach to theming and styling:
- Theme context for global theme access
- Semantic color naming for maintainability
- Consistent spacing and sizing scales
- Component-specific styling through props
A standardized approach to form handling:
- useForm hook for form state management
- Validation rules for field validation
- Consistent error messaging
Strong TypeScript typing throughout the application:
- Interface definitions for data models
- Type definitions for component props
- Type safety for navigation parameters
- Enum and const types for constants
This document provides guidelines for contributing to the NutriHub mobile app. It outlines the development workflow, coding standards, and best practices to ensure consistency and maintainability of the codebase. Following these guidelines will help maintain the extensibility of the application and make collaboration smoother for all team members.
To develop the NutriHub mobile app, you need the following tools:
- Node.js & npm: Current LTS version
- Expo CLI: For running and building the app
- Visual Studio Code (recommended): With React Native and TypeScript extensions
- Git: For version control
-
Clone the repository:
git clone https://github.com/bounswe/bounswe2025group9.git cd bounswe2025group9/mobile/nutrihub
-
Install dependencies:
npm install
-
Start the development server:
npx expo start
-
Run on a device or emulator:
- Scan the QR code with Expo Go on your device
- Press 'a' for Android emulator
- Press 'i' for iOS simulator
NutriHub follows a structured architecture with clear separation of concerns. Familiarize yourself with the Codebase Structure and Architecture section above for a detailed overview.
-
main
: protected branch - Feature branches: Created from
main
for individual features - See the git contribution guide wiki page for more detail
Use the following pattern for branch names:
-
feature/[feature-name]
: For new features -
bugfix/[bug-name]
: For bug fixes
Write clear, descriptive commit messages:
- Start with a capitalized verb
- Keep the first line under 50 characters
- Include a detailed description if necessary
- Reference issue numbers when applicable
Example:
Add forum post creation screen
- Implement form for creating new posts
- Add validation for required fields
- Include tag selection functionality
Fixes #123
- Create a pull request from your feature branch to
develop
- Fill out the PR template with:
- Description of changes
- Screenshot/video (for UI changes)
- Testing performed
- Related issues
- Request reviews from team members
- Address review comments
- Merge once approved and CI checks pass
- Use TypeScript for all new files
- Define interfaces for all data models
- Use type annotations for function parameters and return types
- Avoid using
any
type - Use union types for variables with multiple potential types
// Good
interface User {
id: number;
name: string;
email: string;
}
function getUser(id: number): User | null {
// implementation
}
// Avoid
function getUser(id): any {
// implementation
}
- Use functional components with hooks
- Extract reusable logic into custom hooks
- Break down complex components into smaller, focused components
- Use PascalCase for component names
- Use camelCase for variables, functions, and instances
// Good
const UserProfile: React.FC<UserProfileProps> = ({ user }) => {
const { theme } = useTheme();
return (
<View style={{ backgroundColor: theme.background }}>
{/* component content */}
</View>
);
};
// Avoid
function userProfile(props) {
const theme = useTheme().theme;
return (
<View style={{ backgroundColor: theme.background }}>
{/* component content */}
</View>
);
}
- One component per file
- Place related components in the same directory
- Use index files to export multiple components from a directory
- Keep files relatively small (under 300 lines when possible)
- Files: Use PascalCase for components, camelCase for utilities
-
Components: Use PascalCase (e.g.,
Button.tsx
) -
Hooks: Use camelCase with
use
prefix (e.g.,useForm.ts
) -
Contexts: Use PascalCase with
Context
suffix (e.g.,ThemeContext.tsx
) -
Utilities: Use camelCase (e.g.,
validation.ts
)
Organize imports in the following order:
- React/React Native imports
- Third-party library imports
- Absolute imports from the project
- Relative imports
Separate each group with a blank line.
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { SPACING } from '../../constants/theme';
import { useTheme } from '../../context/ThemeContext';
import Button from '../common/Button';
import TextInput from '../common/TextInput';
Always use the theme context to access theme values:
// Good
const { theme, textStyles } = useTheme();
<View style={{ backgroundColor: theme.background }}>
<Text style={[styles.title, textStyles.heading2]}>Title</Text>
</View>
// Avoid
<View style={{ backgroundColor: '#FEFCE8' }}>
<Text style={[styles.title, { fontSize: 28, fontWeight: 'bold' }]}>Title</Text>
</View>
When adding new theme values:
- Add the property to the
Theme
interface inconstants/theme.ts
- Add the value to both
LIGHT_THEME
andDARK_THEME
objects - Document the purpose of the new theme property
// Add to Theme interface
export interface Theme {
// existing properties
newProperty: string;
}
// Add to both theme objects
export const LIGHT_THEME: Theme = {
// existing properties
newProperty: PALETTE.PRIMARY.LIGHT,
};
export const DARK_THEME: Theme = {
// existing properties
newProperty: PALETTE.PRIMARY.DARK,
};
When creating a new component:
- Place it in the appropriate directory (
common
,food
,forum
, etc.) - Define a clear props interface with JSDoc comments
- Use the theme system for styling
- Include comprehensive JSDoc documentation
- Export the component as the default export
/**
* CustomComponent
*
* Description of what the component does.
*
* Usage:
* ```tsx
* <CustomComponent
* property="value"
* onEvent={handleEvent}
* />
* ```
*/
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { useTheme } from '../../context/ThemeContext';
import { SPACING } from '../../constants/theme';
interface CustomComponentProps {
/**
* Description of the property
*/
property: string;
/**
* Event handler for when something happens
*/
onEvent?: () => void;
}
const CustomComponent: React.FC<CustomComponentProps> = ({
property,
onEvent,
}) => {
const { theme, textStyles } = useTheme();
return (
<View style={[styles.container, { backgroundColor: theme.surface }]}>
<Text style={textStyles.body}>{property}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
padding: SPACING.md,
},
});
export default CustomComponent;
When extending an existing component:
- Maintain backward compatibility
- Add new props as optional
- Document new functionality
- Update unit tests
For each component:
- Test primary functionality
- Test edge cases
- Test accessibility features
When creating a new screen:
- Place it in the appropriate directory (
auth
,food
,forum
, etc.) - Use existing components when possible
- Keep business logic separate from UI
- Add the screen to the appropriate navigator
/**
* NewScreen
*
* Description of what the screen does.
*/
import React, { useState, useCallback } from 'react';
import { View, StyleSheet } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useTheme } from '../../context/ThemeContext';
import { SPACING } from '../../constants/theme';
// Import components
import Button from '../../components/common/Button';
import TextInput from '../../components/common/TextInput';
const NewScreen: React.FC = () => {
const { theme, textStyles } = useTheme();
// Implementation...
return (
<SafeAreaView style={[styles.container, { backgroundColor: theme.background }]}>
{/* Screen content */}
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
// Additional styles...
});
export default NewScreen;
When implementing navigation:
- Define types for route parameters
- Use type-safe navigation
- Add meaningful transition animations when appropriate
// In types.ts
export type StackParamList = {
Home: undefined;
Details: { id: number; title: string };
};
// In component
import { useNavigation, RouteProp } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack';
import { StackParamList } from '../navigation/types';
type DetailsScreenNavigationProp = StackNavigationProp<StackParamList, 'Details'>;
type DetailsScreenRouteProp = RouteProp<StackParamList, 'Details'>;
const Component: React.FC = () => {
const navigation = useNavigation<DetailsScreenNavigationProp>();
const handlePress = () => {
navigation.navigate('Details', { id: 123, title: 'Item Title' });
};
// Implementation...
};
- Use JSDoc comments for functions, components, and interfaces
- Document all props for components
- Include usage examples for complex components
- Explain non-obvious code with inline comments
/**
* Calculates a nutrition score based on nutritional content.
*
* The score is calculated using:
* - 30% from protein content
* - 30% from carbohydrate quality
* - 40% from overall nutrient balance
*
* @param {NutritionData} data - The nutritional data
* @returns {number} A score from 0-10, with 10 being the highest quality
*/
function calculateNutritionScore(data: NutritionData): number {
// Implementation...
}
When making changes:
- Update relevant documentation to reflect your changes
- Add documentation for new features
- Update examples if APIs change
- Update the README if necessary
- Use
useMemo
to memoize expensive calculations - Use
useCallback
to prevent unnecessary re-renders - Use
React.memo
for pure components - Use
FlatList
for rendering long lists - Optimize images and assets
// Memoize expensive calculations
const expensiveValue = useMemo(() => {
return someExpensiveCalculation(a, b);
}, [a, b]);
// Memoize callback functions
const handlePress = useCallback(() => {
// handle press logic
}, [dependency]);
- Avoid defining functions inside render methods
- Avoid creating new objects/arrays in render
- Use proper dependency arrays in hooks
- Minimize the use of
useEffect
When making significant changes to the codebase:
- Update the Codebase Structure document if architecture changes
- Update component usage examples if APIs change
- Update this Contribution Guide if processes change
- Add documentation for new features
-
Metro bundler issues: Clear cache with
npx expo start --clear
- Dependency conflicts: Check for version mismatches in package.json
- TypeScript errors: Ensure types are correctly defined
- React Native version issues: Ensure Expo SDK compatibility
If you encounter issues:
- Check existing documentation
- Search project issues on GitHub
- Ask in the team communication channel
- Create an issue if no solution is found


-
Use Cases:
- Template
- Scenario 1: Community Sharing
- Scenario 2: For a Dietitian
- Scenario 3: Maintaining a Personal Inventory (as a Producer)
- Scenario 4: Proposing a Product and Adding a Recipe to Weekly Meal Plan
- Scenario 5: Creating a Meal Plan
- Scenario 6: Resetting Password
- Scenario 7: Moderator Actions
- Scenario 8: Searching for and Proposing a New Food Item
- Scenario 9: Budget-Conscious Grocery Shopping
- Scenario 10: Creating a New Store
- Scenario 11: User Profile Management
- Git: In a Nutshell
- Example GitHub Repositories
- System Modeling & UML
- Frontend Tutorial
- Frontend Roadmap
- Frontend Styling Guide
- Docker Summary
- Writing Endpoints
- Yusuf AKIN
- Arda SAYGAN
- Fatih Furkan Bilsel
- Berk GOKTAS
- Berkay BILEN
- Yusuf Anıl YAZICI
- Taha Topaloglu
- Nuri Basar
- Onur Kucuk
- Hasancan Keles
- Mete Damar