4.4 Aussagen und Antworten in einer Klasse vereinen
Ihre Quiz-App kann jetzt überprüfen, ob Benutzer:innen die richtige Antwort ausgewählt haben. Aber es ist nicht so elegant, dass man bei Änderungen (z.B. wenn Sie weitere Fragen zum Quiz hinzufügen möchten) die beiden Listen statements und answers immer einzeln anpassen müssen. Es wäre doch hübscher, wenn man die Aussage und die zugehörige Antwort jeweils zusammen abspeichern könnte.
Genau das können Sie unter Verwendung einer Klasse erreichen. Sie werden in diesem Abschnitt eine Klasse erstellen, die eine Statement-Eigenschaft und eine Answer-Eigenschaft besitzt.
Statement Klasse erstellen
- Öffnen Sie in Ihrem Visual Studio Code Projekt den Explorer (1).
- Klicken Sie auf das lib Verzeichnis Ihres Flutter-Projekts (2).
- Bewegen Sie Ihren Maus-Cursor zu dem Titel Ihres Flutter-Projekts bis die Icons mit den Dateien, Ordnern etc. erscheinen und klicken Sie auf das Icon zum Erzeugen von neuen Dateien (3).
- Erstellen Sie eine Klasse für Ihre Aussagen (4).
- Kopieren Sie den folgenden Code in Ihre Datei für Ihre Aussagen-Klasse.
class Statement { String statementText = ''; bool statementAnswer = false; Statement({required String s, required bool a}) { statementText = s; statementAnswer = a; } }
Theorie
Vertiefung zu Klassen, Objekte und Konstruktoren
Wie Sie bereits in Abschnitt 2.2 gelernt haben, beginnt man Klassen per Konvention mit einem Grossbuchstaben: class Statement {...}. Bei einer Klasse handelt es sich nicht um ein Objekt, sondern sie wird verwendet um neue Objekte (auch Instanzen genannt) zu erzeugen. Eine häufig verwendete Analogie ist die Veranschaulichung von Klassen und Objekten mit Blaupausen und Häusern. Mithilfe einer Blaupause kann man beliebig viele Häuser bauen. Und in objektorientierten Programmiersprachen lassen sich mithilfe einer Klasse beliebig viele Objekte erzeugen.
In unserer Klasse Statement bezeichnet man die beiden Variablen statementText und statementAnswer als Attribute. Die Attribute repräsentieren die Daten (oder den Zustand) eines Objekts. Jede Instanz einer Klasse besitzt ihre eigenen Attribute.
In der Programmiersprache Dart sind alle Typen standardmässig nicht-nullfähig (non-nullable), d.h. eine Variablenzuweisung wie String statement; würde vom Kompiler nicht akzeptiert werden. Nicht-nullfähige Variablen müssen immer mit Nicht-Null-Werten initialisiert werden, deshalb werden in unserer Klasse Statement die beiden Attribute mit den Werten '' (entspricht einem leeren String) und false initialisiert.
Eine andere wichtige Eigenschaften von Klassen sind Methoden. Sie bestimmen welche Aktionen die von der Klasse erzeugten Objekte später ausführen können.
Konstruktoren sollten Ihnen auch bereits aus Abschnitt 2.3 bekannt sein. In unserem Beispiel wird mit diesem Code ein Konstruktor erzeugt, welcher ein Argument vom Typ String und ein Argument vom Typ bool annehmen kann und mit diesen Werten die Attribute eines Statement-Objekts initialisieren kann.
Statement({required String s, required bool a}) {
statementText = s;
statementAnswer = a;
}
Wenn wir jetzt z.B. in unserer main.dart Datei eine neues Statement-Objekt (oder neue Instanz der Statement-Klasse) kreieren wollen, müssten wir wie folgt vorgehen.
Statement testStatement =
Statement(s: 'Geöffnete Apps brauchen viel Akku.', a: false);
Auf der rechten Seite der Zuweisung wird der Konstruktor der Statement-Klasse aufgerufen. Dieser Aufruf erzeugt eine neues Objekt und weist es der Variablen testStatement vom Datentyp Statement zu. Die Attribute statementText und statementAnswer des Objekts testStatement entsprechen dann 'Geöffnete Apps brauchen viel Akku.' und false.
Statement-Objekt erzeugen
- Wechseln Sie wieder in ihre main.dart Datei nachdem Sie die Klasse Statement erzeugt haben.
- Fügen Sie dort als zweite Import-Anweisung den Code import 'statement.dart'; ein. Mit dieser Anweisung machen Sie Ihre main.dart Datei mit der Statement-Klasse vertraut.
- In der Klasse _QuizPageState erstellen Sie als nächstes die Liste statementsAndAnwers und fügen dort all Ihre Aussagen und Antworten aus den Listen statements und answers ein.
- Löschen Sie den Code für die Listen statements und answers wieder.
- Da Ihr erstes Text-Widget noch auf die alte statements Liste verweist, müssen Sie dort ebenfalls eine Anpassung vornehmen. Über den Code statementsAndAnswers[statementCounter].statementText erhalten Sie Zugriff auf das Attribut statementText.
- Weiter müssen Sie innerhalb der beiden onPressed-Methoden der Wert für die Extraktion des booleschen Ausdruck auf statementsAndAnswers[statementCounter].statementAnswer anpassen.
Hier der Code inklusive allen Anpassungen:
import 'package:flutter/material.dart';
// 2
import 'statement.dart';
void main() {
runApp(QuizApp());
}
class QuizApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.grey.shade900,
body: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: QuizPage(),
),
),
),
);
}
}
class QuizPage extends StatefulWidget {
@override
_QuizPageState createState() => _QuizPageState();
}
class _QuizPageState extends State {
List<Icon> scoreKeeper = [];
// 3
List statementsAndAnswers = [
Statement(s: 'Geöffnete Apps brauchen viel Akku.', a: false),
Statement(
s: 'Eine Kamera mit mehr Megapixeln resultiert in besseren Bildern.',
a: false),
Statement(s: '5G frisst nicht mehr Daten als 4G.', a: true),
];
int statementCounter = 0;
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
flex: 5,
child: Padding(
padding: EdgeInsets.all(10.0),
child: Center(
child: Text(
// 5
statementsAndAnswers[statementCounter].statementText,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25.0,
color: Colors.white,
),
),
),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.all(15.0),
child: TextButton(
style: TextButton.styleFrom(backgroundColor: Colors.green),
child: Text(
'Wahr',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
),
onPressed: () {
// 6
bool correctAnswer =
statementsAndAnswers[statementCounter].statementAnswer;
if (correctAnswer == true) {
scoreKeeper.add(Icon(Icons.check, color: Colors.green));
} else {
scoreKeeper.add(Icon(Icons.close, color: Colors.red));
}
setState(() {
statementCounter++;
});
},
),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.all(15.0),
child: TextButton(
style: TextButton.styleFrom(backgroundColor: Colors.red),
child: Text(
'Falsch',
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
),
),
onPressed: () {
// 6
bool correctAnswer =
statementsAndAnswers[statementCounter].statementAnswer;
if (correctAnswer == false) {
scoreKeeper.add(Icon(Icons.check, color: Colors.green));
} else {
scoreKeeper.add(Icon(Icons.close, color: Colors.red));
}
setState(() {
statementCounter++;
});
},
),
),
),
Row(
children: scoreKeeper,
)
],
);
}
}
Über die Statement-Klasse haben Sie Ihre Aussagen und Antworten vereint. Und von dieser Klasse können Sie statementsAndAnswers-Objekte erzeugen. Sobald Sie aber zur dritten Aussage gelangen stürzt Ihre App immer noch ab. Dieses Problem beheben Sie in dem nächsten Abschnitt.