Skip to main content

New plant form

If you fancy a challenge, look at the screenshots at the end and try to build this UI yourself first.

Add a new color to our theme file (we'll use it for the input border):

Update theme.ts
@@ -4,4 +4,5 @@ export const theme = {
colorAppleGreen: "#a0d36c",
colorLimeGreen: "#d0e57e",
colorWhite: "#fff",
+ colorLightGrey: "#eee",
};

Now update the modal screen to add the form for creating a new plant:

Update app/new.tsx
import {
Text,
StyleSheet,
TextInput,
Alert,
ScrollView,
View,
} from "react-native";
import { theme } from "@/theme";
import { PlantlyButton } from "@/components/PlantlyButton";
import { useState } from "react";
import { PlantlyImage } from "@/components/PlantlyImage";

export default function NewScreen() {
const [name, setName] = useState<string>();
const [days, setDays] = useState<string>();

const handleSubmit = () => {
if (!name) {
return Alert.alert("Validation Error", "Give your plant a name");
}

if (!days) {
return Alert.alert(
"Validation Error",
`How often does ${name} need to be watered?`,
);
}

if (Number.isNaN(Number(days))) {
return Alert.alert(
"Validation Error",
"Watering frequency must be a be a number",
);
}

console.log("Adding plant", name, days);
};

return (
<ScrollView
style={styles.container}
contentContainerStyle={styles.contentContainer}
keyboardShouldPersistTaps="handled"
>
<View style={styles.centered}>
<PlantlyImage />
</View>
<Text style={styles.label}>Name</Text>
<TextInput
value={name}
onChangeText={setName}
style={styles.input}
placeholder="E.g. Casper the Cactus"
autoCapitalize="words"
/>
<Text style={styles.label}>Watering Frequency (every x days)</Text>
<TextInput
value={days}
onChangeText={setDays}
style={styles.input}
placeholder="E.g. 6"
keyboardType="number-pad"
/>
<PlantlyButton title="Add plant" onPress={handleSubmit} />
</ScrollView>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: theme.colorWhite,
},
contentContainer: {
paddingTop: 24,
paddingHorizontal: 24,
paddingBottom: 100,
},
input: {
borderWidth: 2,
borderColor: theme.colorLightGrey,
padding: 12,
borderRadius: 6,
marginBottom: 24,
fontSize: 18,
},
label: {
fontSize: 18,
marginBottom: 8,
},
centered: {
alignItems: "center",
},
});

Avoid tapping twice to submit

If you're done editing and tap the "add plant" button, by default you'll have to press it twice: once to hide the keyboard and once to actually press the button.

The keyboardShouldPersistTaps prop on the ScrollView lets us configure how we handle taps. In our case, adding keyboardShouldPersistTaps="handled" makes it so keyboard will not dismiss automatically when the tap was handled by children of the scroll view (i.e. our "add plant" button).

And add textAlign: "center" to the button text to ensure it gets rendered in the center for full-width buttons:

Update components/PlantlyButton.tsx
@@ -33,6 +33,7 @@ const styles = StyleSheet.create({
color: "white",
fontSize: 18,
fontWeight: "bold",
+ textAlign: "center",
},
button: {
paddingHorizontal: 18,
Keyboard avoiding view

For large forms, you may want to use KeyboardAvoidingView or KeyboardAwareScrollView to ensure your inputs are always visible.

Instructions for setting up KeyboardAvoidingView

First, install the package:

npx expo install react-native-keyboard-aware-scroll-view

Then, replace the ScrollView with the KeyboardAwareScrollView.

@@ -1,15 +1,9 @@
-import {
- Text,
- StyleSheet,
- TextInput,
- Alert,
- ScrollView,
- View,
-} from "react-native";
+import { Text, StyleSheet, TextInput, Alert, View } from "react-native";
import { theme } from "@/theme";
import { PlantlyButton } from "@/components/PlantlyButton";
import { useState } from "react";
import { PlantlyImage } from "@/components/PlantlyImage";
+import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";

export default function NewScreen() {
const [name, setName] = useState<string>();
@@ -36,7 +30,7 @@ export default function NewScreen() {
};

return (
- <ScrollView
+ <KeyboardAwareScrollView
style={styles.container}
contentContainerStyle={styles.contentContainer}
keyboardShouldPersistTaps="handled"
@@ -61,7 +55,7 @@ export default function NewScreen() {
keyboardType="number-pad"
/>
<PlantlyButton title="Add plant" onPress={handleSubmit} />
- </ScrollView>
+ </KeyboardAwareScrollView>
);
}

Checkpoint

AndroidiOS