React Native course · Module 6: Photos, camera and audio
expo-camera - Advanced Camera Control
In this lesson5
For full control over the camera, use expo-camera!
Installation
npx expo install expo-cameraBasic Camera View
import { CameraView, CameraType, useCameraPermissions } from 'expo-camera';
import { useState, useRef } from 'react';
function CameraScreen() {
const [facing, setFacing] = useState<CameraType>('back');
const [permission, requestPermission] = useCameraPermissions();
const cameraRef = useRef<CameraView>(null);
if (!permission) {
return <View />;
}
if (!permission.granted) {
return (
<View style={styles.container}>
<Text>We need access to the camera</Text>
<Pressable onPress={requestPermission}>
<Text>Grant permissions</Text>
</Pressable>
</View>
);
}
const toggleCameraFacing = () => {
setFacing((current) => (current === 'back' ? 'front' : 'back'));
};
return (
<View style={styles.container}>
<CameraView
ref={cameraRef}
style={styles.camera}
facing={facing}
>
<View style={styles.controls}>
<Pressable onPress={toggleCameraFacing}>
<Ionicons name="camera-reverse" size={32} color="white" />
</Pressable>
</View>
</CameraView>
</View>
);
}Taking Photos
function CameraWithCapture() {
const cameraRef = useRef<CameraView>(null);
const [photo, setPhoto] = useState<string | null>(null);
const takePicture = async () => {
if (cameraRef.current) {
const photo = await cameraRef.current.takePictureAsync({
quality: 0.8,
base64: false,
exif: true,
});
if (photo) {
setPhoto(photo.uri);
}
}
};
if (photo) {
return (
<View style={styles.previewContainer}>
<Image source={{ uri: photo }} style={styles.preview} />
<View style={styles.previewButtons}>
<Pressable onPress={() => setPhoto(null)}>
<Text>Retake</Text>
</Pressable>
<Pressable onPress={() => savePhoto(photo)}>
<Text>Save</Text>
</Pressable>
</View>
</View>
);
}
return (
<CameraView ref={cameraRef} style={styles.camera}>
<Pressable style={styles.captureButton} onPress={takePicture}>
<View style={styles.captureInner} />
</Pressable>
</CameraView>
);
}QR Code Scanning
import { CameraView, useCameraPermissions, BarcodeScanningResult } from 'expo-camera';
function QRScanner() {
const [permission, requestPermission] = useCameraPermissions();
const [scanned, setScanned] = useState(false);
const handleBarCodeScanned = (result: BarcodeScanningResult) => {
if (scanned) return;
setScanned(true);
Alert.alert(
'Code scanned!',
result.data,
[{ text: 'Scan again', onPress: () => setScanned(false) }]
);
};
return (
<CameraView
style={styles.camera}
barcodeScannerSettings={{
barcodeTypes: ['qr', 'ean13', 'ean8'],
}}
onBarcodeScanned={handleBarCodeScanned}
>
<View style={styles.overlay}>
<View style={styles.scanArea} />
<Text style={styles.instruction}>Point the camera at a QR code</Text>
</View>
</CameraView>
);
}Flash
function CameraWithFlash() {
const [flash, setFlash] = useState<'off' | 'on' | 'auto'>('off');
const toggleFlash = () => {
setFlash((current) => {
if (current === 'off') return 'on';
if (current === 'on') return 'auto';
return 'off';
});
};
return (
<CameraView style={styles.camera} flash={flash}>
<Pressable onPress={toggleFlash}>
<Ionicons
name={flash === 'off' ? 'flash-off' : flash === 'on' ? 'flash' : 'flash-outline'}
size={28}
color="white"
/>
</Pressable>
</CameraView>
);
}expo-camera gives you full control over the device camera!
Code for this lesson: App.tsx
import React, { useState } from 'react';
import { View, Text, Pressable, StyleSheet, Alert } from 'react-native';
import { CameraView, useCameraPermissions, BarcodeScanningResult } from 'expo-camera';
export default function App() {
const [permission, requestPermission] = useCameraPermissions();
const [scanned, setScanned] = useState(false);
const handleBarCodeScanned = (result: BarcodeScanningResult) => {
if (scanned) return;
setScanned(true);
Alert.alert('Code scanned!', result.data, [
{ text: 'Scan again', onPress: () => setScanned(false) },
]);
};
if (!permission?.granted) {
return (
<View style={styles.center}>
<Text style={styles.text}>We need camera access</Text>
<Pressable style={styles.button} onPress={requestPermission}>
<Text style={styles.buttonText}>Grant permission</Text>
</Pressable>
</View>
);
}
return (
<CameraView
style={styles.camera}
barcodeScannerSettings={{ barcodeTypes: ['qr'] }}
// TODO: connect the onBarcodeScanned prop to the handleBarCodeScanned function
>
<View style={styles.overlay}>
<Text style={styles.instruction}>Point the camera at a QR code</Text>
</View>
</CameraView>
);
}
const styles = StyleSheet.create({
center: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#1a5276' },
camera: { flex: 1 },
overlay: { flex: 1, justifyContent: 'flex-end', alignItems: 'center', paddingBottom: 60 },
instruction: { color: '#fff', fontSize: 16, backgroundColor: 'rgba(0,0,0,0.5)', padding: 10, borderRadius: 8 },
text: { color: '#fff', fontSize: 16, marginBottom: 16 },
button: { backgroundColor: '#2980b9', padding: 12, borderRadius: 8 },
buttonText: { color: '#fff', fontSize: 16 },
});Docs entries used in this lesson
Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. Which component displays the camera preview in expo-camera?
2. What values does the 'facing' prop accept in CameraView?
Hands-on tasks in the game
- Horizontal ordering
Arrange taking a picture:
- Code editor
Complete the scanner code with onBarcodeScanned.