Implement image handling, camera access, and permission management for creative mobile apps.
Every creative app revolves around images. Your Creative Studio needs to:
import React from 'react';
import { Image, StyleSheet, View } from 'react-native';
function PhotoDisplay({ source, style }) {
return (
<View style={styles.container}>
<Image
source={source}
style={[styles.image, style]}
resizeMode="cover"
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#000',
borderRadius: 8,
overflow: 'hidden',
},
image: {
width: '100%',
aspectRatio: 1, // Square images
},
});
import { Dimensions } from 'react-native';
const { width: screenWidth } = Dimensions.get('window');
const styles = StyleSheet.create({
galleryImage: {
width: (screenWidth - 48) / 3, // 3 columns with margins
aspectRatio: 1,
marginBottom: 4,
},
fullImage: {
width: screenWidth,
height: screenWidth * 0.75, // 4:3 aspect ratio
},
});
Install the required package:
expo install expo-image-picker
import React, { useState } from 'react';
import { View, TouchableOpacity, Text, Image, StyleSheet, Alert } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
function CameraInterface() {
const [selectedImage, setSelectedImage] = useState(null);
// Request permissions
const requestPermissions = async () => {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== 'granted') {
Alert.alert(
'Permission Required',
'We need access to your photo library to select images.',
[{ text: 'OK' }]
);
return false;
}
return true;
};
// Pick image from library
const pickImage = async () => {
const hasPermission = await requestPermissions();
if (!hasPermission) return;
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1],
quality: 0.8,
});
if (!result.canceled) {
setSelectedImage(result.assets[0].uri);
}
};
// Take photo with camera
const takePhoto = async () => {
const { status } = await ImagePicker.requestCameraPermissionsAsync();
if (status !== 'granted') {
Alert.alert(
'Permission Required',
'We need access to your camera to take photos.',
[{ text: 'OK' }]
);
return;
}
const result = await ImagePicker.launchCameraAsync({
allowsEditing: true,
aspect: [1, 1],
quality: 0.8,
});
if (!result.canceled) {
setSelectedImage(result.assets[0].uri);
}
};
return (
<View style={styles.container}>
{selectedImage ? (
<Image source={{ uri: selectedImage }} style={styles.previewImage} />
) : (
<View style={styles.placeholder}>
<Text style={styles.placeholderText}>No image selected</Text>
</View>
)}
<View style={styles.buttons}>
<TouchableOpacity style={styles.button} onPress={takePhoto}>
<Text style={styles.buttonText}>Take Photo</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={pickImage}>
<Text style={styles.buttonText}>Choose from Library</Text>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#000',
},
previewImage: {
width: '100%',
aspectRatio: 1,
borderRadius: 12,
marginBottom: 20,
},
placeholder: {
width: '100%',
aspectRatio: 1,
backgroundColor: '#1f2937',
borderRadius: 12,
borderWidth: 2,
borderColor: '#374151',
borderStyle: 'dashed',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 20,
},
placeholderText: {
color: '#9ca3af',
fontSize: 16,
},
buttons: {
flexDirection: 'row',
gap: 12,
},
button: {
flex: 1,
backgroundColor: '#6366f1',
paddingVertical: 16,
borderRadius: 8,
alignItems: 'center',
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
});
import { useState, useEffect } from 'react';
function usePermissions() {
const [permissions, setPermissions] = useState({
camera: null,
library: null,
});
useEffect(() => {
checkPermissions();
}, []);
const checkPermissions = async () => {
const cameraStatus = await ImagePicker.getCameraPermissionsAsync();
const libraryStatus = await ImagePicker.getMediaLibraryPermissionsAsync();
setPermissions({
camera: cameraStatus.status,
library: libraryStatus.status,
});
};
const requestCameraPermission = async () => {
const result = await ImagePicker.requestCameraPermissionsAsync();
setPermissions(prev => ({ ...prev, camera: result.status }));
return result.status === 'granted';
};
const requestLibraryPermission = async () => {
const result = await ImagePicker.requestMediaLibraryPermissionsAsync();
setPermissions(prev => ({ ...prev, library: result.status }));
return result.status === 'granted';
};
return {
permissions,
requestCameraPermission,
requestLibraryPermission,
};
}
Build a basic gallery interface:
import React, { useState } from 'react';
import { View, FlatList, TouchableOpacity, Image, StyleSheet } from 'react-native';
function PhotoGallery({ photos, onPhotoSelect }) {
const [selectedPhotos, setSelectedPhotos] = useState([]);
const togglePhoto = (photoId) => {
setSelectedPhotos(prev =>
prev.includes(photoId)
? prev.filter(id => id !== photoId)
: [...prev, photoId]
);
};
const renderPhoto = ({ item }) => (
<TouchableOpacity
style={[
styles.photoItem,
selectedPhotos.includes(item.id) && styles.selected
]}
onPress={() => togglePhoto(item.id)}
>
<Image source={{ uri: item.uri }} style={styles.thumbnail} />
{selectedPhotos.includes(item.id) && (
<View style={styles.checkmark}>
<Text style={styles.checkText}>✓</Text>
</View>
)}
</TouchableOpacity>
);
return (
<FlatList
data={photos}
renderItem={renderPhoto}
numColumns={3}
keyExtractor={(item) => item.id}
style={styles.gallery}
/>
);
}
const styles = StyleSheet.create({
gallery: {
backgroundColor: '#000',
},
photoItem: {
flex: 1/3,
aspectRatio: 1,
padding: 2,
},
selected: {
opacity: 0.7,
},
thumbnail: {
flex: 1,
borderRadius: 4,
},
checkmark: {
position: 'absolute',
top: 8,
right: 8,
backgroundColor: '#6366f1',
width: 24,
height: 24,
borderRadius: 12,
justifyContent: 'center',
alignItems: 'center',
},
checkText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
});
// Use appropriate image sizes
const imagePickerOptions = {
quality: 0.8, // Balance quality and file size
aspect: [1, 1], // Consistent aspect ratios
allowsEditing: true, // Let users crop
};
// Clear unused images from state
const clearImage = () => {
setSelectedImage(null); // Frees memory
};
You've learned to:
Next: You'll learn navigation fundamentals to connect your screens together.