Quick Scan
You can utilize the `startFoodDetection` API to scan food items through the device Camera.
How can I implement the startFoodDetection API and retrieve food items through quick scan?
startFoodDetection API and retrieve food items through quick scan?/**
* Begin food detection using the device's camera.
* @param options - An object to determine which types of scanning should be performed.
* @param callback - A callback to repeatedly receive food detection events as they occur.
* @returns A `Subscription` that should be retained by the caller while food detection is running. Call `remove` on the subscription to terminate food detection.
*/
// Configuration for food detection
useEffect(() => {
const config: FoodDetectionConfig = {
detectBarcodes: true,
detectPackagedFood: true,
detectNutritionFacts: true,
};
// Start food detection and subscribe to events
const subscription = PassioSDK.startFoodDetection(
config,
handleFoodDetection
);
// Cleanup function to unsubscribe when the component unmounts
return () => subscription.remove();
}, []); // Empty dependency array to run the effect only once during component mount
Example
In this illustration, we establish the useQuickScan hook, which furnishes methods and data such as food, and use of DetectionCameraVieimport React from 'react';
useQuickScan
import { useEffect, useRef, useState, useCallback } from 'react';
import {
PassioSDK,
type PassioIDAttributes,
type FoodDetectionConfig,
type NutritionFacts,
type FoodDetectionEvent,
} from '@passiolife/nutritionai-react-native-sdk-v2';
import { getAlternateFoodItems } from '../utils';
/**
* Custom hook for handling quick food scanning using PassioSDK.
* It provides functions and state variables related to food detection and alternative food items.
*/
export const useQuickScan = () => {
// State variables
const [passioIDAttributes, setPassioIDAttributes] =
useState<PassioIDAttributes | null>(null);
const [alternative, setAlternativePassioIDAttributes] = useState<
PassioIDAttributes[] | null
>(null);
const [loading, setLoading] = useState(true);
const passioIDAttributesRef = useRef<PassioIDAttributes | null>(null);
const [nutritionFacts, setNutritionFacts] = useState<
NutritionFacts | undefined
>(undefined);
// Function to clear the scanning results
const onClearResultPress = () => {
setLoading(true);
passioIDAttributesRef.current = null;
setPassioIDAttributes(null);
setAlternativePassioIDAttributes(null);
};
useEffect(() => {
// Function to handle food detection events
const handleFoodDetection = async (detection: FoodDetectionEvent) => {
const { candidates , nutritionFacts} = detection;
if (
nutritionFacts !== undefined &&
nutritionFacts.servingSizeGram !== undefined
) {
setNutritionFacts(detection.nutritionFacts);
return;
}
if (!candidates) {
return;
}
let attributes: PassioIDAttributes | null = null;
// Determine the type of food detection and fetch attributes accordingly
if (candidates.barcodeCandidates?.[0]) {
const barcode = candidates.barcodeCandidates[0].barcode;
attributes = await PassioSDK.fetchAttributesForBarcode(barcode);
} else if (candidates.packagedFoodCode?.[0]) {
const packagedFoodCode = candidates.packagedFoodCode[0];
attributes =
await PassioSDK.fetchPassioIDAttributesForPackagedFood(
packagedFoodCode
);
} else if (candidates.detectedCandidates?.[0]) {
const passioID = candidates.detectedCandidates[0].passioID;
attributes = await PassioSDK.getAttributesForPassioID(passioID);
}
if (attributes === null) {
return;
}
// Check if the detected food is different from the previous one
if (attributes?.passioID !== passioIDAttributesRef.current?.passioID) {
passioIDAttributesRef.current = attributes;
// Update state variables and fetch alternative food items
setPassioIDAttributes((prev) => {
if (attributes?.passioID === prev?.passioID) {
return prev;
} else {
async function callAlternative() {
if (attributes) {
const alternativeItems =
await getAlternateFoodItems(attributes);
setAlternativePassioIDAttributes(alternativeItems);
}
}
callAlternative();
return attributes;
}
});
setLoading(false);
}
};
// Configuration for food detection
const config: FoodDetectionConfig = {
detectBarcodes: true,
detectPackagedFood: true,
detectNutritionFacts: true,
};
// Start food detection and subscribe to events
const subscription = PassioSDK.startFoodDetection(
config,
handleFoodDetection
);
// Cleanup function to unsubscribe when the component unmounts
return () => subscription.remove();
}, []); // Empty dependency array to run the effect only once during component mount
// Function to handle changes in alternative food items
const onAlternativeFoodItemChange = useCallback(
async (attribute: PassioIDAttributes) => {
const alternativeItems = await getAlternateFoodItems(attribute);
setAlternativePassioIDAttributes(alternativeItems);
setPassioIDAttributes(attribute);
passioIDAttributesRef.current = attribute;
},
[]
);
// Return the hook's public API
return {
loading,
passioIDAttributes,
onAlternativeFoodItemChange,
onClearResultPress,
alternative,
nutritionFacts
};
};
QuickScanningScreen
import { ActivityIndicator, StyleSheet, View } from 'react-native';
import { DetectionCameraView } from '@passiolife/nutritionai-react-native-sdk-v2';
import { blackBackgroundStyle } from './QuickScanningScreen.Style';
import { useQuickScan } from './useQuickScan';
import { QuickFoodResult } from './views/QuickFoodResult';
export const QuickScanningScreen = () => {
const {
loading,
passioIDAttributes,
onClearResultPress,
alternative,
onAlternativeFoodItemChange,
} = useQuickScan();
const styles = quickScanStyle();
return (
<View style={blackBackgroundStyle}>
<DetectionCameraView style={styles.detectionCamera} />
{loading ? <ActivityIndicator style={styles.loadingIndicator} /> : null}
{passioIDAttributes !== null ? (
<QuickFoodResult
attribute={passioIDAttributes}
onAlternativeFoodItemChange={onAlternativeFoodItemChange}
onClearResultPress={onClearResultPress}
alternativeAttributes={alternative}
/>
) : null}
</View>
);
};
const quickScanStyle = () =>
StyleSheet.create({
detectionCamera: {
flex: 1,
width: '100%',
},
loadingIndicator: {
backgroundColor: 'white',
minHeight: 100,
marginVertical: 30,
position: 'absolute',
bottom: 0,
right: 16,
left: 16,
},
});
QuickFoodResult
import React from 'react';
import { Image, Pressable, StyleSheet, Text, View } from 'react-native';
import {
IconSize,
PassioIconView,
type PassioIDAttributes,
} from '@passiolife/nutritionai-react-native-sdk-v2';
import AlternativeFood from '../AlternativeFood';
export interface QuickFoodResultProps {
attribute: PassioIDAttributes;
alternativeAttributes?: PassioIDAttributes[] | null;
onAlternativeFoodItemChange?: (item: PassioIDAttributes) => void;
onClearResultPress?: () => void;
}
export const QuickFoodResult = ({
attribute,
alternativeAttributes,
onAlternativeFoodItemChange,
onClearResultPress,
}: QuickFoodResultProps) => {
const styles = quickFoodResultStyle();
return (
<View style={styles.itemContainer}>
<View style={styles.foodResult}>
<PassioIconView
style={styles.itemIcon}
config={{
passioID: attribute.imageName ?? attribute.passioID,
iconSize: IconSize.PX180,
passioIDEntityType: attribute.entityType,
}}
/>
<Text style={styles.itemFoodName}>{attribute.name}</Text>
</View>
{alternativeAttributes && (
<AlternativeFood
alternative={alternativeAttributes}
onAlternativeFoodItemChange={onAlternativeFoodItemChange}
/>
)}
<Pressable style={styles.clearResult} onPress={onClearResultPress}>
<Image
resizeMode="contain"
source={require('../../../../assets/ic_close_white.png')}
/>
</Pressable>
</View>
);
};
const quickFoodResultStyle = () =>
StyleSheet.create({
itemContainer: {
position: 'absolute',
bottom: 0,
right: 0,
padding: 12,
marginHorizontal: 16,
backgroundColor: 'white',
marginVertical: 30,
left: 0,
},
foodResult: {
flexDirection: 'row',
alignItems: 'center',
},
itemFoodName: {
flex: 1,
textTransform: 'capitalize',
marginHorizontal: 8,
fontSize: 16,
},
itemIcon: {
height: 60,
width: 60,
},
clearResult: {
position: 'absolute',
top: -30,
right: 0,
},
});
AlternativeFood
import React, { useCallback } from 'react';
import { FlatList, Pressable, StyleSheet, Text } from 'react-native';
import {
IconSize,
PassioIconView,
type PassioIDAttributes,
} from '@passiolife/nutritionai-react-native-sdk-v2';
interface AlternativeFoodProps {
alternative: PassioIDAttributes[];
onAlternativeFoodItemChange?: (item: PassioIDAttributes) => void;
}
const AlternativeFood = ({
alternative,
onAlternativeFoodItemChange,
}: AlternativeFoodProps) => {
const styles = alternativeFoodStyle();
const renderItem = useCallback(
({ item }: { item: PassioIDAttributes }) => (
<Pressable
onPress={() => onAlternativeFoodItemChange?.(item)}
style={styles.itemContainer}
>
<PassioIconView
style={styles.itemIcon}
config={{
passioID: item.imageName ?? item.passioID,
iconSize: IconSize.PX180,
passioIDEntityType: item.entityType,
}}
/>
<Text style={styles.itemFoodName}>{item.name}</Text>
</Pressable>
),
[onAlternativeFoodItemChange, styles]
);
return (
<FlatList
horizontal
data={alternative}
renderItem={renderItem}
keyExtractor={(item) => item.passioID.toString()}
/>
);
};
const alternativeFoodStyle = () =>
StyleSheet.create({
itemContainer: {
marginHorizontal: 8,
alignItems: 'center',
marginVertical: 16,
flexDirection: 'row',
},
itemFoodName: {
marginHorizontal: 8,
flex: 1,
textTransform: 'capitalize',
fontSize: 12,
},
itemIcon: {
height: 24,
width: 24,
},
});
export default AlternativeFood;
Utility Methods
export async function getAlternateFoodItems(
passioIDAttributes: PassioIDAttributes
): Promise<PassioIDAttributes[]> {
const childrenAttributes = passioIDAttributes.children.map(
async ({ passioID }) => {
return PassioSDK.getAttributesForPassioID(passioID);
}
);
const siblingAttributes = passioIDAttributes.siblings.map(
async ({ passioID }) => {
return PassioSDK.getAttributesForPassioID(passioID);
}
);
const parentAttributes = passioIDAttributes.parents.map(
async ({ passioID }) => {
return PassioSDK.getAttributesForPassioID(passioID);
}
);
const combinedAttributes = [
...childrenAttributes,
...siblingAttributes,
...parentAttributes,
];
const attrs = await Promise.all(combinedAttributes);
return attrs.filter(notEmpty);
}Yeah. Added Quick Scan.
Result


Last updated