3

I have written the code below for a quiz app but am stuck on finding a way to compare the selected radio button with the correct ans and also delaying the transition to the next question sufficiently for a visual indication of the choice made. Tried using switch statement and the _counter variable but results into an error

The following NoSuchMethodError was thrown while handling a gesture:
I/flutter (28574): The method '[]' was called on null.

I am not familiar enough to understand where/what the error might be referring to or what may be wrong with that approach(switch statement). Any corrections/directions/hints would be appreciated. Thanks.

import 'dart:async';
import 'package:flutter/material.dart';

Map<String, Map<String, String>> questionBank = {
  "1": {
    "question": "What is the capital of Canada?",
    "ans1": "Toronto",
    "ans2": "Montreal",
    "ans3": "Ottawa",
    "ans4": "Vancouver",
    "coAns": "Ottawa"
  },
  "2": {
    "question": "What is the capital of the United States of America?",
    "ans1": "New York",
    "ans2": "California",
    "ans3": "Texas",
    "ans4": "Washington DC",
    "coAns": "Washington DC"
  },
  "3": {
    "question": "What is the capital of Nigeria?",
    "ans1": "Abuja",
    "ans2": "Lagos",
    "ans3": "Port Harcourt",
    "ans4": "Makurdi",
    "coAns": "Abuja"
  },
  "4": {
    "question": "What is the capital of England?",
    "ans1": "Britain",
    "ans2": "Scotland",
    "ans3": "London",
    "ans4": "Edinburgh",
    "coAns": "London"
  },
  "5": {
    "question": "What is the capital of China?",
    "ans1": "Beijing",
    "ans2": "Shanghai",
    "ans3": "Tianjin",
    "ans4": "Taiwan",
    "coAns": "Beijing"
  },
};

void main() {
  runApp(new _questionDisplay());
}

class _questionDisplay extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(home: new QuestDis());
  }
}

class QuestDis extends StatefulWidget {
  QuestDis({Key key}) : super(key: key);

  @override
  _QuestDisState createState() => new _QuestDisState();
}

class _QuestDisState extends State<QuestDis> {

  @override
  var _counter = 1;
  var bkgrdColor = Colors.blue[50];
  int radioValue = 0;

  int ans1Value = 1;
  int ans2Value = 2;
  int ans3Value = 3;
  int ans4Value = 4;

  void handleRadioValueChanged(int value) {
    setState(() {
      radioValue = value;
      /*
      switch (radioValue) {
        case 1:
          bkgrdColor = (questionBank[_counter]["coAns"] ==
                  questionBank[_counter][ans1Value])
              ? Colors.green[50]
              : Colors.red[50];
          break;
        case 2:
          bkgrdColor = (questionBank[_counter]["coAns"] ==
                  questionBank[_counter][ans2Value])
              ? Colors.green[50]
              : Colors.red[50];
          break;
        case 3:
          bkgrdColor = (questionBank[_counter]["coAns"] ==
                  questionBank[_counter][ans3Value])
              ? Colors.green[50]
              : Colors.red[50];
          break;
        case 4:
          bkgrdColor = (questionBank[_counter]["coAns"] ==
                  questionBank[_counter][ans4Value])
              ? Colors.green[50]
              : Colors.red[50];
          break;
      }
       */
      _counter++;
      radioValue = 0;
    });
  }

  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        leading: new IconButton(icon: new Icon(Icons.menu), onPressed: null),
        title: new Text('quizApp'),
      ),
      body: new Container(
        child: new Column(
          children: [
            new Expanded(
              child: new Container(
                child: new Column(
                  children: [
                    new Expanded(
                      child: new Container(
                        child: new Card(
                          color: bkgrdColor,
                          child: new Row(
                            children: <Widget>[
                              new Text(
                                  "${questionBank[_counter.toString()]["question"]}"),
                            ],
                          ),
                        ),
                      ),
                    ),
                    new Expanded(
                      child: new Container(
                        child: new Card(
                          child: new Column(
                            children: [
                              new Row(
                                children: <Widget>[
                                  new Radio<int>(
                                      value: ans1Value,
                                      groupValue: radioValue,
                                      onChanged: handleRadioValueChanged),
                                  new Text(
                                      "${questionBank[_counter.toString()]["ans1"]}")
                                ],
                              ),
                              new Divider(),
                              new Row(
                                children: <Widget>[
                                  new Radio<int>(
                                      value: ans2Value,
                                      groupValue: radioValue,
                                      onChanged: handleRadioValueChanged),
                                  new Text(
                                      "${questionBank[_counter.toString()]["ans2"]}")
                                ],
                              ),
                              new Divider(),
                              new Row(
                                children: <Widget>[
                                  new Radio<int>(
                                      value: ans3Value,
                                      groupValue: radioValue,
                                      onChanged: handleRadioValueChanged),
                                  new Text(
                                      "${questionBank[_counter.toString()]["ans3"]}")
                                ],
                              ),
                              new Divider(),
                              new Row(
                                children: <Widget>[
                                  new Radio<int>(
                                      value: ans4Value,
                                      groupValue: radioValue,
                                      onChanged: handleRadioValueChanged),
                                  new Text(
                                      "${questionBank[_counter.toString()]["ans4"]}")
                                ],
                              ),
                            ],
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
Stephen Docy
  • 4,738
  • 7
  • 18
  • 31
driftavalii
  • 1,299
  • 1
  • 14
  • 21
  • Have you considered using objects instead of a Map? I don't actually know what the Radio class does, could you explain it to me? – OhMad May 10 '17 at 17:26
  • It's a radio button (material design widget). My challenge is getting user input and comparing the choice selected with the correct answer, how would an object address this? I am currently using the radio widget for user input and associating that with a text widget. – driftavalii May 10 '17 at 17:35
  • what did you actually manage to get? did you manage to get the answer the user clicked or just some of the radio button? – OhMad May 10 '17 at 17:41
  • The way it currently works, the user selects an answer which increments _counter and advances to the next question and resets the radioValue to zero. The radioValue represents the user choice and using that value plus the _counter should (I think) allow me to track user choice/question. That is what I have the switch statement for but that throws an error. – driftavalii May 10 '17 at 17:47

1 Answers1

3

If I were you I would actually create a question object (Don't take any of this for granted I'm quickly writing this out on my ipad so the syntax might not be on point...):

class Question {
  String question;
  List<String> answers;
  String correctAnswer;
  Question(question, answers, correctAnswer);
}

Now you can use the object to create a list of questions:

List<Question> questions = [
  new Question("What is 2+2", ["2", "3", "4"], "4"),
  // create as many questions as you want
];

you can keep your counter variable and actually use it as an index.

Let's say you manage to get the answer the user chose in a string called chosenAnswer. In your setState() I would do something like this:

if (chosenAnswer == questions[_counter].correctAnswer){
  // answer was correct
} else {
  // answer is false
}
// and dont forget to increment your counter
_counter++;

In order to display the questions and answers to the user, you can again use the counter variable as the index and access every possible answer in the Question object that is stored in the list.

Hope I could help a bit

OhMad
  • 6,871
  • 20
  • 56
  • 85
  • Thanks for your help! This worked out brilliantly, one more question, any pointers to how to add a delay before resetting the variables radioValue and bkgrdColor anytime counter is incremented? I tried using Timer(duration, "callback function") but it seems to have no effect. – driftavalii May 10 '17 at 19:10
  • No problem! Unfortunately, I've not been using Flutter for a very long time, so I don't really know an exact answer. I found this solution which I guess is worth a shot http://stackoverflow.com/questions/18449846/how-can-i-sleep-a-dart-program – OhMad May 10 '17 at 19:27