Flutter Maps

We need to improve the functionality of our app. Currently we have a list of just two questions and no answer is associated with the question.

var questions = [
      'What\'s your favorite color?',
      'What\'s your favorite animal?'
    ];

The question should also have the information about available answers. We need more complex object which can group multiple pieces of information together. 

We could create a new class which has all these features we need and use that class to create objects and that is perfectly fine. However, we’ll use a different data structure that’s built in to Dart and that is a map

map is a collection of key value pairs. Lets create a map where we have three questions and four answers for each question as below,

var questions = [
      {
        'questionText': 'What\'s your favorite color?',
        'answers': [ 'Black', 'Red', 'Green', 'White'],
      },
      {
        'questionText': 'What\'s your favorite animal?',
        'answers': [ 'Leopard', 'Markhor', 'Deer', 'Rabbit'],
      },
      {
        'questionText': 'What\'s your favorite lake?',
        'answers': [ 'Saif-ul-Malook', 'Doodi pat sar', 'Katora', 'Saral'],
      },
    ];

Here is our main.dart file where we are going to map lists to widgets.

//File Name: main.dart
import 'package:flutter/material.dart';

/* ./ means look in the main folder where main.dart resides and then the
*  name of the file which you wana import
 */

import './question.dart';
import './answer.dart';

/*
* The arrow function allows us to create a simplified function consisting of a single expression.
* We can omit the curly brackets and the return keyword
*/

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  /*
   * createState() is a method that takes no arguments but returns a State object
   * which is connected to a StatefulWidget
   */
  @override
  State<StatefulWidget> createState() {
    return _MyAppState();
  } //createState()
} //StatefulWidget

/* A leading underscore makes the property private, here leading underscore will turn
 * this public class in to private class. Now MyAppState class can only be used in
 * main.dart file, in our case in MyApp class.
 */
class _MyAppState extends State<MyApp> {
  var _questionIndex = 0;

  void _answerQuestion() {
    setState(() {
      _questionIndex++;
    });
    print(_questionIndex);
  }

  @override
  Widget build(BuildContext context) {
    var questions = [
      {
        'questionText': 'What\'s your favorite color?',
        'answers': ['Black', 'Red', 'Green', 'White'],
      },
      {
        'questionText': 'What\'s your favorite animal?',
        'answers': ['Leopard', 'Markhor', 'Deer', 'Rabbit'],
      },
      {
        'questionText': 'What\'s your favorite lake?',
        'answers': ['Saif-ul-Malook', 'Doodi pat sar', 'Katora', 'Saral'],
      },
    ];
    return MaterialApp(
      /*
      * A Scaffold Widget provides a framework which implements the basic material design visual
      * layout structure of the flutter app.
       */
      home: Scaffold(
        appBar: AppBar(
          title: Text('First Flutter App'),
        ),
        body: Column(
          children: <Widget>[
            Question(
              questions[_questionIndex]['questionText'],
            ),
            /*
            * for answers, transform list of maps in to list of widgets
            * map() method executes a function (anonymous) which we have to pass as an argument.
            * it returns a widget
            * ... is spread operator, it takes a list and pulls all the values in the list out of it
            * and add them to surrounding list as individual value. Means, we don't add a list to a list
            * but values of the list to a list.
             */
            ...(questions[_questionIndex]['answers'] as List<String>)
                .map((answer) {
              return Answer(_answerQuestion, answer);
            }).toList(),
          ],
        ),
      ),
    );
  } //build()
} //MyApp()

Here is answer.dart file

//FileName: answer.dart
import 'package:flutter/material.dart';

class Answer extends StatelessWidget {
  final Function selectHandler;
  final String answerText;

  Answer(this.selectHandler, this.answerText);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity, ////double.infinity means occupy the whole screen (width in this case)
      margin: EdgeInsets.all(10), //apply margin of 10 pts to all 4 sides
      /*
      * We have provided 3 named arguments here to ElevatedButton()
      * 1. child: which is a Text() widget
      * 2. onPressed: which is a function we get via constructor
      * 3. style: where we change the color, padding and text style
       */
      child: ElevatedButton(
        child: Text(answerText),
        //onPressed takes a function
        onPressed: selectHandler,
        style: ElevatedButton.styleFrom(
          primary: Colors.blue, //the button's background color
          onPrimary: Colors.white, //the button's text color

          padding: EdgeInsets.symmetric(horizontal: 50, vertical: 20),
          textStyle: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
        ), //.styleFrom
      ), //ElevatedButton
    ); //Container
  }
}

The question widget will remain unchanged.

//FileName: question.dart
import 'package:flutter/material.dart';

class Question extends StatelessWidget {
  /*
  * final keywords tells dart that this value will never change after its
  * initialization here int he constructor
   */
  final String questionText;

  /* Lets have a constructor to initialize this widget class questionText
  *  Now the first positional argument passed to this constructor will be stored in
  *  questionText variable
   */
  Question(this.questionText);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity, //double.infinity means occupy the whole screen (width in this case)
      margin: EdgeInsets.all(10), //apply margin of 10 pts to all 4 sides
      child: Text(
        questionText,
        style: TextStyle(fontSize: 26),
        textAlign: TextAlign.center,
      ), //Text
    ); //Container()
  } //build
} //Question

Here is the output

Output – Mapping Lists to Widgets

Happy Coding!