├── .expo-shared └── assets.json ├── .gitignore ├── App.tsx ├── README.md ├── app.json ├── assets ├── favicon.png ├── icon.png └── splash.png ├── babel.config.js ├── example.tsx ├── package.json ├── tsconfig.json └── yarn.lock /.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | 12 | # macOS 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /App.tsx: -------------------------------------------------------------------------------- 1 | import {StatusBar} from 'expo-status-bar' 2 | import React from 'react' 3 | import {StyleSheet, Text, View, TouchableOpacity, Alert, ImageBackground, Image} from 'react-native' 4 | import {Camera} from 'expo-camera' 5 | let camera: Camera 6 | export default function App() { 7 | const [startCamera, setStartCamera] = React.useState(false) 8 | const [previewVisible, setPreviewVisible] = React.useState(false) 9 | const [capturedImage, setCapturedImage] = React.useState(null) 10 | const [cameraType, setCameraType] = React.useState(Camera.Constants.Type.back) 11 | const [flashMode, setFlashMode] = React.useState('off') 12 | 13 | const __startCamera = async () => { 14 | const {status} = await Camera.requestPermissionsAsync() 15 | console.log(status) 16 | if (status === 'granted') { 17 | setStartCamera(true) 18 | } else { 19 | Alert.alert('Access denied') 20 | } 21 | } 22 | const __takePicture = async () => { 23 | const photo: any = await camera.takePictureAsync() 24 | console.log(photo) 25 | setPreviewVisible(true) 26 | //setStartCamera(false) 27 | setCapturedImage(photo) 28 | } 29 | const __savePhoto = () => {} 30 | const __retakePicture = () => { 31 | setCapturedImage(null) 32 | setPreviewVisible(false) 33 | __startCamera() 34 | } 35 | const __handleFlashMode = () => { 36 | if (flashMode === 'on') { 37 | setFlashMode('off') 38 | } else if (flashMode === 'off') { 39 | setFlashMode('on') 40 | } else { 41 | setFlashMode('auto') 42 | } 43 | } 44 | const __switchCamera = () => { 45 | if (cameraType === 'back') { 46 | setCameraType('front') 47 | } else { 48 | setCameraType('back') 49 | } 50 | } 51 | return ( 52 | 53 | {startCamera ? ( 54 | 60 | {previewVisible && capturedImage ? ( 61 | 62 | ) : ( 63 | { 68 | camera = r 69 | }} 70 | > 71 | 79 | 88 | 97 | 102 | ⚡️ 103 | 104 | 105 | 114 | 119 | {cameraType === 'front' ? '🤳' : '📷'} 120 | 121 | 122 | 123 | 134 | 141 | 151 | 152 | 153 | 154 | 155 | )} 156 | 157 | ) : ( 158 | 166 | 178 | 185 | Take picture 186 | 187 | 188 | 189 | )} 190 | 191 | 192 | 193 | ) 194 | } 195 | 196 | const styles = StyleSheet.create({ 197 | container: { 198 | flex: 1, 199 | backgroundColor: '#fff', 200 | alignItems: 'center', 201 | justifyContent: 'center' 202 | } 203 | }) 204 | 205 | const CameraPreview = ({photo, retakePicture, savePhoto}: any) => { 206 | console.log('sdsfds', photo) 207 | return ( 208 | 216 | 222 | 230 | 236 | 246 | 252 | Re-take 253 | 254 | 255 | 265 | 271 | save photo 272 | 273 | 274 | 275 | 276 | 277 | 278 | ) 279 | } 280 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 📷expo-camera-tutorial 2 | 3 | 📌 This repo contain the source code of all the examples I used in [Build Camera app with expo with React Native](https://www.freecodecamp.org/news/how-to-create-a-camera-app-with-expo-and-react-native/) article. 4 | 5 | 6 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "camera-app", 4 | "slug": "camera-app", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "web": { 23 | "favicon": "./assets/favicon.png" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hayanisaid/expo-camera-tutorial/3fd57c2a927806a598d5b2aa13d56d5d377c0337/assets/favicon.png -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hayanisaid/expo-camera-tutorial/3fd57c2a927806a598d5b2aa13d56d5d377c0337/assets/icon.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hayanisaid/expo-camera-tutorial/3fd57c2a927806a598d5b2aa13d56d5d377c0337/assets/splash.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /example.tsx: -------------------------------------------------------------------------------- 1 | import {StatusBar} from 'expo-status-bar' 2 | import React, {useState, useEffect} from 'react' 3 | import {StyleSheet, Text, View, TouchableOpacity, ImageBackground} from 'react-native' 4 | import {Camera} from 'expo-camera' 5 | const tag = '[CAMERA]' 6 | export default function App() { 7 | const [hasPermission, setHasPermission] = useState(null) 8 | const [previewVisible, setPreviewVisible] = useState(false) 9 | const [capturedImage, setCapturedImage] = useState(null) 10 | const [startOver, setStartOver] = useState(true) 11 | const [type, setType] = useState(Camera.Constants.Type.back) 12 | let camera: Camera 13 | useEffect(() => { 14 | ;(async () => { 15 | const {status} = await Camera.requestPermissionsAsync() 16 | setHasPermission(status === 'granted') 17 | })() 18 | }, []) 19 | const __closeCamera = () => { 20 | setStartOver(true) 21 | } 22 | const __takePicture = async () => { 23 | if (!camera) return 24 | const photo = await camera.takePictureAsync() 25 | console.log(photo) 26 | setPreviewVisible(true) 27 | setCapturedImage(photo) 28 | } 29 | const __savePhoto = async () => {} 30 | return ( 31 | 36 | {startOver ? ( 37 | 45 | setStartOver(false)} 47 | style={{ 48 | width: 130, 49 | borderRadius: 4, 50 | backgroundColor: '#14274e', 51 | flexDirection: 'row', 52 | justifyContent: 'center', 53 | alignItems: 'center', 54 | height: 40 55 | }} 56 | > 57 | 64 | Take picture 65 | 66 | 67 | 68 | ) : ( 69 | 74 | {previewVisible ? ( 75 | 81 | 89 | 95 | setPreviewVisible(false)} 97 | style={{ 98 | width: 130, 99 | height: 40, 100 | 101 | alignItems: 'center', 102 | borderRadius: 4 103 | }} 104 | > 105 | 111 | Re-take 112 | 113 | 114 | 124 | 130 | save photo 131 | 132 | 133 | 134 | 135 | 136 | ) : ( 137 | { 141 | camera = r 142 | }} 143 | > 144 | 151 | 158 | 159 | 165 | X 166 | 167 | 168 | 169 | { 176 | setType(type === Camera.Constants.Type.back ? Camera.Constants.Type.front : Camera.Constants.Type.back) 177 | }} 178 | > 179 | Flip 180 | 181 | 192 | 199 | 209 | 210 | 211 | 212 | 213 | )} 214 | 215 | )} 216 | 217 | ) 218 | } 219 | 220 | const styles = StyleSheet.create({ 221 | container: { 222 | flex: 1, 223 | backgroundColor: '#000', 224 | alignItems: 'center', 225 | justifyContent: 'center' 226 | } 227 | }) 228 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject" 9 | }, 10 | "dependencies": { 11 | "expo": "~39.0.2", 12 | "expo-camera": "~9.0.0", 13 | "expo-status-bar": "~1.0.2", 14 | "react": "16.13.1", 15 | "react-dom": "16.13.1", 16 | "react-native": "https://github.com/expo/react-native/archive/sdk-39.0.3.tar.gz", 17 | "react-native-web": "~0.13.12" 18 | }, 19 | "devDependencies": { 20 | "@babel/core": "~7.9.0", 21 | "@types/react": "~16.9.35", 22 | "@types/react-dom": "~16.9.8", 23 | "@types/react-native": "~0.63.2", 24 | "typescript": "~3.9.5" 25 | }, 26 | "private": true 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "jsx": "react-native", 5 | "lib": ["dom", "esnext"], 6 | "moduleResolution": "node", 7 | "noEmit": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true, 10 | "strict": true 11 | } 12 | } 13 | --------------------------------------------------------------------------------