Skip to content

Commit af9a663

Browse files
feat: add barcode scanning demo screen
1 parent e2ad8b8 commit af9a663

File tree

4 files changed

+240
-1
lines changed

4 files changed

+240
-1
lines changed

apps/ExampleApp/app/navigators/AppNavigator.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type AppStackParamList = {
3535
ObjectDetection: Record<string, never>
3636
DocumentScanner: Record<string, never>
3737
TextRecognition: Record<string, never>
38+
BarcodeScanning: Record<string, never>
3839
// IGNITE_GENERATOR_ANCHOR_APP_STACK_PARAM_LIST
3940
}
4041

@@ -63,6 +64,7 @@ const AppStack = observer(function AppStack() {
6364
<Stack.Screen name="ObjectDetection" component={Screens.ObjectDetectionScreen} />
6465
<Stack.Screen name="DocumentScanner" component={Screens.DocumentScannerScreen} />
6566
<Stack.Screen name="TextRecognition" component={Screens.TextRecognitionScreen} />
67+
<Stack.Screen name="BarcodeScanning" component={Screens.BarcodeScanningScreen} />
6668
{/* IGNITE_GENERATOR_ANCHOR_APP_STACK_SCREENS */}
6769
</Stack.Navigator>
6870
)
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
import React, { FC, useState, useEffect, useCallback } from "react"
2+
import { observer } from "mobx-react-lite"
3+
import { ViewStyle, View, ImageStyle, TextStyle, ScrollView, Pressable } from "react-native"
4+
import { NativeStackScreenProps } from "@react-navigation/native-stack"
5+
import { AppStackScreenProps } from "../navigators"
6+
import { Text, Icon, ImageSelector, Screen } from "../components"
7+
import { useTypedNavigation } from "../navigators/useTypedNavigation"
8+
9+
import RNMLKitBarcodeScanningModule from "@infinitered/react-native-mlkit-barcode-scanning"
10+
import type { BarcodeScannerResult } from "@infinitered/react-native-mlkit-barcode-scanning"
11+
import { UseExampleImageStatus, SelectedImage } from "../utils/useExampleImage"
12+
13+
type BarcodeScanningScreenProps = NativeStackScreenProps<AppStackScreenProps<"BarcodeScanning">>
14+
15+
function DebugOutput({ data }: { data: unknown }) {
16+
const [expanded, setExpanded] = useState(false)
17+
18+
return (
19+
<View style={$debugContainer}>
20+
<Pressable onPress={() => setExpanded(!expanded)} style={$debugHeader}>
21+
<Text style={$debugTitle}>{expanded ? "▼" : "▶"} Debug Output</Text>
22+
</Pressable>
23+
{expanded && (
24+
<ScrollView style={$debugContent} horizontal>
25+
<ScrollView nestedScrollEnabled>
26+
<Text style={$debugText}>{JSON.stringify(data, null, 2)}</Text>
27+
</ScrollView>
28+
</ScrollView>
29+
)}
30+
</View>
31+
)
32+
}
33+
34+
export const BarcodeScanningScreen: FC<BarcodeScanningScreenProps> = observer(
35+
function BarcodeScanningScreen() {
36+
const navigation = useTypedNavigation<"BarcodeScanning">()
37+
38+
const [image, setImage] = useState<SelectedImage | null>(null)
39+
40+
const handleImageChange = useCallback((nextImage: SelectedImage) => {
41+
setImage(nextImage)
42+
}, [])
43+
44+
const [result, setResult] = useState<BarcodeScannerResult | null>(null)
45+
const [status, setStatus] = useState<
46+
"init" | "noPermissions" | "done" | "error" | "loading" | UseExampleImageStatus
47+
>("init")
48+
49+
const onStatusChange = React.useCallback(
50+
(status: "init" | "noPermissions" | "done" | "error" | "loading" | UseExampleImageStatus) => {
51+
setStatus(status)
52+
},
53+
[],
54+
)
55+
56+
const [isScanning, setIsScanning] = useState(false)
57+
58+
useEffect(() => {
59+
const scanBarcode = async () => {
60+
if (!image?.uri) return
61+
setIsScanning(true)
62+
try {
63+
const scanResult = await RNMLKitBarcodeScanningModule.process(image.uri)
64+
setResult(scanResult)
65+
setStatus("done")
66+
} catch (error) {
67+
console.error("Error scanning barcode:", error)
68+
setStatus("error")
69+
} finally {
70+
setIsScanning(false)
71+
}
72+
}
73+
74+
scanBarcode().then(() => null)
75+
}, [image])
76+
77+
const statusMessage = React.useMemo(() => {
78+
if (!image && status !== "init") {
79+
setStatus("init")
80+
}
81+
if (isScanning) {
82+
return "Scanning for barcodes..."
83+
}
84+
switch (status) {
85+
case "init":
86+
return "Take a photo or select one from your camera roll"
87+
case "noPermissions":
88+
return "You need to grant camera permissions to take a photo"
89+
case "takingPhoto":
90+
return "Taking photo..."
91+
case "selectingPhoto":
92+
return "Selecting photo..."
93+
case "done":
94+
return result?.barcodes.length
95+
? `Found ${result.barcodes.length} barcode${result.barcodes.length > 1 ? "s" : ""}!`
96+
: "No barcodes found"
97+
case "error":
98+
return "Error during scanning!"
99+
case "loading":
100+
return "Loading Example Images..."
101+
default:
102+
return ""
103+
}
104+
}, [result, image, status, isScanning])
105+
106+
const clearResults = useCallback(() => {
107+
setResult(null)
108+
}, [])
109+
110+
return (
111+
<Screen style={$root} preset="scroll" safeAreaEdges={["top", "bottom"]}>
112+
<View>
113+
<Icon icon={"back"} onPress={() => navigation.navigate("Home")} style={$backIcon} />
114+
<Text preset={"heading"} text="Barcode Scanning" />
115+
<Text style={$description}>Take a photo of a barcode and scan it.</Text>
116+
</View>
117+
<ImageSelector
118+
onImageChange={handleImageChange}
119+
onImageClear={clearResults}
120+
onStatusChange={onStatusChange}
121+
statusMessage={statusMessage}
122+
status={isScanning ? "loading" : status}
123+
isLoading={false}
124+
images={{
125+
filter: "all",
126+
groupBy: "label",
127+
}}
128+
/>
129+
130+
{result && result.barcodes.length > 0 && (
131+
<>
132+
<View style={$resultContainer}>
133+
<Text preset="subheading">Scanned Barcodes</Text>
134+
{result.barcodes.map((barcode, index) => (
135+
<View key={index} style={$barcodeItem}>
136+
<Text style={$barcodeLabel}>Format: {barcode.format}</Text>
137+
<Text style={$barcodeLabel}>Type: {barcode.valueType}</Text>
138+
{barcode.displayValue && (
139+
<Text style={$barcodeValue}>Value: {barcode.displayValue}</Text>
140+
)}
141+
{barcode.rawValue && barcode.rawValue !== barcode.displayValue && (
142+
<Text style={$barcodeRaw}>Raw: {barcode.rawValue}</Text>
143+
)}
144+
</View>
145+
))}
146+
</View>
147+
<DebugOutput data={result} />
148+
</>
149+
)}
150+
</Screen>
151+
)
152+
},
153+
)
154+
155+
const $root: ViewStyle = {
156+
flex: 1,
157+
padding: 16,
158+
display: "flex",
159+
flexDirection: "column",
160+
}
161+
const $backIcon: ImageStyle = { marginVertical: 8 }
162+
163+
const $description: TextStyle = {
164+
marginVertical: 8,
165+
color: "rgba(0,0,0,0.6)",
166+
}
167+
168+
const $resultContainer: ViewStyle = {
169+
width: "100%",
170+
borderWidth: 1,
171+
borderColor: "rgba(0,0,0,0.2)",
172+
borderRadius: 8,
173+
padding: 12,
174+
marginVertical: 16,
175+
}
176+
177+
const $barcodeItem: ViewStyle = {
178+
marginTop: 12,
179+
paddingTop: 12,
180+
borderTopWidth: 1,
181+
borderTopColor: "rgba(0,0,0,0.1)",
182+
}
183+
184+
const $barcodeLabel: TextStyle = {
185+
fontSize: 12,
186+
color: "rgba(0,0,0,0.6)",
187+
marginBottom: 4,
188+
}
189+
190+
const $barcodeValue: TextStyle = {
191+
fontSize: 16,
192+
fontWeight: "600",
193+
marginTop: 4,
194+
}
195+
196+
const $barcodeRaw: TextStyle = {
197+
fontSize: 12,
198+
fontFamily: "monospace",
199+
marginTop: 4,
200+
color: "rgba(0,0,0,0.5)",
201+
}
202+
203+
const $debugContainer: ViewStyle = {
204+
width: "100%",
205+
borderWidth: 1,
206+
borderColor: "rgba(0,0,0,0.2)",
207+
borderRadius: 8,
208+
marginBottom: 24,
209+
overflow: "hidden",
210+
}
211+
212+
const $debugHeader: ViewStyle = {
213+
padding: 12,
214+
backgroundColor: "rgba(0,0,0,0.05)",
215+
}
216+
217+
const $debugTitle: TextStyle = {
218+
fontWeight: "bold",
219+
}
220+
221+
const $debugContent: ViewStyle = {
222+
maxHeight: 300,
223+
padding: 12,
224+
backgroundColor: "rgba(0,0,0,0.02)",
225+
}
226+
227+
const $debugText: TextStyle = {
228+
fontFamily: "monospace",
229+
fontSize: 12,
230+
}

apps/ExampleApp/app/screens/HomeScreen/demoInfo.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,11 @@ export const DEMO_LIST: DemoInfo[] = [
5353
screen: "TextRecognition",
5454
image: TEXT_RECOGNITION,
5555
},
56+
{
57+
title: "Barcode Scanning",
58+
description: "Scan barcodes and QR codes from images",
59+
screen: "BarcodeScanning",
60+
image: FACE_HOLDER,
61+
},
5662
...PLATFORM_SPECIFIC_DEMOS,
5763
]

apps/ExampleApp/app/screens/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export * from "./ImageLabelingScreen"
77
export * from "./DocumentScannerScreen"
88
export { BOX_COLORS } from "./FaceDetectionScreen"
99
export * from "./ObjectDetectionScreen"
10-
export * from "./TextRecognitionScreen"
10+
export * from "./TextRecognitionScreen"
11+
export * from "./BarcodeScanningScreen"

0 commit comments

Comments
 (0)