How to make a Flutter list Quiz App

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
Flutter list Quiz App alert dialog at the end

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
Flutter list Quiz App first example

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
Flutter list Quiz App second example

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
Flutter list Quiz App third example

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.

What Next

Books at Leanpub

Books in Apress

My books at Amazon

Courses at Educative

GitHub repository

Technical blog

Twitter

Comments

5 responses to “How to make a Flutter list Quiz App”

  1. […] the Quiz Master App we have built in the last […]

  2. […] questions keep haunting Flutter learners. Certainly, we want to create either Web App, or Mobile App. And that is the first reason why we learn […]

  3. […] In the above code, a list contains a map. A map is an object that associates keys and values. […]

  4. […] questions keep haunting Flutter learners. Certainly, we want to create either Web App, or Mobile App. And that is the first reason why we learn […]

Leave a Reply