diff --git a/README.md b/README.md
index f3240019..8f27c2ed 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ For example, the branch [02-basics](https://github.com/academind/react-native-pr
You can switch branches via the dropdown in the top left corner of this repository page:
-[Selecting a branch](./selecting-branches.png)
+
# Provided Resources
diff --git a/code/01-working-with-core-components/App.js b/code/01-working-with-core-components/App.js
new file mode 100644
index 00000000..a1755513
--- /dev/null
+++ b/code/01-working-with-core-components/App.js
@@ -0,0 +1,22 @@
+import { StyleSheet, Text, View, Button } from 'react-native';
+
+export default function App() {
+ return (
+
+
+ Another piece of text!
+
+ Hello World!
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#fff',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+});
diff --git a/code/01-working-with-core-components/app.json b/code/01-working-with-core-components/app.json
new file mode 100644
index 00000000..9a1223e7
--- /dev/null
+++ b/code/01-working-with-core-components/app.json
@@ -0,0 +1,32 @@
+{
+ "expo": {
+ "name": "RNCourse",
+ "slug": "RNCourse",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/adaptive-icon.png",
+ "backgroundColor": "#FFFFFF"
+ }
+ },
+ "web": {
+ "favicon": "./assets/favicon.png"
+ }
+ }
+}
diff --git a/code/01-working-with-core-components/assets/adaptive-icon.png b/code/01-working-with-core-components/assets/adaptive-icon.png
new file mode 100644
index 00000000..03d6f6b6
Binary files /dev/null and b/code/01-working-with-core-components/assets/adaptive-icon.png differ
diff --git a/code/01-working-with-core-components/assets/favicon.png b/code/01-working-with-core-components/assets/favicon.png
new file mode 100644
index 00000000..e75f697b
Binary files /dev/null and b/code/01-working-with-core-components/assets/favicon.png differ
diff --git a/code/01-working-with-core-components/assets/icon.png b/code/01-working-with-core-components/assets/icon.png
new file mode 100644
index 00000000..a0b1526f
Binary files /dev/null and b/code/01-working-with-core-components/assets/icon.png differ
diff --git a/code/01-working-with-core-components/assets/splash.png b/code/01-working-with-core-components/assets/splash.png
new file mode 100644
index 00000000..0e89705a
Binary files /dev/null and b/code/01-working-with-core-components/assets/splash.png differ
diff --git a/code/01-working-with-core-components/babel.config.js b/code/01-working-with-core-components/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/code/01-working-with-core-components/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/code/01-working-with-core-components/package.json b/code/01-working-with-core-components/package.json
new file mode 100644
index 00000000..a56b658b
--- /dev/null
+++ b/code/01-working-with-core-components/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rncourse",
+ "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",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~44.0.0",
+ "expo-status-bar": "~1.2.0",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-native": "0.64.3",
+ "react-native-web": "0.17.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/code/02-styling-react-native-apps/App.js b/code/02-styling-react-native-apps/App.js
new file mode 100644
index 00000000..21763ca7
--- /dev/null
+++ b/code/02-styling-react-native-apps/App.js
@@ -0,0 +1,36 @@
+import { StyleSheet, Text, View, Button } from 'react-native';
+
+export default function App() {
+ return (
+
+
+
+ Another piece of text!
+
+
+
+ Hello World!
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#fff',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ dummyText: {
+ margin: 16,
+ padding: 16,
+ borderWidth: 2,
+ borderColor: 'blue',
+ }
+});
diff --git a/code/02-styling-react-native-apps/app.json b/code/02-styling-react-native-apps/app.json
new file mode 100644
index 00000000..9a1223e7
--- /dev/null
+++ b/code/02-styling-react-native-apps/app.json
@@ -0,0 +1,32 @@
+{
+ "expo": {
+ "name": "RNCourse",
+ "slug": "RNCourse",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/adaptive-icon.png",
+ "backgroundColor": "#FFFFFF"
+ }
+ },
+ "web": {
+ "favicon": "./assets/favicon.png"
+ }
+ }
+}
diff --git a/code/02-styling-react-native-apps/assets/adaptive-icon.png b/code/02-styling-react-native-apps/assets/adaptive-icon.png
new file mode 100644
index 00000000..03d6f6b6
Binary files /dev/null and b/code/02-styling-react-native-apps/assets/adaptive-icon.png differ
diff --git a/code/02-styling-react-native-apps/assets/favicon.png b/code/02-styling-react-native-apps/assets/favicon.png
new file mode 100644
index 00000000..e75f697b
Binary files /dev/null and b/code/02-styling-react-native-apps/assets/favicon.png differ
diff --git a/code/02-styling-react-native-apps/assets/icon.png b/code/02-styling-react-native-apps/assets/icon.png
new file mode 100644
index 00000000..a0b1526f
Binary files /dev/null and b/code/02-styling-react-native-apps/assets/icon.png differ
diff --git a/code/02-styling-react-native-apps/assets/splash.png b/code/02-styling-react-native-apps/assets/splash.png
new file mode 100644
index 00000000..0e89705a
Binary files /dev/null and b/code/02-styling-react-native-apps/assets/splash.png differ
diff --git a/code/02-styling-react-native-apps/babel.config.js b/code/02-styling-react-native-apps/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/code/02-styling-react-native-apps/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/code/02-styling-react-native-apps/package.json b/code/02-styling-react-native-apps/package.json
new file mode 100644
index 00000000..a56b658b
--- /dev/null
+++ b/code/02-styling-react-native-apps/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rncourse",
+ "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",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~44.0.0",
+ "expo-status-bar": "~1.2.0",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-native": "0.64.3",
+ "react-native-web": "0.17.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/code/03-improving-the-layout/App.js b/code/03-improving-the-layout/App.js
new file mode 100644
index 00000000..aabc7c4e
--- /dev/null
+++ b/code/03-improving-the-layout/App.js
@@ -0,0 +1,42 @@
+import { StyleSheet, Text, View, Button, TextInput } from 'react-native';
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ List of goals...
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ appContainer: {
+ flex: 1,
+ paddingTop: 50,
+ paddingHorizontal: 16
+ },
+ inputContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 24,
+ borderBottomWidth: 1,
+ borderBottomColor: '#cccccc'
+ },
+ textInput: {
+ borderWidth: 1,
+ borderColor: '#cccccc',
+ width: '70%',
+ marginRight: 8,
+ padding: 8
+ },
+ goalsContainer: {
+ flex: 5
+ }
+});
diff --git a/code/03-improving-the-layout/app.json b/code/03-improving-the-layout/app.json
new file mode 100644
index 00000000..9a1223e7
--- /dev/null
+++ b/code/03-improving-the-layout/app.json
@@ -0,0 +1,32 @@
+{
+ "expo": {
+ "name": "RNCourse",
+ "slug": "RNCourse",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/adaptive-icon.png",
+ "backgroundColor": "#FFFFFF"
+ }
+ },
+ "web": {
+ "favicon": "./assets/favicon.png"
+ }
+ }
+}
diff --git a/code/03-improving-the-layout/assets/adaptive-icon.png b/code/03-improving-the-layout/assets/adaptive-icon.png
new file mode 100644
index 00000000..03d6f6b6
Binary files /dev/null and b/code/03-improving-the-layout/assets/adaptive-icon.png differ
diff --git a/code/03-improving-the-layout/assets/favicon.png b/code/03-improving-the-layout/assets/favicon.png
new file mode 100644
index 00000000..e75f697b
Binary files /dev/null and b/code/03-improving-the-layout/assets/favicon.png differ
diff --git a/code/03-improving-the-layout/assets/icon.png b/code/03-improving-the-layout/assets/icon.png
new file mode 100644
index 00000000..a0b1526f
Binary files /dev/null and b/code/03-improving-the-layout/assets/icon.png differ
diff --git a/code/03-improving-the-layout/assets/splash.png b/code/03-improving-the-layout/assets/splash.png
new file mode 100644
index 00000000..0e89705a
Binary files /dev/null and b/code/03-improving-the-layout/assets/splash.png differ
diff --git a/code/03-improving-the-layout/babel.config.js b/code/03-improving-the-layout/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/code/03-improving-the-layout/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/code/03-improving-the-layout/package.json b/code/03-improving-the-layout/package.json
new file mode 100644
index 00000000..a56b658b
--- /dev/null
+++ b/code/03-improving-the-layout/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rncourse",
+ "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",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~44.0.0",
+ "expo-status-bar": "~1.2.0",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-native": "0.64.3",
+ "react-native-web": "0.17.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/code/04-handling-events/App.js b/code/04-handling-events/App.js
new file mode 100644
index 00000000..9f2b30fa
--- /dev/null
+++ b/code/04-handling-events/App.js
@@ -0,0 +1,57 @@
+import { useState } from 'react';
+import { StyleSheet, Text, View, Button, TextInput } from 'react-native';
+
+export default function App() {
+ const [enteredGoalText, setEnteredGoalText] = useState('');
+
+ function goalInputHandler(enteredText) {
+ setEnteredGoalText(enteredText);
+ }
+
+ function addGoalHandler() {
+ console.log(enteredGoalText);
+ }
+
+ return (
+
+
+
+
+
+
+ List of goals...
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ appContainer: {
+ flex: 1,
+ paddingTop: 50,
+ paddingHorizontal: 16,
+ },
+ inputContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 24,
+ borderBottomWidth: 1,
+ borderBottomColor: '#cccccc',
+ },
+ textInput: {
+ borderWidth: 1,
+ borderColor: '#cccccc',
+ width: '70%',
+ marginRight: 8,
+ padding: 8,
+ },
+ goalsContainer: {
+ flex: 5,
+ },
+});
diff --git a/code/04-handling-events/app.json b/code/04-handling-events/app.json
new file mode 100644
index 00000000..9a1223e7
--- /dev/null
+++ b/code/04-handling-events/app.json
@@ -0,0 +1,32 @@
+{
+ "expo": {
+ "name": "RNCourse",
+ "slug": "RNCourse",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/adaptive-icon.png",
+ "backgroundColor": "#FFFFFF"
+ }
+ },
+ "web": {
+ "favicon": "./assets/favicon.png"
+ }
+ }
+}
diff --git a/code/04-handling-events/assets/adaptive-icon.png b/code/04-handling-events/assets/adaptive-icon.png
new file mode 100644
index 00000000..03d6f6b6
Binary files /dev/null and b/code/04-handling-events/assets/adaptive-icon.png differ
diff --git a/code/04-handling-events/assets/favicon.png b/code/04-handling-events/assets/favicon.png
new file mode 100644
index 00000000..e75f697b
Binary files /dev/null and b/code/04-handling-events/assets/favicon.png differ
diff --git a/code/04-handling-events/assets/icon.png b/code/04-handling-events/assets/icon.png
new file mode 100644
index 00000000..a0b1526f
Binary files /dev/null and b/code/04-handling-events/assets/icon.png differ
diff --git a/code/04-handling-events/assets/splash.png b/code/04-handling-events/assets/splash.png
new file mode 100644
index 00000000..0e89705a
Binary files /dev/null and b/code/04-handling-events/assets/splash.png differ
diff --git a/code/04-handling-events/babel.config.js b/code/04-handling-events/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/code/04-handling-events/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/code/04-handling-events/package.json b/code/04-handling-events/package.json
new file mode 100644
index 00000000..a56b658b
--- /dev/null
+++ b/code/04-handling-events/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rncourse",
+ "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",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~44.0.0",
+ "expo-status-bar": "~1.2.0",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-native": "0.64.3",
+ "react-native-web": "0.17.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/code/05-managing-a-list-of-course-goals/App.js b/code/05-managing-a-list-of-course-goals/App.js
new file mode 100644
index 00000000..dd0c22d8
--- /dev/null
+++ b/code/05-managing-a-list-of-course-goals/App.js
@@ -0,0 +1,61 @@
+import { useState } from 'react';
+import { StyleSheet, Text, View, Button, TextInput } from 'react-native';
+
+export default function App() {
+ const [enteredGoalText, setEnteredGoalText] = useState('');
+ const [courseGoals, setCourseGoals] = useState([]);
+
+ function goalInputHandler(enteredText) {
+ setEnteredGoalText(enteredText);
+ }
+
+ function addGoalHandler() {
+ setCourseGoals((currentCourseGoals) => [
+ ...currentCourseGoals,
+ enteredGoalText,
+ ]);
+ }
+
+ return (
+
+
+
+
+
+
+ {courseGoals.map((goal) => {goal})}
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ appContainer: {
+ flex: 1,
+ paddingTop: 50,
+ paddingHorizontal: 16,
+ },
+ inputContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 24,
+ borderBottomWidth: 1,
+ borderBottomColor: '#cccccc',
+ },
+ textInput: {
+ borderWidth: 1,
+ borderColor: '#cccccc',
+ width: '70%',
+ marginRight: 8,
+ padding: 8,
+ },
+ goalsContainer: {
+ flex: 5,
+ },
+});
diff --git a/code/05-managing-a-list-of-course-goals/app.json b/code/05-managing-a-list-of-course-goals/app.json
new file mode 100644
index 00000000..9a1223e7
--- /dev/null
+++ b/code/05-managing-a-list-of-course-goals/app.json
@@ -0,0 +1,32 @@
+{
+ "expo": {
+ "name": "RNCourse",
+ "slug": "RNCourse",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/adaptive-icon.png",
+ "backgroundColor": "#FFFFFF"
+ }
+ },
+ "web": {
+ "favicon": "./assets/favicon.png"
+ }
+ }
+}
diff --git a/code/05-managing-a-list-of-course-goals/assets/adaptive-icon.png b/code/05-managing-a-list-of-course-goals/assets/adaptive-icon.png
new file mode 100644
index 00000000..03d6f6b6
Binary files /dev/null and b/code/05-managing-a-list-of-course-goals/assets/adaptive-icon.png differ
diff --git a/code/05-managing-a-list-of-course-goals/assets/favicon.png b/code/05-managing-a-list-of-course-goals/assets/favicon.png
new file mode 100644
index 00000000..e75f697b
Binary files /dev/null and b/code/05-managing-a-list-of-course-goals/assets/favicon.png differ
diff --git a/code/05-managing-a-list-of-course-goals/assets/icon.png b/code/05-managing-a-list-of-course-goals/assets/icon.png
new file mode 100644
index 00000000..a0b1526f
Binary files /dev/null and b/code/05-managing-a-list-of-course-goals/assets/icon.png differ
diff --git a/code/05-managing-a-list-of-course-goals/assets/splash.png b/code/05-managing-a-list-of-course-goals/assets/splash.png
new file mode 100644
index 00000000..0e89705a
Binary files /dev/null and b/code/05-managing-a-list-of-course-goals/assets/splash.png differ
diff --git a/code/05-managing-a-list-of-course-goals/babel.config.js b/code/05-managing-a-list-of-course-goals/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/code/05-managing-a-list-of-course-goals/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/code/05-managing-a-list-of-course-goals/package.json b/code/05-managing-a-list-of-course-goals/package.json
new file mode 100644
index 00000000..a56b658b
--- /dev/null
+++ b/code/05-managing-a-list-of-course-goals/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rncourse",
+ "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",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~44.0.0",
+ "expo-status-bar": "~1.2.0",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-native": "0.64.3",
+ "react-native-web": "0.17.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/code/06-ios-android-styling-differences/App.js b/code/06-ios-android-styling-differences/App.js
new file mode 100644
index 00000000..975ed8be
--- /dev/null
+++ b/code/06-ios-android-styling-differences/App.js
@@ -0,0 +1,74 @@
+import { useState } from 'react';
+import { StyleSheet, Text, View, Button, TextInput } from 'react-native';
+
+export default function App() {
+ const [enteredGoalText, setEnteredGoalText] = useState('');
+ const [courseGoals, setCourseGoals] = useState([]);
+
+ function goalInputHandler(enteredText) {
+ setEnteredGoalText(enteredText);
+ }
+
+ function addGoalHandler() {
+ setCourseGoals((currentCourseGoals) => [
+ ...currentCourseGoals,
+ enteredGoalText,
+ ]);
+ }
+
+ return (
+
+
+
+
+
+
+ {courseGoals.map((goal) => (
+
+ {goal}
+
+ ))}
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ appContainer: {
+ flex: 1,
+ paddingTop: 50,
+ paddingHorizontal: 16,
+ },
+ inputContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 24,
+ borderBottomWidth: 1,
+ borderBottomColor: '#cccccc',
+ },
+ textInput: {
+ borderWidth: 1,
+ borderColor: '#cccccc',
+ width: '70%',
+ marginRight: 8,
+ padding: 8,
+ },
+ goalsContainer: {
+ flex: 5,
+ },
+ goalItem: {
+ margin: 8,
+ padding: 8,
+ borderRadius: 6,
+ backgroundColor: '#5e0acc',
+ },
+ goalText: {
+ color: 'white',
+ }
+});
diff --git a/code/06-ios-android-styling-differences/app.json b/code/06-ios-android-styling-differences/app.json
new file mode 100644
index 00000000..9a1223e7
--- /dev/null
+++ b/code/06-ios-android-styling-differences/app.json
@@ -0,0 +1,32 @@
+{
+ "expo": {
+ "name": "RNCourse",
+ "slug": "RNCourse",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/adaptive-icon.png",
+ "backgroundColor": "#FFFFFF"
+ }
+ },
+ "web": {
+ "favicon": "./assets/favicon.png"
+ }
+ }
+}
diff --git a/code/06-ios-android-styling-differences/assets/adaptive-icon.png b/code/06-ios-android-styling-differences/assets/adaptive-icon.png
new file mode 100644
index 00000000..03d6f6b6
Binary files /dev/null and b/code/06-ios-android-styling-differences/assets/adaptive-icon.png differ
diff --git a/code/06-ios-android-styling-differences/assets/favicon.png b/code/06-ios-android-styling-differences/assets/favicon.png
new file mode 100644
index 00000000..e75f697b
Binary files /dev/null and b/code/06-ios-android-styling-differences/assets/favicon.png differ
diff --git a/code/06-ios-android-styling-differences/assets/icon.png b/code/06-ios-android-styling-differences/assets/icon.png
new file mode 100644
index 00000000..a0b1526f
Binary files /dev/null and b/code/06-ios-android-styling-differences/assets/icon.png differ
diff --git a/code/06-ios-android-styling-differences/assets/splash.png b/code/06-ios-android-styling-differences/assets/splash.png
new file mode 100644
index 00000000..0e89705a
Binary files /dev/null and b/code/06-ios-android-styling-differences/assets/splash.png differ
diff --git a/code/06-ios-android-styling-differences/babel.config.js b/code/06-ios-android-styling-differences/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/code/06-ios-android-styling-differences/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/code/06-ios-android-styling-differences/package.json b/code/06-ios-android-styling-differences/package.json
new file mode 100644
index 00000000..a56b658b
--- /dev/null
+++ b/code/06-ios-android-styling-differences/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rncourse",
+ "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",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~44.0.0",
+ "expo-status-bar": "~1.2.0",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-native": "0.64.3",
+ "react-native-web": "0.17.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/code/07-optimizing-lists-with-flatlist/App.js b/code/07-optimizing-lists-with-flatlist/App.js
new file mode 100644
index 00000000..4cd0a148
--- /dev/null
+++ b/code/07-optimizing-lists-with-flatlist/App.js
@@ -0,0 +1,91 @@
+import { useState } from 'react';
+import {
+ StyleSheet,
+ Text,
+ View,
+ Button,
+ TextInput,
+ ScrollView,
+ FlatList,
+} from 'react-native';
+
+export default function App() {
+ const [enteredGoalText, setEnteredGoalText] = useState('');
+ const [courseGoals, setCourseGoals] = useState([]);
+
+ function goalInputHandler(enteredText) {
+ setEnteredGoalText(enteredText);
+ }
+
+ function addGoalHandler() {
+ setCourseGoals((currentCourseGoals) => [
+ ...currentCourseGoals,
+ { text: enteredGoalText, id: Math.random().toString() },
+ ]);
+ }
+
+ return (
+
+
+
+
+
+
+ {
+ return (
+
+ {itemData.item.text}
+
+ );
+ }}
+ keyExtractor={(item, index) => {
+ return item.id;
+ }}
+ alwaysBounceVertical={false}
+ />
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ appContainer: {
+ flex: 1,
+ paddingTop: 50,
+ paddingHorizontal: 16,
+ },
+ inputContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 24,
+ borderBottomWidth: 1,
+ borderBottomColor: '#cccccc',
+ },
+ textInput: {
+ borderWidth: 1,
+ borderColor: '#cccccc',
+ width: '70%',
+ marginRight: 8,
+ padding: 8,
+ },
+ goalsContainer: {
+ flex: 5,
+ },
+ goalItem: {
+ margin: 8,
+ padding: 8,
+ borderRadius: 6,
+ backgroundColor: '#5e0acc',
+ },
+ goalText: {
+ color: 'white',
+ },
+});
diff --git a/code/07-optimizing-lists-with-flatlist/app.json b/code/07-optimizing-lists-with-flatlist/app.json
new file mode 100644
index 00000000..9a1223e7
--- /dev/null
+++ b/code/07-optimizing-lists-with-flatlist/app.json
@@ -0,0 +1,32 @@
+{
+ "expo": {
+ "name": "RNCourse",
+ "slug": "RNCourse",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/adaptive-icon.png",
+ "backgroundColor": "#FFFFFF"
+ }
+ },
+ "web": {
+ "favicon": "./assets/favicon.png"
+ }
+ }
+}
diff --git a/code/07-optimizing-lists-with-flatlist/assets/adaptive-icon.png b/code/07-optimizing-lists-with-flatlist/assets/adaptive-icon.png
new file mode 100644
index 00000000..03d6f6b6
Binary files /dev/null and b/code/07-optimizing-lists-with-flatlist/assets/adaptive-icon.png differ
diff --git a/code/07-optimizing-lists-with-flatlist/assets/favicon.png b/code/07-optimizing-lists-with-flatlist/assets/favicon.png
new file mode 100644
index 00000000..e75f697b
Binary files /dev/null and b/code/07-optimizing-lists-with-flatlist/assets/favicon.png differ
diff --git a/code/07-optimizing-lists-with-flatlist/assets/icon.png b/code/07-optimizing-lists-with-flatlist/assets/icon.png
new file mode 100644
index 00000000..a0b1526f
Binary files /dev/null and b/code/07-optimizing-lists-with-flatlist/assets/icon.png differ
diff --git a/code/07-optimizing-lists-with-flatlist/assets/splash.png b/code/07-optimizing-lists-with-flatlist/assets/splash.png
new file mode 100644
index 00000000..0e89705a
Binary files /dev/null and b/code/07-optimizing-lists-with-flatlist/assets/splash.png differ
diff --git a/code/07-optimizing-lists-with-flatlist/babel.config.js b/code/07-optimizing-lists-with-flatlist/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/code/07-optimizing-lists-with-flatlist/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/code/07-optimizing-lists-with-flatlist/package.json b/code/07-optimizing-lists-with-flatlist/package.json
new file mode 100644
index 00000000..a56b658b
--- /dev/null
+++ b/code/07-optimizing-lists-with-flatlist/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rncourse",
+ "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",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~44.0.0",
+ "expo-status-bar": "~1.2.0",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-native": "0.64.3",
+ "react-native-web": "0.17.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/code/08-splitting-components/App.js b/code/08-splitting-components/App.js
new file mode 100644
index 00000000..bd13171f
--- /dev/null
+++ b/code/08-splitting-components/App.js
@@ -0,0 +1,78 @@
+import { useState } from 'react';
+import {
+ StyleSheet,
+ View,
+ Button,
+ TextInput,
+ FlatList,
+} from 'react-native';
+
+import GoalItem from './components/GoalItem';
+
+export default function App() {
+ const [enteredGoalText, setEnteredGoalText] = useState('');
+ const [courseGoals, setCourseGoals] = useState([]);
+
+ function goalInputHandler(enteredText) {
+ setEnteredGoalText(enteredText);
+ }
+
+ function addGoalHandler() {
+ setCourseGoals((currentCourseGoals) => [
+ ...currentCourseGoals,
+ { text: enteredGoalText, id: Math.random().toString() },
+ ]);
+ }
+
+ return (
+
+
+
+
+
+
+ {
+ return ;
+ }}
+ keyExtractor={(item, index) => {
+ return item.id;
+ }}
+ alwaysBounceVertical={false}
+ />
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ appContainer: {
+ flex: 1,
+ paddingTop: 50,
+ paddingHorizontal: 16,
+ },
+ inputContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 24,
+ borderBottomWidth: 1,
+ borderBottomColor: '#cccccc',
+ },
+ textInput: {
+ borderWidth: 1,
+ borderColor: '#cccccc',
+ width: '70%',
+ marginRight: 8,
+ padding: 8,
+ },
+ goalsContainer: {
+ flex: 5,
+ }
+});
diff --git a/code/08-splitting-components/app.json b/code/08-splitting-components/app.json
new file mode 100644
index 00000000..9a1223e7
--- /dev/null
+++ b/code/08-splitting-components/app.json
@@ -0,0 +1,32 @@
+{
+ "expo": {
+ "name": "RNCourse",
+ "slug": "RNCourse",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/adaptive-icon.png",
+ "backgroundColor": "#FFFFFF"
+ }
+ },
+ "web": {
+ "favicon": "./assets/favicon.png"
+ }
+ }
+}
diff --git a/code/08-splitting-components/assets/adaptive-icon.png b/code/08-splitting-components/assets/adaptive-icon.png
new file mode 100644
index 00000000..03d6f6b6
Binary files /dev/null and b/code/08-splitting-components/assets/adaptive-icon.png differ
diff --git a/code/08-splitting-components/assets/favicon.png b/code/08-splitting-components/assets/favicon.png
new file mode 100644
index 00000000..e75f697b
Binary files /dev/null and b/code/08-splitting-components/assets/favicon.png differ
diff --git a/code/08-splitting-components/assets/icon.png b/code/08-splitting-components/assets/icon.png
new file mode 100644
index 00000000..a0b1526f
Binary files /dev/null and b/code/08-splitting-components/assets/icon.png differ
diff --git a/code/08-splitting-components/assets/splash.png b/code/08-splitting-components/assets/splash.png
new file mode 100644
index 00000000..0e89705a
Binary files /dev/null and b/code/08-splitting-components/assets/splash.png differ
diff --git a/code/08-splitting-components/babel.config.js b/code/08-splitting-components/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/code/08-splitting-components/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/code/08-splitting-components/components/GoalInput.js b/code/08-splitting-components/components/GoalInput.js
new file mode 100644
index 00000000..e69de29b
diff --git a/code/08-splitting-components/components/GoalItem.js b/code/08-splitting-components/components/GoalItem.js
new file mode 100644
index 00000000..9a8da317
--- /dev/null
+++ b/code/08-splitting-components/components/GoalItem.js
@@ -0,0 +1,23 @@
+import { StyleSheet, View, Text } from 'react-native';
+
+function GoalItem(props) {
+ return (
+
+ {props.text}
+
+ );
+}
+
+export default GoalItem;
+
+const styles = StyleSheet.create({
+ goalItem: {
+ margin: 8,
+ padding: 8,
+ borderRadius: 6,
+ backgroundColor: '#5e0acc',
+ },
+ goalText: {
+ color: 'white',
+ },
+});
diff --git a/code/08-splitting-components/package.json b/code/08-splitting-components/package.json
new file mode 100644
index 00000000..a56b658b
--- /dev/null
+++ b/code/08-splitting-components/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rncourse",
+ "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",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~44.0.0",
+ "expo-status-bar": "~1.2.0",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-native": "0.64.3",
+ "react-native-web": "0.17.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/code/09-utilizing-props/App.js b/code/09-utilizing-props/App.js
new file mode 100644
index 00000000..4c7edf46
--- /dev/null
+++ b/code/09-utilizing-props/App.js
@@ -0,0 +1,45 @@
+import { useState } from 'react';
+import { StyleSheet, View, FlatList } from 'react-native';
+
+import GoalItem from './components/GoalItem';
+import GoalInput from './components/GoalInput';
+
+export default function App() {
+ const [courseGoals, setCourseGoals] = useState([]);
+
+ function addGoalHandler(enteredGoalText) {
+ setCourseGoals((currentCourseGoals) => [
+ ...currentCourseGoals,
+ { text: enteredGoalText, id: Math.random().toString() },
+ ]);
+ }
+
+ return (
+
+
+
+ {
+ return ;
+ }}
+ keyExtractor={(item, index) => {
+ return item.id;
+ }}
+ alwaysBounceVertical={false}
+ />
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ appContainer: {
+ flex: 1,
+ paddingTop: 50,
+ paddingHorizontal: 16,
+ },
+ goalsContainer: {
+ flex: 5,
+ },
+});
diff --git a/code/09-utilizing-props/app.json b/code/09-utilizing-props/app.json
new file mode 100644
index 00000000..9a1223e7
--- /dev/null
+++ b/code/09-utilizing-props/app.json
@@ -0,0 +1,32 @@
+{
+ "expo": {
+ "name": "RNCourse",
+ "slug": "RNCourse",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/adaptive-icon.png",
+ "backgroundColor": "#FFFFFF"
+ }
+ },
+ "web": {
+ "favicon": "./assets/favicon.png"
+ }
+ }
+}
diff --git a/code/09-utilizing-props/assets/adaptive-icon.png b/code/09-utilizing-props/assets/adaptive-icon.png
new file mode 100644
index 00000000..03d6f6b6
Binary files /dev/null and b/code/09-utilizing-props/assets/adaptive-icon.png differ
diff --git a/code/09-utilizing-props/assets/favicon.png b/code/09-utilizing-props/assets/favicon.png
new file mode 100644
index 00000000..e75f697b
Binary files /dev/null and b/code/09-utilizing-props/assets/favicon.png differ
diff --git a/code/09-utilizing-props/assets/icon.png b/code/09-utilizing-props/assets/icon.png
new file mode 100644
index 00000000..a0b1526f
Binary files /dev/null and b/code/09-utilizing-props/assets/icon.png differ
diff --git a/code/09-utilizing-props/assets/splash.png b/code/09-utilizing-props/assets/splash.png
new file mode 100644
index 00000000..0e89705a
Binary files /dev/null and b/code/09-utilizing-props/assets/splash.png differ
diff --git a/code/09-utilizing-props/babel.config.js b/code/09-utilizing-props/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/code/09-utilizing-props/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/code/09-utilizing-props/components/GoalInput.js b/code/09-utilizing-props/components/GoalInput.js
new file mode 100644
index 00000000..8cfb5637
--- /dev/null
+++ b/code/09-utilizing-props/components/GoalInput.js
@@ -0,0 +1,48 @@
+import { useState } from 'react';
+import { View, TextInput, Button, StyleSheet } from 'react-native';
+
+function GoalInput(props) {
+ const [enteredGoalText, setEnteredGoalText] = useState('');
+
+ function goalInputHandler(enteredText) {
+ setEnteredGoalText(enteredText);
+ }
+
+ function addGoalHandler() {
+ props.onAddGoal(enteredGoalText);
+ setEnteredGoalText('');
+ }
+
+ return (
+
+
+
+
+ );
+}
+
+export default GoalInput;
+
+const styles = StyleSheet.create({
+ inputContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 24,
+ borderBottomWidth: 1,
+ borderBottomColor: '#cccccc',
+ },
+ textInput: {
+ borderWidth: 1,
+ borderColor: '#cccccc',
+ width: '70%',
+ marginRight: 8,
+ padding: 8,
+ },
+});
diff --git a/code/09-utilizing-props/components/GoalItem.js b/code/09-utilizing-props/components/GoalItem.js
new file mode 100644
index 00000000..9a8da317
--- /dev/null
+++ b/code/09-utilizing-props/components/GoalItem.js
@@ -0,0 +1,23 @@
+import { StyleSheet, View, Text } from 'react-native';
+
+function GoalItem(props) {
+ return (
+
+ {props.text}
+
+ );
+}
+
+export default GoalItem;
+
+const styles = StyleSheet.create({
+ goalItem: {
+ margin: 8,
+ padding: 8,
+ borderRadius: 6,
+ backgroundColor: '#5e0acc',
+ },
+ goalText: {
+ color: 'white',
+ },
+});
diff --git a/code/09-utilizing-props/package.json b/code/09-utilizing-props/package.json
new file mode 100644
index 00000000..a56b658b
--- /dev/null
+++ b/code/09-utilizing-props/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rncourse",
+ "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",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~44.0.0",
+ "expo-status-bar": "~1.2.0",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-native": "0.64.3",
+ "react-native-web": "0.17.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/code/10-handling-taps-with-pressable/App.js b/code/10-handling-taps-with-pressable/App.js
new file mode 100644
index 00000000..d32a480c
--- /dev/null
+++ b/code/10-handling-taps-with-pressable/App.js
@@ -0,0 +1,57 @@
+import { useState } from 'react';
+import { StyleSheet, View, FlatList } from 'react-native';
+
+import GoalItem from './components/GoalItem';
+import GoalInput from './components/GoalInput';
+
+export default function App() {
+ const [courseGoals, setCourseGoals] = useState([]);
+
+ function addGoalHandler(enteredGoalText) {
+ setCourseGoals((currentCourseGoals) => [
+ ...currentCourseGoals,
+ { text: enteredGoalText, id: Math.random().toString() },
+ ]);
+ }
+
+ function deleteGoalHandler(id) {
+ setCourseGoals((currentCourseGoals) => {
+ return currentCourseGoals.filter((goal) => goal.id !== id);
+ });
+ }
+
+ return (
+
+
+
+ {
+ return (
+
+ );
+ }}
+ keyExtractor={(item, index) => {
+ return item.id;
+ }}
+ alwaysBounceVertical={false}
+ />
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ appContainer: {
+ flex: 1,
+ paddingTop: 50,
+ paddingHorizontal: 16,
+ },
+ goalsContainer: {
+ flex: 5,
+ },
+});
diff --git a/code/10-handling-taps-with-pressable/app.json b/code/10-handling-taps-with-pressable/app.json
new file mode 100644
index 00000000..9a1223e7
--- /dev/null
+++ b/code/10-handling-taps-with-pressable/app.json
@@ -0,0 +1,32 @@
+{
+ "expo": {
+ "name": "RNCourse",
+ "slug": "RNCourse",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/adaptive-icon.png",
+ "backgroundColor": "#FFFFFF"
+ }
+ },
+ "web": {
+ "favicon": "./assets/favicon.png"
+ }
+ }
+}
diff --git a/code/10-handling-taps-with-pressable/assets/adaptive-icon.png b/code/10-handling-taps-with-pressable/assets/adaptive-icon.png
new file mode 100644
index 00000000..03d6f6b6
Binary files /dev/null and b/code/10-handling-taps-with-pressable/assets/adaptive-icon.png differ
diff --git a/code/10-handling-taps-with-pressable/assets/favicon.png b/code/10-handling-taps-with-pressable/assets/favicon.png
new file mode 100644
index 00000000..e75f697b
Binary files /dev/null and b/code/10-handling-taps-with-pressable/assets/favicon.png differ
diff --git a/code/10-handling-taps-with-pressable/assets/icon.png b/code/10-handling-taps-with-pressable/assets/icon.png
new file mode 100644
index 00000000..a0b1526f
Binary files /dev/null and b/code/10-handling-taps-with-pressable/assets/icon.png differ
diff --git a/code/10-handling-taps-with-pressable/assets/splash.png b/code/10-handling-taps-with-pressable/assets/splash.png
new file mode 100644
index 00000000..0e89705a
Binary files /dev/null and b/code/10-handling-taps-with-pressable/assets/splash.png differ
diff --git a/code/10-handling-taps-with-pressable/babel.config.js b/code/10-handling-taps-with-pressable/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/code/10-handling-taps-with-pressable/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/code/10-handling-taps-with-pressable/components/GoalInput.js b/code/10-handling-taps-with-pressable/components/GoalInput.js
new file mode 100644
index 00000000..8cfb5637
--- /dev/null
+++ b/code/10-handling-taps-with-pressable/components/GoalInput.js
@@ -0,0 +1,48 @@
+import { useState } from 'react';
+import { View, TextInput, Button, StyleSheet } from 'react-native';
+
+function GoalInput(props) {
+ const [enteredGoalText, setEnteredGoalText] = useState('');
+
+ function goalInputHandler(enteredText) {
+ setEnteredGoalText(enteredText);
+ }
+
+ function addGoalHandler() {
+ props.onAddGoal(enteredGoalText);
+ setEnteredGoalText('');
+ }
+
+ return (
+
+
+
+
+ );
+}
+
+export default GoalInput;
+
+const styles = StyleSheet.create({
+ inputContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 24,
+ borderBottomWidth: 1,
+ borderBottomColor: '#cccccc',
+ },
+ textInput: {
+ borderWidth: 1,
+ borderColor: '#cccccc',
+ width: '70%',
+ marginRight: 8,
+ padding: 8,
+ },
+});
diff --git a/code/10-handling-taps-with-pressable/components/GoalItem.js b/code/10-handling-taps-with-pressable/components/GoalItem.js
new file mode 100644
index 00000000..d629ae56
--- /dev/null
+++ b/code/10-handling-taps-with-pressable/components/GoalItem.js
@@ -0,0 +1,25 @@
+import { StyleSheet, View, Text, Pressable } from 'react-native';
+
+function GoalItem(props) {
+ return (
+
+
+ {props.text}
+
+
+ );
+}
+
+export default GoalItem;
+
+const styles = StyleSheet.create({
+ goalItem: {
+ margin: 8,
+ padding: 8,
+ borderRadius: 6,
+ backgroundColor: '#5e0acc',
+ },
+ goalText: {
+ color: 'white',
+ },
+});
diff --git a/code/10-handling-taps-with-pressable/package.json b/code/10-handling-taps-with-pressable/package.json
new file mode 100644
index 00000000..a56b658b
--- /dev/null
+++ b/code/10-handling-taps-with-pressable/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rncourse",
+ "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",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~44.0.0",
+ "expo-status-bar": "~1.2.0",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-native": "0.64.3",
+ "react-native-web": "0.17.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/code/11-adding-android-ripple-and-ios-styling/App.js b/code/11-adding-android-ripple-and-ios-styling/App.js
new file mode 100644
index 00000000..d32a480c
--- /dev/null
+++ b/code/11-adding-android-ripple-and-ios-styling/App.js
@@ -0,0 +1,57 @@
+import { useState } from 'react';
+import { StyleSheet, View, FlatList } from 'react-native';
+
+import GoalItem from './components/GoalItem';
+import GoalInput from './components/GoalInput';
+
+export default function App() {
+ const [courseGoals, setCourseGoals] = useState([]);
+
+ function addGoalHandler(enteredGoalText) {
+ setCourseGoals((currentCourseGoals) => [
+ ...currentCourseGoals,
+ { text: enteredGoalText, id: Math.random().toString() },
+ ]);
+ }
+
+ function deleteGoalHandler(id) {
+ setCourseGoals((currentCourseGoals) => {
+ return currentCourseGoals.filter((goal) => goal.id !== id);
+ });
+ }
+
+ return (
+
+
+
+ {
+ return (
+
+ );
+ }}
+ keyExtractor={(item, index) => {
+ return item.id;
+ }}
+ alwaysBounceVertical={false}
+ />
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ appContainer: {
+ flex: 1,
+ paddingTop: 50,
+ paddingHorizontal: 16,
+ },
+ goalsContainer: {
+ flex: 5,
+ },
+});
diff --git a/code/11-adding-android-ripple-and-ios-styling/app.json b/code/11-adding-android-ripple-and-ios-styling/app.json
new file mode 100644
index 00000000..9a1223e7
--- /dev/null
+++ b/code/11-adding-android-ripple-and-ios-styling/app.json
@@ -0,0 +1,32 @@
+{
+ "expo": {
+ "name": "RNCourse",
+ "slug": "RNCourse",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/adaptive-icon.png",
+ "backgroundColor": "#FFFFFF"
+ }
+ },
+ "web": {
+ "favicon": "./assets/favicon.png"
+ }
+ }
+}
diff --git a/code/11-adding-android-ripple-and-ios-styling/assets/adaptive-icon.png b/code/11-adding-android-ripple-and-ios-styling/assets/adaptive-icon.png
new file mode 100644
index 00000000..03d6f6b6
Binary files /dev/null and b/code/11-adding-android-ripple-and-ios-styling/assets/adaptive-icon.png differ
diff --git a/code/11-adding-android-ripple-and-ios-styling/assets/favicon.png b/code/11-adding-android-ripple-and-ios-styling/assets/favicon.png
new file mode 100644
index 00000000..e75f697b
Binary files /dev/null and b/code/11-adding-android-ripple-and-ios-styling/assets/favicon.png differ
diff --git a/code/11-adding-android-ripple-and-ios-styling/assets/icon.png b/code/11-adding-android-ripple-and-ios-styling/assets/icon.png
new file mode 100644
index 00000000..a0b1526f
Binary files /dev/null and b/code/11-adding-android-ripple-and-ios-styling/assets/icon.png differ
diff --git a/code/11-adding-android-ripple-and-ios-styling/assets/splash.png b/code/11-adding-android-ripple-and-ios-styling/assets/splash.png
new file mode 100644
index 00000000..0e89705a
Binary files /dev/null and b/code/11-adding-android-ripple-and-ios-styling/assets/splash.png differ
diff --git a/code/11-adding-android-ripple-and-ios-styling/babel.config.js b/code/11-adding-android-ripple-and-ios-styling/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/code/11-adding-android-ripple-and-ios-styling/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/code/11-adding-android-ripple-and-ios-styling/components/GoalInput.js b/code/11-adding-android-ripple-and-ios-styling/components/GoalInput.js
new file mode 100644
index 00000000..8cfb5637
--- /dev/null
+++ b/code/11-adding-android-ripple-and-ios-styling/components/GoalInput.js
@@ -0,0 +1,48 @@
+import { useState } from 'react';
+import { View, TextInput, Button, StyleSheet } from 'react-native';
+
+function GoalInput(props) {
+ const [enteredGoalText, setEnteredGoalText] = useState('');
+
+ function goalInputHandler(enteredText) {
+ setEnteredGoalText(enteredText);
+ }
+
+ function addGoalHandler() {
+ props.onAddGoal(enteredGoalText);
+ setEnteredGoalText('');
+ }
+
+ return (
+
+
+
+
+ );
+}
+
+export default GoalInput;
+
+const styles = StyleSheet.create({
+ inputContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 24,
+ borderBottomWidth: 1,
+ borderBottomColor: '#cccccc',
+ },
+ textInput: {
+ borderWidth: 1,
+ borderColor: '#cccccc',
+ width: '70%',
+ marginRight: 8,
+ padding: 8,
+ },
+});
diff --git a/code/11-adding-android-ripple-and-ios-styling/components/GoalItem.js b/code/11-adding-android-ripple-and-ios-styling/components/GoalItem.js
new file mode 100644
index 00000000..4116169e
--- /dev/null
+++ b/code/11-adding-android-ripple-and-ios-styling/components/GoalItem.js
@@ -0,0 +1,32 @@
+import { StyleSheet, View, Text, Pressable } from 'react-native';
+
+function GoalItem(props) {
+ return (
+
+ pressed && styles.pressedItem}
+ >
+ {props.text}
+
+
+ );
+}
+
+export default GoalItem;
+
+const styles = StyleSheet.create({
+ goalItem: {
+ margin: 8,
+ borderRadius: 6,
+ backgroundColor: '#5e0acc',
+ },
+ pressedItem: {
+ opacity: 0.5,
+ },
+ goalText: {
+ color: 'white',
+ padding: 8,
+ },
+});
diff --git a/code/11-adding-android-ripple-and-ios-styling/package.json b/code/11-adding-android-ripple-and-ios-styling/package.json
new file mode 100644
index 00000000..a56b658b
--- /dev/null
+++ b/code/11-adding-android-ripple-and-ios-styling/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rncourse",
+ "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",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~44.0.0",
+ "expo-status-bar": "~1.2.0",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-native": "0.64.3",
+ "react-native-web": "0.17.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/code/12-styling-the-modal-overlay/App.js b/code/12-styling-the-modal-overlay/App.js
new file mode 100644
index 00000000..61e98eb6
--- /dev/null
+++ b/code/12-styling-the-modal-overlay/App.js
@@ -0,0 +1,67 @@
+import { useState } from 'react';
+import { StyleSheet, View, FlatList, Button } from 'react-native';
+
+import GoalItem from './components/GoalItem';
+import GoalInput from './components/GoalInput';
+
+export default function App() {
+ const [modalIsVisible, setModalIsVisible] = useState(false);
+ const [courseGoals, setCourseGoals] = useState([]);
+
+ function startAddGoalHandler() {
+ setModalIsVisible(true);
+ }
+
+ function addGoalHandler(enteredGoalText) {
+ setCourseGoals((currentCourseGoals) => [
+ ...currentCourseGoals,
+ { text: enteredGoalText, id: Math.random().toString() },
+ ]);
+ }
+
+ function deleteGoalHandler(id) {
+ setCourseGoals((currentCourseGoals) => {
+ return currentCourseGoals.filter((goal) => goal.id !== id);
+ });
+ }
+
+ return (
+
+
+
+
+ {
+ return (
+
+ );
+ }}
+ keyExtractor={(item, index) => {
+ return item.id;
+ }}
+ alwaysBounceVertical={false}
+ />
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ appContainer: {
+ flex: 1,
+ paddingTop: 50,
+ paddingHorizontal: 16,
+ },
+ goalsContainer: {
+ flex: 5,
+ },
+});
diff --git a/code/12-styling-the-modal-overlay/app.json b/code/12-styling-the-modal-overlay/app.json
new file mode 100644
index 00000000..9a1223e7
--- /dev/null
+++ b/code/12-styling-the-modal-overlay/app.json
@@ -0,0 +1,32 @@
+{
+ "expo": {
+ "name": "RNCourse",
+ "slug": "RNCourse",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/adaptive-icon.png",
+ "backgroundColor": "#FFFFFF"
+ }
+ },
+ "web": {
+ "favicon": "./assets/favicon.png"
+ }
+ }
+}
diff --git a/code/12-styling-the-modal-overlay/assets/adaptive-icon.png b/code/12-styling-the-modal-overlay/assets/adaptive-icon.png
new file mode 100644
index 00000000..03d6f6b6
Binary files /dev/null and b/code/12-styling-the-modal-overlay/assets/adaptive-icon.png differ
diff --git a/code/12-styling-the-modal-overlay/assets/favicon.png b/code/12-styling-the-modal-overlay/assets/favicon.png
new file mode 100644
index 00000000..e75f697b
Binary files /dev/null and b/code/12-styling-the-modal-overlay/assets/favicon.png differ
diff --git a/code/12-styling-the-modal-overlay/assets/icon.png b/code/12-styling-the-modal-overlay/assets/icon.png
new file mode 100644
index 00000000..a0b1526f
Binary files /dev/null and b/code/12-styling-the-modal-overlay/assets/icon.png differ
diff --git a/code/12-styling-the-modal-overlay/assets/splash.png b/code/12-styling-the-modal-overlay/assets/splash.png
new file mode 100644
index 00000000..0e89705a
Binary files /dev/null and b/code/12-styling-the-modal-overlay/assets/splash.png differ
diff --git a/code/12-styling-the-modal-overlay/babel.config.js b/code/12-styling-the-modal-overlay/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/code/12-styling-the-modal-overlay/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/code/12-styling-the-modal-overlay/components/GoalInput.js b/code/12-styling-the-modal-overlay/components/GoalInput.js
new file mode 100644
index 00000000..3addc5a5
--- /dev/null
+++ b/code/12-styling-the-modal-overlay/components/GoalInput.js
@@ -0,0 +1,64 @@
+import { useState } from 'react';
+import { View, TextInput, Button, StyleSheet, Modal } from 'react-native';
+
+function GoalInput(props) {
+ const [enteredGoalText, setEnteredGoalText] = useState('');
+
+ function goalInputHandler(enteredText) {
+ setEnteredGoalText(enteredText);
+ }
+
+ function addGoalHandler() {
+ props.onAddGoal(enteredGoalText);
+ setEnteredGoalText('');
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default GoalInput;
+
+const styles = StyleSheet.create({
+ inputContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginBottom: 24,
+ padding: 16,
+ borderBottomWidth: 1,
+ borderBottomColor: '#cccccc',
+ },
+ textInput: {
+ borderWidth: 1,
+ borderColor: '#cccccc',
+ width: '100%',
+ padding: 8,
+ },
+ buttonContainer: {
+ marginTop: 16,
+ flexDirection: 'row',
+ },
+ button: {
+ width: 100,
+ marginHorizontal: 8
+ }
+});
diff --git a/code/12-styling-the-modal-overlay/components/GoalItem.js b/code/12-styling-the-modal-overlay/components/GoalItem.js
new file mode 100644
index 00000000..4116169e
--- /dev/null
+++ b/code/12-styling-the-modal-overlay/components/GoalItem.js
@@ -0,0 +1,32 @@
+import { StyleSheet, View, Text, Pressable } from 'react-native';
+
+function GoalItem(props) {
+ return (
+
+ pressed && styles.pressedItem}
+ >
+ {props.text}
+
+
+ );
+}
+
+export default GoalItem;
+
+const styles = StyleSheet.create({
+ goalItem: {
+ margin: 8,
+ borderRadius: 6,
+ backgroundColor: '#5e0acc',
+ },
+ pressedItem: {
+ opacity: 0.5,
+ },
+ goalText: {
+ color: 'white',
+ padding: 8,
+ },
+});
diff --git a/code/12-styling-the-modal-overlay/package.json b/code/12-styling-the-modal-overlay/package.json
new file mode 100644
index 00000000..a56b658b
--- /dev/null
+++ b/code/12-styling-the-modal-overlay/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rncourse",
+ "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",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~44.0.0",
+ "expo-status-bar": "~1.2.0",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-native": "0.64.3",
+ "react-native-web": "0.17.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/code/13-opening-and-closing-the-modal/App.js b/code/13-opening-and-closing-the-modal/App.js
new file mode 100644
index 00000000..fef72eb2
--- /dev/null
+++ b/code/13-opening-and-closing-the-modal/App.js
@@ -0,0 +1,76 @@
+import { useState } from 'react';
+import { StyleSheet, View, FlatList, Button } from 'react-native';
+
+import GoalItem from './components/GoalItem';
+import GoalInput from './components/GoalInput';
+
+export default function App() {
+ const [modalIsVisible, setModalIsVisible] = useState(false);
+ const [courseGoals, setCourseGoals] = useState([]);
+
+ function startAddGoalHandler() {
+ setModalIsVisible(true);
+ }
+
+ function endAddGoalHandler() {
+ setModalIsVisible(false);
+ }
+
+ function addGoalHandler(enteredGoalText) {
+ setCourseGoals((currentCourseGoals) => [
+ ...currentCourseGoals,
+ { text: enteredGoalText, id: Math.random().toString() },
+ ]);
+ endAddGoalHandler();
+ }
+
+ function deleteGoalHandler(id) {
+ setCourseGoals((currentCourseGoals) => {
+ return currentCourseGoals.filter((goal) => goal.id !== id);
+ });
+ }
+
+ return (
+
+
+
+
+ {
+ return (
+
+ );
+ }}
+ keyExtractor={(item, index) => {
+ return item.id;
+ }}
+ alwaysBounceVertical={false}
+ />
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ appContainer: {
+ flex: 1,
+ paddingTop: 50,
+ paddingHorizontal: 16,
+ },
+ goalsContainer: {
+ flex: 5,
+ },
+});
diff --git a/code/13-opening-and-closing-the-modal/app.json b/code/13-opening-and-closing-the-modal/app.json
new file mode 100644
index 00000000..9a1223e7
--- /dev/null
+++ b/code/13-opening-and-closing-the-modal/app.json
@@ -0,0 +1,32 @@
+{
+ "expo": {
+ "name": "RNCourse",
+ "slug": "RNCourse",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/adaptive-icon.png",
+ "backgroundColor": "#FFFFFF"
+ }
+ },
+ "web": {
+ "favicon": "./assets/favicon.png"
+ }
+ }
+}
diff --git a/code/13-opening-and-closing-the-modal/assets/adaptive-icon.png b/code/13-opening-and-closing-the-modal/assets/adaptive-icon.png
new file mode 100644
index 00000000..03d6f6b6
Binary files /dev/null and b/code/13-opening-and-closing-the-modal/assets/adaptive-icon.png differ
diff --git a/code/13-opening-and-closing-the-modal/assets/favicon.png b/code/13-opening-and-closing-the-modal/assets/favicon.png
new file mode 100644
index 00000000..e75f697b
Binary files /dev/null and b/code/13-opening-and-closing-the-modal/assets/favicon.png differ
diff --git a/code/13-opening-and-closing-the-modal/assets/icon.png b/code/13-opening-and-closing-the-modal/assets/icon.png
new file mode 100644
index 00000000..a0b1526f
Binary files /dev/null and b/code/13-opening-and-closing-the-modal/assets/icon.png differ
diff --git a/code/13-opening-and-closing-the-modal/assets/splash.png b/code/13-opening-and-closing-the-modal/assets/splash.png
new file mode 100644
index 00000000..0e89705a
Binary files /dev/null and b/code/13-opening-and-closing-the-modal/assets/splash.png differ
diff --git a/code/13-opening-and-closing-the-modal/babel.config.js b/code/13-opening-and-closing-the-modal/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/code/13-opening-and-closing-the-modal/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/code/13-opening-and-closing-the-modal/components/GoalInput.js b/code/13-opening-and-closing-the-modal/components/GoalInput.js
new file mode 100644
index 00000000..9fbfff22
--- /dev/null
+++ b/code/13-opening-and-closing-the-modal/components/GoalInput.js
@@ -0,0 +1,64 @@
+import { useState } from 'react';
+import { View, TextInput, Button, StyleSheet, Modal } from 'react-native';
+
+function GoalInput(props) {
+ const [enteredGoalText, setEnteredGoalText] = useState('');
+
+ function goalInputHandler(enteredText) {
+ setEnteredGoalText(enteredText);
+ }
+
+ function addGoalHandler() {
+ props.onAddGoal(enteredGoalText);
+ setEnteredGoalText('');
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default GoalInput;
+
+const styles = StyleSheet.create({
+ inputContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginBottom: 24,
+ padding: 16,
+ borderBottomWidth: 1,
+ borderBottomColor: '#cccccc',
+ },
+ textInput: {
+ borderWidth: 1,
+ borderColor: '#cccccc',
+ width: '100%',
+ padding: 8,
+ },
+ buttonContainer: {
+ marginTop: 16,
+ flexDirection: 'row',
+ },
+ button: {
+ width: 100,
+ marginHorizontal: 8
+ }
+});
diff --git a/code/13-opening-and-closing-the-modal/components/GoalItem.js b/code/13-opening-and-closing-the-modal/components/GoalItem.js
new file mode 100644
index 00000000..4116169e
--- /dev/null
+++ b/code/13-opening-and-closing-the-modal/components/GoalItem.js
@@ -0,0 +1,32 @@
+import { StyleSheet, View, Text, Pressable } from 'react-native';
+
+function GoalItem(props) {
+ return (
+
+ pressed && styles.pressedItem}
+ >
+ {props.text}
+
+
+ );
+}
+
+export default GoalItem;
+
+const styles = StyleSheet.create({
+ goalItem: {
+ margin: 8,
+ borderRadius: 6,
+ backgroundColor: '#5e0acc',
+ },
+ pressedItem: {
+ opacity: 0.5,
+ },
+ goalText: {
+ color: 'white',
+ padding: 8,
+ },
+});
diff --git a/code/13-opening-and-closing-the-modal/package.json b/code/13-opening-and-closing-the-modal/package.json
new file mode 100644
index 00000000..a56b658b
--- /dev/null
+++ b/code/13-opening-and-closing-the-modal/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rncourse",
+ "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",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~44.0.0",
+ "expo-status-bar": "~1.2.0",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-native": "0.64.3",
+ "react-native-web": "0.17.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/code/14-working-with-images/App.js b/code/14-working-with-images/App.js
new file mode 100644
index 00000000..fef72eb2
--- /dev/null
+++ b/code/14-working-with-images/App.js
@@ -0,0 +1,76 @@
+import { useState } from 'react';
+import { StyleSheet, View, FlatList, Button } from 'react-native';
+
+import GoalItem from './components/GoalItem';
+import GoalInput from './components/GoalInput';
+
+export default function App() {
+ const [modalIsVisible, setModalIsVisible] = useState(false);
+ const [courseGoals, setCourseGoals] = useState([]);
+
+ function startAddGoalHandler() {
+ setModalIsVisible(true);
+ }
+
+ function endAddGoalHandler() {
+ setModalIsVisible(false);
+ }
+
+ function addGoalHandler(enteredGoalText) {
+ setCourseGoals((currentCourseGoals) => [
+ ...currentCourseGoals,
+ { text: enteredGoalText, id: Math.random().toString() },
+ ]);
+ endAddGoalHandler();
+ }
+
+ function deleteGoalHandler(id) {
+ setCourseGoals((currentCourseGoals) => {
+ return currentCourseGoals.filter((goal) => goal.id !== id);
+ });
+ }
+
+ return (
+
+
+
+
+ {
+ return (
+
+ );
+ }}
+ keyExtractor={(item, index) => {
+ return item.id;
+ }}
+ alwaysBounceVertical={false}
+ />
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ appContainer: {
+ flex: 1,
+ paddingTop: 50,
+ paddingHorizontal: 16,
+ },
+ goalsContainer: {
+ flex: 5,
+ },
+});
diff --git a/code/14-working-with-images/app.json b/code/14-working-with-images/app.json
new file mode 100644
index 00000000..9a1223e7
--- /dev/null
+++ b/code/14-working-with-images/app.json
@@ -0,0 +1,32 @@
+{
+ "expo": {
+ "name": "RNCourse",
+ "slug": "RNCourse",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/adaptive-icon.png",
+ "backgroundColor": "#FFFFFF"
+ }
+ },
+ "web": {
+ "favicon": "./assets/favicon.png"
+ }
+ }
+}
diff --git a/code/14-working-with-images/assets/adaptive-icon.png b/code/14-working-with-images/assets/adaptive-icon.png
new file mode 100644
index 00000000..03d6f6b6
Binary files /dev/null and b/code/14-working-with-images/assets/adaptive-icon.png differ
diff --git a/code/14-working-with-images/assets/favicon.png b/code/14-working-with-images/assets/favicon.png
new file mode 100644
index 00000000..e75f697b
Binary files /dev/null and b/code/14-working-with-images/assets/favicon.png differ
diff --git a/code/14-working-with-images/assets/icon.png b/code/14-working-with-images/assets/icon.png
new file mode 100644
index 00000000..a0b1526f
Binary files /dev/null and b/code/14-working-with-images/assets/icon.png differ
diff --git a/code/14-working-with-images/assets/images/goal.png b/code/14-working-with-images/assets/images/goal.png
new file mode 100644
index 00000000..04c24ca0
Binary files /dev/null and b/code/14-working-with-images/assets/images/goal.png differ
diff --git a/code/14-working-with-images/assets/splash.png b/code/14-working-with-images/assets/splash.png
new file mode 100644
index 00000000..0e89705a
Binary files /dev/null and b/code/14-working-with-images/assets/splash.png differ
diff --git a/code/14-working-with-images/babel.config.js b/code/14-working-with-images/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/code/14-working-with-images/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/code/14-working-with-images/components/GoalInput.js b/code/14-working-with-images/components/GoalInput.js
new file mode 100644
index 00000000..97a2d5ef
--- /dev/null
+++ b/code/14-working-with-images/components/GoalInput.js
@@ -0,0 +1,81 @@
+import { useState } from 'react';
+import {
+ View,
+ TextInput,
+ Button,
+ StyleSheet,
+ Modal,
+ Image,
+} from 'react-native';
+
+function GoalInput(props) {
+ const [enteredGoalText, setEnteredGoalText] = useState('');
+
+ function goalInputHandler(enteredText) {
+ setEnteredGoalText(enteredText);
+ }
+
+ function addGoalHandler() {
+ props.onAddGoal(enteredGoalText);
+ setEnteredGoalText('');
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default GoalInput;
+
+const styles = StyleSheet.create({
+ inputContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ padding: 16,
+ backgroundColor: '#311b6b',
+ },
+ image: {
+ width: 100,
+ height: 100,
+ margin: 20,
+ },
+ textInput: {
+ borderWidth: 1,
+ borderColor: '#e4d0ff',
+ backgroundColor: '#e4d0ff',
+ color: '#120438',
+ borderRadius: 6,
+ width: '100%',
+ padding: 16,
+ },
+ buttonContainer: {
+ marginTop: 16,
+ flexDirection: 'row',
+ },
+ button: {
+ width: 100,
+ marginHorizontal: 8,
+ },
+});
diff --git a/code/14-working-with-images/components/GoalItem.js b/code/14-working-with-images/components/GoalItem.js
new file mode 100644
index 00000000..4116169e
--- /dev/null
+++ b/code/14-working-with-images/components/GoalItem.js
@@ -0,0 +1,32 @@
+import { StyleSheet, View, Text, Pressable } from 'react-native';
+
+function GoalItem(props) {
+ return (
+
+ pressed && styles.pressedItem}
+ >
+ {props.text}
+
+
+ );
+}
+
+export default GoalItem;
+
+const styles = StyleSheet.create({
+ goalItem: {
+ margin: 8,
+ borderRadius: 6,
+ backgroundColor: '#5e0acc',
+ },
+ pressedItem: {
+ opacity: 0.5,
+ },
+ goalText: {
+ color: 'white',
+ padding: 8,
+ },
+});
diff --git a/code/14-working-with-images/package.json b/code/14-working-with-images/package.json
new file mode 100644
index 00000000..a56b658b
--- /dev/null
+++ b/code/14-working-with-images/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rncourse",
+ "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",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~44.0.0",
+ "expo-status-bar": "~1.2.0",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-native": "0.64.3",
+ "react-native-web": "0.17.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/code/15-finished/App.js b/code/15-finished/App.js
new file mode 100644
index 00000000..5813d9e9
--- /dev/null
+++ b/code/15-finished/App.js
@@ -0,0 +1,80 @@
+import { useState } from 'react';
+import { StyleSheet, View, FlatList, Button } from 'react-native';
+import { StatusBar } from 'expo-status-bar';
+
+import GoalItem from './components/GoalItem';
+import GoalInput from './components/GoalInput';
+
+export default function App() {
+ const [modalIsVisible, setModalIsVisible] = useState(false);
+ const [courseGoals, setCourseGoals] = useState([]);
+
+ function startAddGoalHandler() {
+ setModalIsVisible(true);
+ }
+
+ function endAddGoalHandler() {
+ setModalIsVisible(false);
+ }
+
+ function addGoalHandler(enteredGoalText) {
+ setCourseGoals((currentCourseGoals) => [
+ ...currentCourseGoals,
+ { text: enteredGoalText, id: Math.random().toString() },
+ ]);
+ endAddGoalHandler();
+ }
+
+ function deleteGoalHandler(id) {
+ setCourseGoals((currentCourseGoals) => {
+ return currentCourseGoals.filter((goal) => goal.id !== id);
+ });
+ }
+
+ return (
+ <>
+
+
+
+
+
+ {
+ return (
+
+ );
+ }}
+ keyExtractor={(item, index) => {
+ return item.id;
+ }}
+ alwaysBounceVertical={false}
+ />
+
+
+ >
+ );
+}
+
+const styles = StyleSheet.create({
+ appContainer: {
+ flex: 1,
+ paddingTop: 50,
+ paddingHorizontal: 16,
+ },
+ goalsContainer: {
+ flex: 5,
+ },
+});
diff --git a/code/15-finished/app.json b/code/15-finished/app.json
new file mode 100644
index 00000000..a6e1bffd
--- /dev/null
+++ b/code/15-finished/app.json
@@ -0,0 +1,33 @@
+{
+ "expo": {
+ "name": "RNCourse",
+ "slug": "RNCourse",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "backgroundColor": "#1e085a",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "foregroundImage": "./assets/adaptive-icon.png",
+ "backgroundColor": "#FFFFFF"
+ }
+ },
+ "web": {
+ "favicon": "./assets/favicon.png"
+ }
+ }
+}
diff --git a/code/15-finished/assets/adaptive-icon.png b/code/15-finished/assets/adaptive-icon.png
new file mode 100644
index 00000000..03d6f6b6
Binary files /dev/null and b/code/15-finished/assets/adaptive-icon.png differ
diff --git a/code/15-finished/assets/favicon.png b/code/15-finished/assets/favicon.png
new file mode 100644
index 00000000..e75f697b
Binary files /dev/null and b/code/15-finished/assets/favicon.png differ
diff --git a/code/15-finished/assets/icon.png b/code/15-finished/assets/icon.png
new file mode 100644
index 00000000..a0b1526f
Binary files /dev/null and b/code/15-finished/assets/icon.png differ
diff --git a/code/15-finished/assets/images/goal.png b/code/15-finished/assets/images/goal.png
new file mode 100644
index 00000000..04c24ca0
Binary files /dev/null and b/code/15-finished/assets/images/goal.png differ
diff --git a/code/15-finished/assets/splash.png b/code/15-finished/assets/splash.png
new file mode 100644
index 00000000..0e89705a
Binary files /dev/null and b/code/15-finished/assets/splash.png differ
diff --git a/code/15-finished/babel.config.js b/code/15-finished/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/code/15-finished/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/code/15-finished/components/GoalInput.js b/code/15-finished/components/GoalInput.js
new file mode 100644
index 00000000..97a2d5ef
--- /dev/null
+++ b/code/15-finished/components/GoalInput.js
@@ -0,0 +1,81 @@
+import { useState } from 'react';
+import {
+ View,
+ TextInput,
+ Button,
+ StyleSheet,
+ Modal,
+ Image,
+} from 'react-native';
+
+function GoalInput(props) {
+ const [enteredGoalText, setEnteredGoalText] = useState('');
+
+ function goalInputHandler(enteredText) {
+ setEnteredGoalText(enteredText);
+ }
+
+ function addGoalHandler() {
+ props.onAddGoal(enteredGoalText);
+ setEnteredGoalText('');
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default GoalInput;
+
+const styles = StyleSheet.create({
+ inputContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ padding: 16,
+ backgroundColor: '#311b6b',
+ },
+ image: {
+ width: 100,
+ height: 100,
+ margin: 20,
+ },
+ textInput: {
+ borderWidth: 1,
+ borderColor: '#e4d0ff',
+ backgroundColor: '#e4d0ff',
+ color: '#120438',
+ borderRadius: 6,
+ width: '100%',
+ padding: 16,
+ },
+ buttonContainer: {
+ marginTop: 16,
+ flexDirection: 'row',
+ },
+ button: {
+ width: 100,
+ marginHorizontal: 8,
+ },
+});
diff --git a/code/15-finished/components/GoalItem.js b/code/15-finished/components/GoalItem.js
new file mode 100644
index 00000000..4116169e
--- /dev/null
+++ b/code/15-finished/components/GoalItem.js
@@ -0,0 +1,32 @@
+import { StyleSheet, View, Text, Pressable } from 'react-native';
+
+function GoalItem(props) {
+ return (
+
+ pressed && styles.pressedItem}
+ >
+ {props.text}
+
+
+ );
+}
+
+export default GoalItem;
+
+const styles = StyleSheet.create({
+ goalItem: {
+ margin: 8,
+ borderRadius: 6,
+ backgroundColor: '#5e0acc',
+ },
+ pressedItem: {
+ opacity: 0.5,
+ },
+ goalText: {
+ color: 'white',
+ padding: 8,
+ },
+});
diff --git a/code/15-finished/package.json b/code/15-finished/package.json
new file mode 100644
index 00000000..a56b658b
--- /dev/null
+++ b/code/15-finished/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rncourse",
+ "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",
+ "eject": "expo eject"
+ },
+ "dependencies": {
+ "expo": "~44.0.0",
+ "expo-status-bar": "~1.2.0",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-native": "0.64.3",
+ "react-native-web": "0.17.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.9"
+ },
+ "private": true
+}
diff --git a/extra-files/goal.png b/extra-files/goal.png
new file mode 100644
index 00000000..04c24ca0
Binary files /dev/null and b/extra-files/goal.png differ
diff --git a/extra-files/starting-project.zip b/extra-files/starting-project.zip
new file mode 100644
index 00000000..5cd61032
Binary files /dev/null and b/extra-files/starting-project.zip differ
diff --git a/slides/slides.pdf b/slides/slides.pdf
new file mode 100644
index 00000000..10a99801
Binary files /dev/null and b/slides/slides.pdf differ