Version 1.2
These instructions will walk you through the creation of a cross-platform smartphone app, in the form of a scavenger hunt that prompts visitors to find and check off artifacts on display.
The app will be based on the free, versatile React Native framework used by Facebook, Instagram, and other prominent companies. You will customize React Native's HTML-like tags and test-drive your new app on your own Android or iOS device.
This workshop assumes you already know why a mobile app would appeal to young users. Although everyone is welcome to attend regardless of background, participants who want to make the app should have these ready:
// Here you import the components necessary to build the app.
import * as React from 'react' ;
...
export default function App() {
return (
// Here you add tags corresponding to the visible parts of the app.
<ScrollView>...
);
}
const styles = StyleSheet.create({
// Here you style the color, text, and other aspects of the look of the app.
container: {
backgroundColor: 'cadetblue',
...
}
});
Expo makes it easy to write apps in your browser, which are called Snacks. Visit the Scavenger Hunt snack in your browser and edit the code in the left pane to see a preview of the result at right:
React Native "start tags" start with < and end with >. The closing tag starts with </ and ends with >.
⚠️ Don't change what's inside the tags--only the content they enclose.
...
export default function App() {
return (
<ScrollView style={styles.container}>
<View style={styles.titleContainer}>
<Text style={styles.title}>My Awesome Hats</Text>
</View>
In this context, a uri is the same as a URL.
⚠️ Make sure the uri is between two quote marks.
⚠️ These must be regular apostrophes (') or double quotes("), not curly (“ ”) or angled (`) quotes.
<Image
style={styles.thumbnail}
source={{
uri:
'https://example.com/grumpy_cat.jpg',
}}
/>
The question appears next to the image until you press on it to reveal the caption.
The ❓ and ✅ characters are just emoji--you don't have to include them.
<Text
style={styles.question}
ref={(component) => (this.manetQuestion = component)}>
❓Whose cat is this?
</Text>
<Text
style={styles.caption}
ref={(component) => (this.manetCaption = component)}>
✅ It's mine!
</Text>
React Native sizes are just numbers like 24 instead of 24px; the implicit units are relative pixel dimensions.
You can use simple color names (red, blue) or any of these CSS color names.
You can use any of these font families.
React Native style properties are camelCase, eg fontSize or backgroundColor.
question: {
...
fontSize: 24,
color: 'yellow',
fontFamily: 'Marker Felt',
...
},
🙂 The good news is that you can't break anything on the web or your phone with Expo, so don't worry if you see the Red Screen of Death while editing the code. You may just not be done with your revision, or you could have made one of the following mistakes.
// V3.7 works in iPhone and Android simulator.
import * as React from 'react';
import {
Text,
View,
StyleSheet,
ScrollView,
Image,
TouchableOpacity,
} from 'react-native';
export default function App() {
return (
<ScrollView style={styles.container}>
<View style={styles.titleContainer}>
<Text style={styles.title}>Scavenger Hunt 3.7</Text>
</View>
<TouchableOpacity
onPress={() => {
this.manetQuestion.setNativeProps({ style: { opacity: 0 } });
this.manetCaption.setNativeProps({ style: { opacity: 1 } });
}}>
<View style={styles.card}>
<Image
style={styles.thumbnail}
source={{
uri:
'https://tutorials.nmdprojects.net/react_native_scavenger_hunt/related_files/manet_before_the_mirror.jpg',
}}
/>
<Text
style={styles.question}
ref={(component) => (this.manetQuestion = component)}>
❓Who painted Before the Mirror (1876), featuring a woman whose face
you can't see?
</Text>
<Text
style={styles.caption}
ref={(component) => (this.manetCaption = component)}>
✅ Although not considered an Impressionist, Eduard Manet conveys
detail with vivid brushwork.
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.picassoQuestion.setNativeProps({ style: { opacity: 0 } });
this.picassoCaption.setNativeProps({ style: { opacity: 1 } });
}}>
<View style={styles.card}>
<Image
style={styles.thumbnail}
source={{
uri:
'https://tutorials.nmdprojects.net/react_native_scavenger_hunt/related_files/picasso_woman_with_yellow_hair.jpg',
}}
/>
<Text
style={styles.question}
ref={(component) => (this.picassoQuestion = component)}>
❓Who was the model for this portrait by one of the most famous
artists of all time?
</Text>
<Text
style={styles.caption}
ref={(component) => (this.picassoCaption = component)}>
✅ Marie-Therese Walter posed for this 1931 work by Pablo Picasso
when she was 21 and he was 49.
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.bonnardQuestion.setNativeProps({ style: { opacity: 0 } });
this.bonnardCaption.setNativeProps({ style: { opacity: 1 } });
}}>
<View style={styles.card}>
<Image
style={styles.thumbnail}
source={{
uri:
'https://tutorials.nmdprojects.net/react_native_scavenger_hunt/related_files/bonnard_dining_room_on_the_garden.jpg',
}}
/>
<Text
style={styles.question}
ref={(component) => (this.bonnardQuestion = component)}>
❓What is hidden in a corner of this dining room painted by Fauvist
artist Pierre Bonnard?
</Text>
<Text
style={styles.caption}
ref={(component) => (this.bonnardCaption = component)}>
✅ Dining Room on the Garden (1934-35) contains a hidden figure who
blends into the background.
</Text>
</View>
</TouchableOpacity>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-start',
backgroundColor: 'cadetblue',
padding: 8,
},
titleContainer: {
display: 'flex',
alignItems: 'center',
},
title: {
fontWeight: 'bold',
color: 'teal',
fontSize: 24,
marginTop: 30,
},
card: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
padding: 24,
backgroundColor: 'slategray',
marginTop: 20,
borderRadius: 20,
},
thumbnail: {
width: 100,
height: 100,
},
question: {
position: 'absolute',
left: 140,
top: 20,
fontSize: 18,
color: 'white',
width: '64%',
},
caption: {
position: 'absolute',
left: 140,
top: 20,
fontSize: 18,
color: 'paleturquoise',
width: '64%',
opacity: 0,
},
});