Camera
Using expo-camera for taking photos, QR code scanning, and handling camera permissions.
Camera
ScaleRocket Mobile uses expo-camera for taking photos and scanning QR codes. The camera requires a development build and explicit user permission.
Setup
npx expo install expo-cameraAdd the camera permission description to app.json:
{
"expo": {
"ios": {
"infoPlist": {
"NSCameraUsageDescription": "We use the camera to take profile photos and scan QR codes"
}
}
}
}Permission Handling
Always request permission before showing the camera:
import { CameraView, useCameraPermissions } from "expo-camera";
export default function CameraScreen() {
const [permission, requestPermission] = useCameraPermissions();
if (!permission) return <Loading />;
if (!permission.granted) {
return (
<View style={styles.container}>
<Text>Camera access is required</Text>
<Button title="Grant Permission" onPress={requestPermission} />
</View>
);
}
return <CameraView style={{ flex: 1 }} />;
}Taking Photos
Capture a photo using a ref:
import { useRef } from "react";
import { CameraView } from "expo-camera";
export default function PhotoScreen() {
const cameraRef = useRef<CameraView>(null);
const takePhoto = async () => {
if (!cameraRef.current) return;
const photo = await cameraRef.current.takePictureAsync({
quality: 0.8,
base64: false,
exif: false,
});
console.log("Photo URI:", photo.uri);
// Navigate to preview or upload
};
return (
<View style={{ flex: 1 }}>
<CameraView ref={cameraRef} style={{ flex: 1 }} facing="back">
<View style={styles.controls}>
<TouchableOpacity style={styles.captureButton} onPress={takePhoto}>
<View style={styles.captureInner} />
</TouchableOpacity>
</View>
</CameraView>
</View>
);
}Switch Camera
Toggle between front and back cameras:
function CameraWithToggle() {
const [facing, setFacing] = useState<"front" | "back">("back");
const cameraRef = useRef<CameraView>(null);
const toggleFacing = () => {
setFacing((prev) => (prev === "back" ? "front" : "back"));
};
return (
<CameraView ref={cameraRef} style={{ flex: 1 }} facing={facing}>
<TouchableOpacity style={styles.flipButton} onPress={toggleFacing}>
<Ionicons name="camera-reverse" size={28} color="white" />
</TouchableOpacity>
</CameraView>
);
}QR Code Scanning
Use the camera's built-in barcode scanner:
import { CameraView } from "expo-camera";
function QRScanner() {
const [scanned, setScanned] = useState(false);
const handleBarCodeScanned = ({ type, data }: { type: string; data: string }) => {
if (scanned) return;
setScanned(true);
Alert.alert("QR Code Scanned", data, [
{ text: "Scan Again", onPress: () => setScanned(false) },
{ text: "Open", onPress: () => Linking.openURL(data) },
]);
};
return (
<CameraView
style={{ flex: 1 }}
facing="back"
barcodeScannerSettings={{
barcodeTypes: ["qr"],
}}
onBarcodeScanned={scanned ? undefined : handleBarCodeScanned}
/>
);
}Camera Overlay
Add a custom overlay for the camera viewfinder:
<CameraView style={{ flex: 1 }} facing="back">
<View style={styles.overlay}>
<View style={styles.scanArea}>
{/* Transparent center for QR scanning */}
</View>
<Text style={styles.instructions}>
Point your camera at a QR code
</Text>
</View>
</CameraView>
const styles = StyleSheet.create({
overlay: {
flex: 1,
backgroundColor: "rgba(0,0,0,0.5)",
justifyContent: "center",
alignItems: "center",
},
scanArea: {
width: 250,
height: 250,
borderWidth: 2,
borderColor: "white",
borderRadius: 12,
backgroundColor: "transparent",
},
instructions: {
color: "white",
marginTop: 24,
fontSize: 16,
},
});Important Notes
- Camera requires a development build (not Expo Go)
- iOS simulators have a limited camera simulation
- Android emulators support a virtual scene camera
- Always handle the case where permission is denied gracefully
- Dispose of photo URIs when done to free memory