Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8122141
wip: copied module structure over, started working on types
coolsoftwaretyler Dec 4, 2025
e4e65a3
fix: scaffold with create expo module, add placeholder doc
coolsoftwaretyler Dec 5, 2025
3dcaf71
wip: start adding more types to barcode interface
coolsoftwaretyler Dec 5, 2025
b95acd9
feat: finish barcode types on TS side
coolsoftwaretyler Dec 5, 2025
0f12bdf
fix: add other root level files to module, use core for types
coolsoftwaretyler Dec 5, 2025
449103a
chore: remove unused Expo files
coolsoftwaretyler Dec 5, 2025
4df6399
docs: clean up readme
coolsoftwaretyler Dec 5, 2025
10cf3c4
fix: add gradle dependencies for expo and mlkit core
coolsoftwaretyler Dec 5, 2025
f0c5159
fix: add podspec dependencies for MLKit and MLKitCore
coolsoftwaretyler Dec 6, 2025
84f02d6
refactor: rename to RNMLKitBarcodeScanning
coolsoftwaretyler Dec 6, 2025
718fbd0
fix: some metadata in podspec and module config
coolsoftwaretyler Dec 6, 2025
c9629f1
fix: readme formatting
coolsoftwaretyler Dec 6, 2025
c4465ef
refactor: rename files to RNMLKit
coolsoftwaretyler Dec 6, 2025
8dc6687
chore: remove unused view files
coolsoftwaretyler Dec 6, 2025
9cd87bb
fix: use the correct google dep for ios
coolsoftwaretyler Dec 6, 2025
f028c22
fix: nclude correct barcode-scanning in gradle
coolsoftwaretyler Dec 6, 2025
e2ad8b8
feat: add barcode scanning module to ex app deps
coolsoftwaretyler Dec 12, 2025
af9a663
feat: add barcode scanning demo screen
coolsoftwaretyler Dec 12, 2025
46b17be
fix: index file not found
coolsoftwaretyler Dec 12, 2025
557f606
feat(barcode-scanning): Android implementation (#249 by @frankcalise)
frankcalise Jan 13, 2026
e40b1bc
fix(barcode-scanner): initialization options (#256 by @frankcalise)
frankcalise Jan 19, 2026
8ca8095
Feat/tw barcode scanning swift (#257)
coolsoftwaretyler Jan 20, 2026
5dd0b74
feat: implement barcode scanning options in Swift
coolsoftwaretyler Jan 20, 2026
4f427ab
fix: implement rawData on Swift side
coolsoftwaretyler Jan 20, 2026
0311bac
fix: incldue geo case for valueTypeToString
coolsoftwaretyler Jan 20, 2026
682cb28
refactor: rename encryptionType to type for consistency
coolsoftwaretyler Jan 20, 2026
aa8e9a3
fix: add issuingDate to Swift DL implementation
coolsoftwaretyler Jan 20, 2026
00cf87a
fix: cleanup unused file
coolsoftwaretyler Jan 27, 2026
2a1958f
test: add placeholder test for barcode scanner
coolsoftwaretyler Jan 27, 2026
140213a
docs: add docs for barcode scanning
coolsoftwaretyler Jan 27, 2026
62c1933
docs(changeset): add react native mlkit barcode scanning module
coolsoftwaretyler Jan 27, 2026
47735a9
fix: android barcode scanning (#258 by @frankcalise)
frankcalise Feb 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/sour-wombats-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@infinitered/react-native-mlkit-barcode-scanning": minor
"example-app": minor
---

add react native mlkit barcode scanning module
3 changes: 2 additions & 1 deletion apps/ExampleApp/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
{
"photosPermission": "This app uses the photo library to select images for Machine Learning purposes. i.e. Object and Image detection."
}
]
],
"expo-asset"
],
"experiments": {
"tsconfigPaths": true
Expand Down
223 changes: 223 additions & 0 deletions apps/ExampleApp/app/components/CameraModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
import React, { useRef, useState } from "react"
import {
Modal,
View,
TouchableOpacity,
ActivityIndicator,
ViewStyle,
TextStyle,
} from "react-native"
import { useSafeAreaInsets } from "react-native-safe-area-context"
import { CameraView, useCameraPermissions, CameraType } from "expo-camera"
import { Text } from "./Text"
import { Button } from "./Button"
import { colors, spacing } from "../theme"

interface CameraModalProps {
visible: boolean
onCapture: (uri: string) => void
onClose: () => void
}

export function CameraModal({ visible, onCapture, onClose }: CameraModalProps) {
const cameraRef = useRef<CameraView>(null)
const [permission, requestPermission] = useCameraPermissions()
const [isCapturing, setIsCapturing] = useState(false)
const [facing, setFacing] = useState<CameraType>("back")
const insets = useSafeAreaInsets()

const toggleFacing = () => {
setFacing((current) => (current === "back" ? "front" : "back"))
}

const handleCapture = async () => {
if (!cameraRef.current || isCapturing) return

setIsCapturing(true)
try {
const photo = await cameraRef.current.takePictureAsync({
quality: 0.5,
})
if (photo?.uri) {
onCapture(photo.uri)
}
} catch (error) {
console.error("Error capturing photo:", error)
} finally {
setIsCapturing(false)
}
}

if (!permission) {
return null
}

if (!permission.granted) {
return (
<Modal visible={visible} animationType="slide" onRequestClose={onClose}>
<View style={[$container, { paddingTop: insets.top, paddingBottom: insets.bottom }]}>
<View style={$permissionContainer}>
<Text style={$permissionText}>Camera permission is required to take photos.</Text>
<Button
text="Grant Permission"
preset="reversed"
onPress={requestPermission}
style={$permissionButton}
/>
<Button
text="Cancel"
preset="default"
onPress={onClose}
style={$cancelButton}
textStyle={$cancelButtonText}
/>
</View>
</View>
</Modal>
)
}

return (
<Modal visible={visible} animationType="slide" onRequestClose={onClose}>
<View style={$container}>
<CameraView ref={cameraRef} style={$camera} facing={facing}>
<View style={[$overlay, { paddingTop: insets.top, paddingBottom: insets.bottom }]}>
<View style={$topBar}>
<TouchableOpacity style={$closeButton} onPress={onClose}>
<Text style={$closeButtonText}>✕</Text>
</TouchableOpacity>
<TouchableOpacity style={$flipButton} onPress={toggleFacing}>
<Text style={$flipButtonText}>⟲</Text>
</TouchableOpacity>
</View>
<View style={$bottomBar}>
<TouchableOpacity
style={[$captureButton, isCapturing && $captureButtonDisabled]}
onPress={handleCapture}
disabled={isCapturing}
>
{isCapturing ? (
<ActivityIndicator color={colors.palette.neutral900} />
) : (
<View style={$captureButtonInner} />
)}
</TouchableOpacity>
</View>
</View>
</CameraView>
</View>
</Modal>
)
}

const $container: ViewStyle = {
flex: 1,
backgroundColor: colors.palette.neutral900,
}

const $camera: ViewStyle = {
flex: 1,
}

const $overlay: ViewStyle = {
flex: 1,
justifyContent: "space-between",
}

const $topBar: ViewStyle = {
flexDirection: "row",
justifyContent: "space-between",
padding: spacing.md,
}

const $flipButton: ViewStyle = {
width: 44,
height: 44,
borderRadius: 22,
backgroundColor: colors.palette.overlay50,
justifyContent: "center",
alignItems: "center",
paddingBottom: 8,
paddingRight: 2,
}

const $flipButtonText: TextStyle = {
color: colors.palette.neutral100,
fontSize: 28,
textAlign: "center",
// marginTop: -8,
alignItems: "center",
alignSelf: "center",
}

const $closeButton: ViewStyle = {
width: 44,
height: 44,
borderRadius: 22,
backgroundColor: colors.palette.overlay50,
justifyContent: "center",
alignItems: "center",
}

const $closeButtonText: TextStyle = {
color: colors.palette.neutral100,
fontSize: 20,
fontWeight: "bold",
}

const $bottomBar: ViewStyle = {
flexDirection: "row",
justifyContent: "center",
paddingBottom: 40,
}

const $captureButton: ViewStyle = {
width: 80,
height: 80,
borderRadius: 40,
backgroundColor: colors.palette.neutral100,
justifyContent: "center",
alignItems: "center",
borderWidth: 4,
borderColor: "rgba(255,255,255,0.5)",
}

const $captureButtonDisabled: ViewStyle = {
opacity: 0.7,
}

const $captureButtonInner: ViewStyle = {
width: 60,
height: 60,
borderRadius: 30,
backgroundColor: colors.palette.neutral100,
}

const $permissionContainer: ViewStyle = {
flex: 1,
justifyContent: "center",
alignItems: "center",
padding: spacing.lg,
}

const $permissionText: TextStyle = {
fontSize: 16,
textAlign: "center",
marginBottom: spacing.lg,
color: colors.palette.neutral100,
}

const $permissionButton: ViewStyle = {
minWidth: 200,
marginBottom: spacing.sm,
}

const $cancelButton: ViewStyle = {
minWidth: 200,
backgroundColor: colors.transparent,
borderColor: colors.palette.neutral100,
}

const $cancelButtonText: TextStyle = {
color: colors.palette.neutral100,
}
8 changes: 7 additions & 1 deletion apps/ExampleApp/app/components/ImageSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Text } from "app/components/Text"
import { useExampleImage, UseExampleImageStatus, SelectedImage } from "../utils/useExampleImage"
import { RNMLKitImageView } from "./RNMLKitImageView"
import { Button } from "./Button"
import { CameraModal } from "./CameraModal"
import { colors } from "../theme"
import { ImageFilter, ImageGrouper } from "../utils/useExampleImage/examples"
import { BoundingBox } from "@infinitered/react-native-mlkit-core"
Expand Down Expand Up @@ -36,7 +37,7 @@ export const ImageSelector = observer(function ImageSelector({
}: ImageSelectorProps) {
const [_status, _setStatus] = React.useState<UseExampleImageStatus>("init")

const { image, takePhoto, selectPhoto, clearPhoto } = useExampleImage({
const { image, takePhoto, selectPhoto, clearPhoto, showCameraModal, onCameraCapture, onCameraClose } = useExampleImage({
filter: images?.filter ?? "all",
groupBy: images?.groupBy ?? "none",
})
Expand Down Expand Up @@ -94,6 +95,11 @@ export const ImageSelector = observer(function ImageSelector({
/>
)}
</View>
<CameraModal
visible={showCameraModal}
onCapture={onCameraCapture}
onClose={onCameraClose}
/>
</View>
)
})
Expand Down
2 changes: 2 additions & 0 deletions apps/ExampleApp/app/navigators/AppNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export type AppStackParamList = {
ObjectDetection: Record<string, never>
DocumentScanner: Record<string, never>
TextRecognition: Record<string, never>
BarcodeScanning: Record<string, never>
// IGNITE_GENERATOR_ANCHOR_APP_STACK_PARAM_LIST
}

Expand Down Expand Up @@ -63,6 +64,7 @@ const AppStack = observer(function AppStack() {
<Stack.Screen name="ObjectDetection" component={Screens.ObjectDetectionScreen} />
<Stack.Screen name="DocumentScanner" component={Screens.DocumentScannerScreen} />
<Stack.Screen name="TextRecognition" component={Screens.TextRecognitionScreen} />
<Stack.Screen name="BarcodeScanning" component={Screens.BarcodeScanningScreen} />
{/* IGNITE_GENERATOR_ANCHOR_APP_STACK_SCREENS */}
</Stack.Navigator>
)
Expand Down
Loading
Loading