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 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 Todos
  • Button zum Abbrechen
  • Button zum Speichern
  • Views 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.

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:

Sicherlich finden sich auch im Web komplette FAB-Komponenten als Open Source Pakete, die mit npm installiert werden können.

PAUSE