Buttons und Modal
Dauer: 45 Minuten
Button
für neues Todo hinzufügen- Modal für die Eingabe vorbereiten
- FAB (Floating Action Button) für neues Todo
Button
für neues Todo hinzufügen
Mit der Button
-Komponente
von React Native erhalten wir eine einfache Möglichkeit, um Buttons zu
erstellen. Im Vergleich zu Pressable
ist Button
weniger flexibel,
dafür aber einfacher zu verwenden.
Wir fügen einen Button unterhalb der Todo-Liste hinzu. Zusätzlich
ersetzen wir View
mit SafeAreaView
, um den Button innerhalb des
sichtbaren Bereichs auf iPhones zu platzieren. Dies erzeugt etwas
Abstand unterhalb des Buttons und hat keine Auswirkung in Android.
// App.js: Button und SafeAreaView importieren
import { Button, SafeAreaView, StyleSheet } from 'react-native';
// Rest bleibt gleich
export default function App() {
return (
<SafeAreaView style={styles.container}>
<TodoList todos={todos} />
<Button
title="Todo hinzufügen"
onPress={() => alert('Neues TODO!')}
/>
<StatusBar style="auto" />
</SafeAreaView>
);
}
// Styles bleiben gleich
Beim Antippen des Buttons wird ein einfacher Alert-Dialog angezeigt. Diesen ersetzen wir im nächsten Schritt durch einen eigenen Dialog.
Modal für die Eingabe vorbereiten
Modal
ist eine Komponente in React Native, mit der wir einen anpassbaren Dialog
anzeigen können.
Das Modal wird mehrere Komponenten enthalten:
Text
für die Überschrift (z.B. “Todo hinzufügen”)TextInput
für die Eingabe des TodosButton
zum AbbrechenButton
zum SpeichernView
s für die Anordnung der Komponenten
Unser Modal wird also eine etwas komplexere Komponente sein und daher werden wir es in eine eigene Komponente auslagern:
// components/TodoModal.jsx
import { Button, Modal, StyleSheet, Text, View } from 'react-native';
export default function TodoModal({ visible }) {
return (
<Modal
visible={visible}
animationType="slide"
onRequestClose={() => {}}
>
<View style={styles.container}>
<View style={styles.content}>
<Text style={styles.text}>Todo hinzufügen</Text>
<View style={styles.buttons}>
<Button title="Abbrechen" onPress={() => {}} />
<Button title="Speichern" onPress={() => {}} />
</View>
</View>
</View>
</Modal>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
marginTop: 22,
},
content: {
margin: 20,
backgroundColor: 'white',
borderRadius: 20,
padding: 35,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
text: {
fontSize: 20,
marginBottom: 15,
textAlign: 'center',
},
buttons: {
flexDirection: 'row',
},
});
Damit die beiden Buttons nebeneinander
dargestellt werden, haben wir sie mit einer View
-Komponente umgeben, die ein
horizontales Flexbox-Layout in den Styles definiert:
<View style={styles.buttons}>
<Button title="Abbrechen" onPress={() => {}} />
<Button title="Speichern" onPress={() => {}} />
</View>
// im styles-Objekt:
buttons: {
flexDirection: 'row',
},
Flexbox ist ein eigenes Thema, welches aus CSS stammt und in React Native übernommen wurde. Dazu gibt es einen eigenen Abschnitt im Videokurs.
Bei Bedarf kann dies spontan oder später in einer kleineren Gruppe am Whiteboard und einer eigenen Beispiel-App mit einfachen, farbigen View
-Komponenten erklärt werden.
Diese Komponente erhält als prop einen Boolean-Wert visible
, mit dem bestimmt
wird, ob das Modal sichtbar ist oder nicht:
// sichtbar
<Modal visible={true} … />
// nicht sichtbar
<Modal visible={false} … />
Es gibt drei Callback-Funktionen, die wir noch implementieren werden:
onRequestClose
: Wird aufgerufen, wenn der Benutzer das Modal schließt.onPress
für den Button “Abbrechen”onPress
für den Button “Speichern”
Wir können unser TodoModal
nun in App
einbinden:
import TodoModal from './components/TodoModal';
// Rest bleibt gleich
export default function App() {
return (
<SafeAreaView style={styles.container}>
<TodoModal visible={false} />
// Rest bleibt gleich
</SafeAreaView>
);
}
Das Modal wird zunächst nicht angezeigt, da visible
hier auf false
gesetzt ist.
Modal anzeigen und schließen
Unser Modal kann sich in zwei Zuständen befinden: sichtbar oder unsichtbar. Wir wollen nun das Modal anzeigen, wenn der Button “Todo hinzufügen” gedrückt wird. Dazu fügen wir in App
einen State modalVisible
hinzu, der den Zustand des Modals speichert.
import { useState } from 'react';
// Rest bleibt gleich
export default function App() {
const [modalVisible, setModalVisible] = useState(false);
return (
<SafeAreaView style={styles.container}>
<TodoModal visible={modalVisible} />
<TodoList todos={todos} />
<Button
title="Todo hinzufügen"
onPress={() => setModalVisible(true)}
/>
<StatusBar style="auto" />
</SafeAreaView>
);
}
// Styles bleiben gleich
Nun wird das Modal sichtbar, wenn der Button gedrückt wird. Um das Modal zu schließen, werden wir an das TodoModal
eine Callback-Funktion im prop onCancel
übergeben:
// in App.js
export default function App() {
const [modalVisible, setModalVisible] = useState(false);
return (
<SafeAreaView style={styles.container}>
<TodoModal
visible={modalVisible}
onCancel={() => setModalVisible(false)}
/>
<TodoList todos={todos} />
// Rest bleibt gleich
</SafeAreaView>
);
};
Damit erhält das TodoModal
die Funktion onCancel
, die aufgerufen wird, wenn der Button “Abbrechen” gedrückt wird, was wir
in TodoModal
entsprechend implementieren
müssen:
// components/TodoModal.jsx
// Zuerst den neuen Prop onCancel zuweisen
export default function TodoModal({ visible, onCancel }) {
return (
<Modal
visible={visible}
animationType="slide"
onRequestClose={onCancel}
>
<View style={styles.container}>
<View style={styles.content}>
<Text style={styles.text}>Todo hinzufügen</Text>
<View style={styles.buttons}>
<Button title="Abbrechen" onPress={onCancel} /> // onCancel aufrufen
<Button title="Speichern" onPress={() => {}} />
</View>
</View>
</View>
</Modal>
);
}
Nun lässt sich das Modal mit dem Button “Abbrechen” schließen.
Auf der nächsten Seite werden wir die Texteingabe für das neue Todo umsetzen.
Zuvor fügen wir noch einen FAB (Floating Action Button) hinzu, der den Button für das Öffnen des Alert-Dialogs ersetzt.
FAB (Floating Action Button) für neues Todo
Wie in den beiden Apps für Flutter und Android wollen wir auch in React Native einen FAB hinzufügen.
Es gibt zwar keine spezielle Komponente für einen FAB in React Native,
aber wir können eine einfache FAB-Komponente selbst erstellen. Dazu
verwenden wir Standardkomponenten wie z.B. Pressable
aus React Native:
// components/FAB.jsx
import { StyleSheet, Pressable, View, Text } from 'react-native';
export default function FAB({ onPress }) {
return (
<View style={styles.container}>
<Pressable
onPress={onPress}
style={({ pressed }) => [
styles.fab,
{ backgroundColor: pressed ? '#0288D1' : '#03A9F4' },
]}
>
<Text style={styles.fabText}>+</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
bottom: 30,
right: 30,
},
fab: {
backgroundColor: '#03A9F4',
width: 56,
height: 56,
borderRadius: 12,
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.8,
shadowRadius: 2,
elevation: 5,
},
fabText: {
color: 'white',
fontSize: 24,
},
});
Durch position: 'absolute'
legen wir für den FAB eine feste Position im Screen fest.
Nun müssen wir den FAB noch in App.js
einbinden, indem wir die FAB
-Komponente
importieren und damit den Button
ersetzen:
import { Alert, SafeAreaView, StyleSheet } from 'react-native';
import FAB from './components/FAB';
// Rest bleibt gleich
export default function App() {
const [modalVisible, setModalVisible] = useState(false);
return (
<SafeAreaView style={styles.container}>
<TodoModal
visible={modalVisible}
onCancel={() => setModalVisible(false)}
/>
<TodoList todos={todos} />
<FAB onPress={() => setModalVisible(true)}/>
</SafeAreaView>
);
};
// Styles bleiben gleich
Wir verwenden ein einfaches +
als Text im FAB. In Expo gibt es
eine umfangreiche Sammlung von Icons:
- Dokumentation: https://docs.expo.dev/guides/icons/
- Liste der verfügbaren Icons: https://icons.expo.fyi/
Sicherlich finden sich auch im Web komplette FAB-Komponenten als Open Source Pakete, die mit npm installiert werden können.