In our last section, we have built a Flutter Quiz App using List. However, we tried to do in a simple way.
For one reason.
We did not want to make things complicated firstly. Secondly, we tried to give an idea how we can use list in Flutter.
Earlier we have learned that list is a collection of items. It is an ordered collection. Moreover it is sequential.
Because a list in Flutter is sequential, the index number starts with 0. And every index refers to an item.
As a result, we can easily manipulate a list. And to do that, Dart comes with a lot of list methods. In the previous section, we have also seen some examples.
This time, we will build one Quiz App in a more object oriented way.
Why we need Object Oriented Style?
We know Dart is an object oriented programming language. As a consequence, everything in Dart is Object.
That means, behind every Object there is a Class. Which defines the Type of that object.
We can relate this concept to Flutter. Because, in Flutter, everything is Widget, which is actually a Class.
There are four pillars in Object Oriented Programming.
- Abstraction
- Encapsulation
- Inheritance
- Polymorphism
When we implement these concepts, we understand them better. While building the Quiz App, in an object oriented way, we will implement first two concepts. Abstraction and Encapsulation.
Firstly, we have used two Flutter packages to give our App a unique look and feel.
Therefore, let us first add the dependencies in our “pubspec.yaml” file.
dependencies:
flutter:
sdk: flutter
google_fonts: ^2.3.1
rflutter_alert: ^2.0.4
We have used Google Fonts package before. However, the alert dialog package is new. Basically when our quiz ends, it will alert the user with a dialog box.
![Flutter list Quiz App alert dialog at the end](https://i0.wp.com/sanjibsinha.com/wp-content/uploads/2022/03/Flutter-list-Quiz-App-alert-dialog-at-the-end.jpg?ssl=1)
We will develop the previous Quiz App in an Object oriented way.
To implement the first principle of Abstraction, we will create a Question class in our model folder.
class Question {
String question;
bool answer;
Question(
this.question,
this.answer,
);
}
Now, inside another class QuizMaster we can add the list items by using the Question Class constructors.
As a result, we can establish a relationship between question and answer in one object.
We have also declared an integer as the list index and set the value to 0.
import 'question.dart';
class QuizMaster {
int _indexNumber = 0;
final List<Question> _quiz = [
Question('29 - 3 = 26', true),
Question('711 - 4 = 677', false),
Question('455 * 3 = 1365', true),
Question('76 / 8 = 9.5', true),
Question('Many Thanks, press any button to end the Quiz.', true),
];
void nextQuestion() {
if (_indexNumber <= _quiz.length) {
_indexNumber++;
}
}
String getQuestion() {
return _quiz[_indexNumber].question;
}
bool getAnswer() {
return _quiz[_indexNumber].answer;
}
bool isFinished() {
if (_indexNumber >= _quiz.length - 1) {
return true;
} else {
return false;
}
}
void reset() {
_indexNumber = 0;
}
}
As we see in the above code, now we are in more control to move forward our questions.
Not only that, we can get the question, answer and move to the next question.
Encapsulation and Private property
Apart from that, we have marked each property of the class as “private” by adding an underscore “_” before the name of the property.
class QuizMaster {
int _indexNumber = 0;
final List<Question> _quiz = [
...
It implements another important principle of object oriented programming. Encapsulation.
As we have made each property “private”, no one can access these properties from outside this class.
Now we can create a QuizMaster object in our top-level main() function. And, consequently, we can create our whole quiz app.
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
import 'model/quiz_master.dart';
QuizMaster quizMaster = QuizMaster();
void main() => runApp(const QuizApp());
class QuizApp extends StatelessWidget {
const QuizApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'Mathematical Quiz',
style: GoogleFonts.lacquer(
textStyle: TextStyle(
color: Colors.purple.shade600,
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
),
body: const SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: QuizPage(),
),
),
),
);
}
}
class QuizPage extends StatefulWidget {
const QuizPage({Key? key}) : super(key: key);
@override
_QuizPageState createState() => _QuizPageState();
}
class _QuizPageState extends State<QuizPage> {
List<Text> check = [];
void checkAnswer(bool userPickedAnswer) {
bool correctAnswer = quizMaster.getAnswer();
setState(() {
if (quizMaster.isFinished() == true) {
Alert(
context: context,
title: 'Quiz Completed.',
desc:
'We\'ve reached the end. Thanks for taking part. Meet you again.',
).show();
quizMaster.reset();
check = [];
} else {
if (userPickedAnswer == correctAnswer) {
check.add(
Text(
'You\'re Right. Well Done.',
style: GoogleFonts.laila(
textStyle: const TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
);
} else {
check.add(
Text(
'You\'re Wrong. Try Again.',
style: GoogleFonts.laila(
textStyle: const TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
);
}
quizMaster.nextQuestion();
}
});
}
@override
Widget build(BuildContext context) {
return SafeArea(
top: true,
child: Center(
child: ListView(
children: <Widget>[
Container(
margin: const EdgeInsets.fromLTRB(20.0, 5.0, 20.0, 5.0),
alignment: Alignment.center,
child: Text(
quizMaster.getQuestion(),
style: GoogleFonts.lalezar(
textStyle: const TextStyle(
fontSize: 30.0,
fontWeight: FontWeight.bold,
),
),
),
),
checkingAnswer('Correct', true),
checkingAnswer('Wrong', false),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: check,
),
)
],
),
),
);
}
Container checkingAnswer(String corerctOrWrong, bool trueOrFalse) {
return Container(
padding: const EdgeInsets.all(5.0),
child: ElevatedButton(
onPressed: () {
checkAnswer(trueOrFalse);
},
child: Text(
corerctOrWrong,
style: GoogleFonts.laila(
textStyle: const TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
),
);
}
}
we have used common Container Widget for two Elevated Buttons.
Therefore, we can refactor this part of code. We have extracted a common method only changing the parameters.
checkingAnswer('Correct', true),
checkingAnswer('Wrong', false),
How Flutter List Quiz App works
Here the setState() method plays a crucial role. As it updates the question. It also checks whether the answer is correct or wrong.
After that, it adds the answers to another list which we show inside a Column below.
![Flutter list Quiz App first example](https://i0.wp.com/sanjibsinha.com/wp-content/uploads/2022/03/Flutter-list-Quiz-App-first-example.jpg?ssl=1)
The first mathematical equation is quite easy. It is correct.
When user presses the Correct button, it will rightly show the answer and displays the next equation.
Suppose this time user cannot choose the correct answer. Accordingly, it will show that user is wrong, and displays the third equation as follows.
![Flutter list Quiz App second example](https://i0.wp.com/sanjibsinha.com/wp-content/uploads/2022/03/Flutter-list-Quiz-App-second-example.jpg?ssl=1)
As we progress, one time, we reach the end point.
Now it is time to press any button to finish the quiz.
![Flutter list Quiz App third example](https://i0.wp.com/sanjibsinha.com/wp-content/uploads/2022/03/Flutter-list-Quiz-App-third-example.jpg?ssl=1)
Completing this journey, will finally display the alert dialog that we have seen before.
You can just create the same Quiz App by following the above code. Or, you can clone the respective GitHub repository.
Leave a Reply