Food Detail
To display the food details, passioFoodItem
is required.
We start by defining a FoodNutrient interface, which will represent individual nutrient information:
export interface FoodNutrient {
title: string
value: number
unit: string
}
interface Props {
passioFoodItem: PassioFoodItem
}
This defines the structure of each nutrient, with a title
for its name, a value
for its numerical data, and a unit
to specify the measurement unit (e.g., grams, calories).
We create a state variable, foodNutrients
, using React's useState
hook to hold an array of FoodNutrient
objects:
const [foodNutrients, setFoodNutrients] = useState<FoodNutrient[]>([])
Extracting Nutrients with useEffect
useEffect
We use useEffect
to trigger the extraction of nutrient information when the passioFoodItem
changes. The PassioSDK.getNutrientsOfPassioFoodItem()
method is used to get the nutrients based on the food item and its weight:
useEffect(() => {
function init() {
setFoodNutrients(
extractFoodNutrients(
PassioSDK.getNutrientsOfPassioFoodItem(
passioFoodItem,
passioFoodItem.amount.weight
)
)
)
}
init()
}, [passioFoodItem])
he extractFoodNutrients()
is a utility function that processes the raw data returned from the SDK and formats it into the FoodNutrient
structure.
function extractFoodNutrients(
passioNutrients: PassioNutrients | null
): FoodNutrient[] {
if (passioNutrients == null) {
return []
}
return Object.entries(passioNutrients)
.map(([title, { value, unit }]) => ({
title,
value: value,
unit,
}))
.filter((item) => item.title !== 'weight')
}
Rendering the Macro-Nutrients
Next, we build a UI component to display macro-nutrients like calories, carbohydrates, protein, and fat. In the renderMacro
function, we map through a predefined array of macro-nutrients, search for the corresponding nutrient in the foodNutrients
array, and display its value:
const renderMacro = () => {
return (
<View
style={{
flexDirection: "row",
flex: 1,
}}
>
{["Calories", "Carbs", "Protein", "Fat"].map((title) => {
const nutrient = nutrients.find(
(item) => item.title.toLowerCase() === title.toLowerCase()
);
return (
<View
key={title}
style={{
flex: 1,
marginTop: 12,
}}
>
<Text
style={{
fontSize: 16,
fontWeight: "600",
}}
>
{title}
</Text>
<Text>{(nutrient?.value ?? 0).toFixed(2)}</Text>
</View>
);
})}
</View>
);
};
Rendering the Micro-Nutrients
const renderNutrientItem = () => (
<View
style={{
backgroundColor: "white",
padding: 16,
}}
>
<Text
style={{
fontWeight: "500",
color: "black",
fontSize: 16,
marginBottom: 12,
flex: 1,
}}
>
Nutrients
</Text>
<FlatList
data={foodNutrients.filter((item) => item.value >= 1)}
renderItem={({ item }) => {
return (
<View
style={{
flexDirection: "row",
marginVertical: 4,
}}
>
<Text
style={{
fontWeight: "400",
color: "black",
textTransform: "capitalize",
flex: 1,
}}
>
{getNutrientName[item.title] ?? item.title}
</Text>
<Text>
{Math.round(item.value)}
{" " + item.unit}
</Text>
</View>
);
}}
/>
</View>
);
Rendering Food Information
The renderFoodDetailCard
function is a React Native component that generates a card UI displaying detailed information about a food item. It features an icon, the food item's name, and additional information based on the food item's attributes.
Icon Display:
The icon of the food item is displayed within a circular
View
. ThePassioIconView
component is used to render the icon based on theiconId
andiconSize
properties.
Food Name:
The food item's name is displayed in a
Text
component, styled for clarity with padding, font size, weight, and text transformation.
Additional Information:
The
renderMacro()
function is called to render macronutrient information.If
passioFoodItem.isOpenFood
is true, therenderOpenFood()
function is called to display additional details relevant to open food items.
const renderFoodDetailCard = () => {
return (
<View
style={{
borderRadius: 16,
backgroundColor: "white",
padding: 16,
}}
>
<View
style={{
flexDirection: "row",
alignSelf: "center",
justifyContent: "center",
alignItems: "center",
flex: 1,
}}
>
<View
style={{
overflow: "hidden",
height: 60,
width: 60,
borderRadius: 30,
}}
>
<PassioIconView
style={{
height: 60,
width: 60,
}}
config={{
passioID: passioFoodItem.iconId,
iconSize: IconSize.PX90,
}}
/>
</View>
<View
style={{
flex: 1,
}}
>
<Text
style={{
paddingHorizontal: 16,
fontSize: 16,
textTransform: "capitalize",
fontWeight: "600",
}}
>
{passioFoodItem.name}
</Text>
</View>
</View>
{renderMacro()}
</View>
);
};
Edit Serving Size
React Native component that generates a user interface for editing serving sizes of a food item. It displays the current serving size, provides an input field for updating the quantity, and offers selectable serving units.
The structure of passioFoodItem
is determined by the properties of prop.passioFoodItem
, which could include fields such as:
name
: The name of the food item.iconId
: An identifier for the food item's icon.amount
: An object containing serving sizes and units.isOpenFood
: A boolean indicating whether the food item is an open food.
const [passioFoodItem, setPassioFoodItem] = useState(({ ...prop.passioFoodItem }))
onServingQuantityChange && onServingSizeSelect
The onServingQuantityChange
function is a callback that handles changes to the serving quantity input. It updates the state of the input and recalculates the weight based on the quantity entered by the user. This function is optimized with useCallback
to prevent unnecessary re-renders.
Purpose
To manage the input for serving quantities, ensuring that the food item's state is updated dynamically based on user input. It calculates the corresponding weight based on the selected serving unit and updates the passioFoodItem
state accordingly.
const onServingQuantityChange = useCallback(
(value: string) => {
setTextInput(value)
const newQuantity = Number(value.length > 0 ? value : 1)
const weight =
passioFoodItem.amount.servingUnits?.filter(
(i) => i.unitName === passioFoodItem.amount.selectedUnit
)[0].value ?? 1
setPassioFoodItem((foodItem) => {
foodItem.amount.weight.value = weight * newQuantity
foodItem.amount.selectedQuantity = newQuantity
return {
...foodItem,
}
})
},
[passioFoodItem.amount.selectedUnit, passioFoodItem.amount.servingUnits]
)
const onServingSizeSelect = useCallback(
(value: ServingUnit) => {
const defaultWeight = passioFoodItem.amount.weight.value ?? 0
const newQuantity = Number((defaultWeight / value.value).toFixed(2))
setTextInput((newQuantity ?? 1).toString())
setPassioFoodItem((foodItem) => {
foodItem.amount.selectedUnit = value.unitName
foodItem.amount.selectedQuantity = newQuantity
return {
...foodItem,
}
})
},
[passioFoodItem.amount.weight.value]
)
Render View of Edit Serving
Serving Size Display:
A
Text
component displays the serving sizes, formatted to show the calculated weight and its unit. The text is styled for emphasis.
Serving Quantity Input:
A
TextInput
component allows users to input the serving quantity. It is styled with borders, padding, and a placeholder that indicates the expected input.
Selectable Serving Units:
A
FlatList
displays the available serving units horizontally. Each item in the list is rendered as aPressable
, allowing users to select a unit.The background color of the selected unit changes to blue, and the text color changes to white for clarity.
calculatedWeight: passioFoodItem.amount?.weight.value,
calculatedWeightUnit: passioFoodItem?.amount?.weight?.unit,
selectedServingUnit: passioFoodItem?.amount.selectedUnit,
const renderEditServing = () => {
return (
<View
style={{
backgroundColor: "white",
padding: 16,
}}
>
<Text
style={{
fontWeight: "500",
color: "black",
fontSize: 16,
marginBottom: 12,
flex: 1,
}}
>{`Serving Sizes (${Math.round(
calculatedWeight ?? 0
)} ${calculatedWeightUnit})`}</Text>
<TextInput
value={textInputServingQty.toString()}
style={{
borderColor: "rgba(209, 213, 219, 1)",
backgroundColor: "rgba(238, 242, 255, 1)",
borderWidth: 1,
paddingHorizontal: 16,
borderRadius: 16,
padding: 16,
marginVertical: 16,
}}
keyboardType="numeric"
placeholder="Serving Size"
placeholderTextColor={"gray"}
onChangeText={onServingQuantityChange}
/>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={passioFoodItem.amount.servingUnits}
renderItem={({ item }) => (
<Pressable
onPress={() => onServingSizeSelect(item)}
style={[
styles.servingContainer,
selectedServingUnit === item.unitName && {
backgroundColor: "blue",
},
]}
>
<Text
style={[
{
fontWeight: "400",
color: "black",
fontSize: 13,
paddingVertical: 6,
overflow: "hidden",
textTransform: "capitalize",
},
selectedServingUnit === item.unitName && {
color: "white",
},
]}
>
{item.unitName}
</Text>
</Pressable>
)}
/>
</View>
);
};
Show Ingredients
Ingredients
The renderIngredient
function is a React Native component that displays a list of ingredients for a food item. It checks if the passioFoodItem
has ingredients and renders a list of them, including each ingredient's name, amount, and icon.
FlatList:
A
FlatList
is used to display each ingredient.Each ingredient is represented by a row, consisting of:
An icon (
PassioIconView
) displaying the ingredient’siconId
.The name of the ingredient.
The selected unit or amount of the ingredient.
Render Item:
For each ingredient, a row is rendered, showing:
The ingredient’s icon.
The ingredient’s name.
The selected unit of the ingredient, such as its amount.
const renderIngredient = () => {
return (
<View
style={{
backgroundColor: "white",
padding: 16,
}}
>
{passioFoodItem.ingredients &&
passioFoodItem.ingredients.length > 1 && (
<>
<Text style={styles.title}>Ingredients</Text>
<FlatList
data={passioFoodItem.ingredients}
keyExtractor={(item, index) => item.toString() + index}
renderItem={({ item }) => (
<View
style={{
flexDirection: "row",
marginVertical: 4,
padding: 8,
backgroundColor: "rgba(238, 242, 255, 1)",
}}
>
<View
style={{
overflow: "hidden",
height: 60,
width: 60,
borderRadius: 30,
}}
>
<PassioIconView
style={{
height: 60,
width: 60,
}}
config={{
passioID: item.iconId,
iconSize: IconSize.PX360,
}}
/>
</View>
<View
style={{
alignSelf: "center",
}}
>
<Text
style={{
marginHorizontal: 16,
fontWeight: "500",
fontSize: 14,
}}
>
{item.name}
</Text>
<Text
style={{
marginHorizontal: 16,
}}
>
{item.amount?.selectedUnit}{" "}
</Text>
</View>
</View>
)}
/>
</>
)}
</View>
);
};
Check out the full code here. FULL CODE
Last updated