Chapter 6 of 12
~18 min read
Camera, Gallery & Filesystem
Migrating from cordova-plugin-camera & cordova-plugin-file to expo-camera, expo-image-picker, expo-file-system.
1. Modern Permission & Gallery Flow
On modern Android (Android 14, 15, 16), scoped storage prevents apps from reading arbitrary disk paths. Cordova apps frequently crashed due to outdated file URIs. With expo-image-picker, runtime permissions and temporary secure cached URIs are handled automatically.
React Native Complete Image Picker Component
import React, { useState } from 'react';
import { View, Image, StyleSheet, Button, Alert } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import * as FileSystem from 'expo-file-system';
export function MediaPickerScreen() {
const [imageUri, setImageUri] = useState<string | null>(null);
const pickImage = async () => {
// 1. Request permission
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== 'granted') {
Alert.alert('Permission Denied', 'We need access to your gallery.');
return;
}
// 2. Launch native picker
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ['images'],
allowsEditing: true,
aspect: [4, 3],
quality: 0.8,
});
if (!result.canceled) {
setImageUri(result.assets[0].uri);
// 3. Inspect file info via expo-file-system
const info = await FileSystem.getInfoAsync(result.assets[0].uri);
console.log('File size in bytes:', info.size);
}
};
return (
<View style={styles.container}>
<Button title="Select Photo from Gallery" onPress={pickImage} />
{imageUri && <Image source={{ uri: imageUri }} style={styles.image} />}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 20, alignItems: 'center', justifyContent: 'center' },
image: { width: 300, height: 225, marginTop: 20, borderRadius: 12 }
});