In this tutorial, we are going to build an Image crop tool using React-Native. The Image Crop tool is a very important tool for cropping the Images. It will allow the users to pick an image from storage, crop it, and later save it locally.
Preview Image:
Prerequisites
- Introduction to React Native
- React Native Components
- React Native State
- React Native Props
- Expo CLI
- Node.js and npm (Node Package Manager)
Project Setup
Step 1: Create the project
npx create-expo-app image_crop
Step 2: Navigate to the project
cd image_crop
Step 3: Install the required packages.
npx expo install expo-file-system expo-image-picker
- expo-file-system: For saving the file.
- expo-image-picker: For picking and cropping the image.
Project Structure:
package.json for dependencies and respective versions.
{
"name": "image_crop",
"version": "1.0.0",
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web"
},
"dependencies": {
"expo": "~49.0.11",
"expo-file-system": "~15.4.4",
"expo-image-picker": "~14.3.2",
"expo-status-bar": "~1.6.0",
"react": "18.2.0",
"react-native": "0.72.4"
},
"devDependencies": {
"@babel/core": "^7.20.0"
},
"private": true
}
Approach
- The application will have three buttons, pick image, reset image, and save image.
- On clicking the Pick Image button, Image Picker opens. We need to select an image.
- After that crop window opens where we need to crop it.
- After cropping, the image is displayed.
- If satisfied, press the save button.
- When completed, we can reset the app.
Example: This example shows the creation of the image crop tool using React-Native.
Javascript
// App.js file   import {     Button,     StyleSheet,     Text,     View,     Image,     TextInput, } from "react-native" ; import * as ImagePicker from "expo-image-picker" ; import * as FileSystem from "expo-file-system" ; import { useRef, useState } from "react" ; export default function App() {     const [fileUri, setFileUri] = useState( "" );     const [fileType, setFileType] = useState( "" );     const [heightAspect, setHeightAspect] = useState( "3" );     const [widthAspect, setWidthAspect] = useState( "4" );     const handlePickFile = async () => {         if (heightAspect == "0" || widthAspect == "0" ) {             const res =                 await ImagePicker.launchImageLibraryAsync({                     mediaTypes:                         ImagePicker.MediaTypeOptions.Images,                     quality: 1,                     allowsEditing: true ,                     allowsMultipleSelection: false ,                 });             if (res.canceled) return ;               setFileUri(res.assets[0].uri);             setFileType(res.assets[0].type);         } else {             const res =                 await ImagePicker.launchImageLibraryAsync({                     mediaTypes:                         ImagePicker.MediaTypeOptions.Images,                     quality: 1,                     aspect: [                         parseInt(widthAspect),                         parseInt(heightAspect),                     ],                     allowsEditing: true ,                     allowsMultipleSelection: false ,                 });             if (res.canceled) return ;               setFileUri(res.assets[0].uri);             setFileType(res.assets[0].type);         }     };       const saveFile = async (uri, mimetype) => {         let fileName = Date.now() + ".jpg" ;         const permissions =             await                 FileSystem.StorageAccessFramework                     .requestDirectoryPermissionsAsync();         if (permissions.granted) {             const base64 =                 await FileSystem.readAsStringAsync(uri, {                     encoding:                         FileSystem.EncodingType.Base64,                 });             await FileSystem.StorageAccessFramework.createFileAsync(                 permissions.directoryUri,                 fileName,                 mimetype             )                 .then(async (uri) => {                     await FileSystem.writeAsStringAsync(                         uri,                         base64,                         {                             encoding:                                 FileSystem.EncodingType                                     .Base64,                         }                     );                 })                 . catch ((e) => console.log(e));         } else {             alert( "Permission not granted" );         }     };     return (         <View style={styles.container}>             <Text style={styles.heading1}>                 Image Crop neveropen             </Text>             {fileUri.length != 0 ? (                 <Image                     source={{ uri: fileUri }}                     style={{                         width: 400,                         height: 400,                         objectFit: "contain" ,                     }}                 />             ) : (                 <View></View>             )}             <View                 style={{                     flexDirection: "column" ,                     alignItems: "center" ,                 }}>                                   <Text                     style={{ fontSize: 24, color: "red" }}>                     Aspect ratio                 </Text>                 <Text>                     Set values 0 for any for free aspect                     ratio. Default is 4:3                 </Text>                 <View                     style={{                         display: "flex" ,                         flexDirection: "row" ,                         alignContent: "center" ,                     }}>                                           <Text style={styles.inputLabel}>                         Width:{ " " }                     </Text>                     <TextInput                         onChangeText={setWidthAspect}                         value={widthAspect}                         inputMode= "numeric"                         keyboardType= "numeric"                         style={styles.input}                         maxLength={2}/>                                           </View>                 <View                     style={{                         display: "flex" ,                         flexDirection: "row" ,                         alignContent: "center" ,                     }}>                                           <Text style={styles.inputLabel}>                         Height:{ " " }                     </Text>                     <TextInput                         onChangeText={setHeightAspect}                         value={heightAspect}                         inputMode= "numeric"                         keyboardType= "numeric"                         style={styles.input}                         maxLength={2}/>                 </View>             </View>             <View                 style={{                     display: "flex" ,                     flexDirection: "row" ,                     justifyContent: "space-evenly" ,                     width: "100%" ,                     padding: 10,                 }}>                                   <Button                     title= "Pick Image"                     onPress={handlePickFile}/>                                       {fileUri.length != 0 ? (                     <>                         <Button                             title= "Save Image"                             onPress={() =>                                 saveFile(fileUri, fileType)                             }                         />                         <Button                             title= "reset"                             onPress={() => setFileUri( "" )}                         />                     </>                 ) : (                     <></>                 )}             </View>         </View>     ); }   const styles = StyleSheet.create({     container: {         flex: 1,         backgroundColor: "#fff" ,         alignItems: "center" ,         justifyContent: "space-evenly" ,         height: "100%" ,     },     heading1: {         fontSize: 28,         fontWeight: "bold" ,         color: "green" ,         textAlign: "center" ,     },     input: {         width: 50,         height: 30,         borderColor: "gray" ,         borderWidth: 1,         textAlign: "center" ,         borderRadius: 5,         padding: 5,         margin: 5,     },     inputLabel: {         fontSize: 20,         margin: 5,         padding: 5,     }, }); |
Step 4: Run the application
npx expo start
To run on Android:
npx react-native run-android
To run on iOS:
npx react-native run-ios
Output: