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!

Flutter Private Properties

Unlike Java, Dart doesn’t have the keywords public, protected, and private. If an identifier starts with an underscore _, it’s private to its library. Libraries not only provide APIs, but are a unit of privacy: identifiers that start with an underscore _ are visible only inside the library.

Every Dart app is a library, even if it doesn’t use a library directive. The import and library directives can help you create a modular and shareable code base.

//File Name: main.dart
import 'package:flutter/material.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 = [
      'What\'s your favorite color?',
      'What\'s your favorite animal?'
    ];
    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>[
            Text(questions[_questionIndex]),
            ElevatedButton(
              child: Text('Answer1'),
              //onPressed takes a function
              onPressed: _answerQuestion,
            ),
            ElevatedButton(
              child: Text('Answer2'),
              onPressed: () => print('Answer2 chosen!'),
            ),
            ElevatedButton(
              child: Text('Answer3'),
              onPressed: () {
                //...multiline function body
                print('Answer3 chosen');
              },
            ),
          ],
        ),
      ),
    );
  } //build()
} //MyApp()

Output

Output

Happy Coding!

Dart Named Arguments

There are two types of arguments for a constructor in dart.

  1. Positional arguments – These are commonly used. For Positional arguments you have to remember which argument goes in to which place.
  2. Named arguments – This is a new concept if you belong to C++, Java or PHP. For Named Arguments you don’t need to remember the place/position of the argument. Named Arguments are optional but can be made mandatory using @required
class Person{

  String name="";
  int age=0;
  
  Person({String inputName="", int inputAge=0}){

    this.name = inputName;
    this.age = inputAge;
  }//Person()
  
  Person.veryOld(this.name){
    age = 60;
  }//Person.veryOld()
}//Person


void main() {

  var p1 = Person(inputName: "Rizwan", inputAge: 34);
  var p2 = Person(inputName: "Ali", inputAge: 32);
  var p3 = Person.veryOld("Usama");

  print(p1.name); //Rizwan
  print(p1.age);  //34

  print(p2.name); //Ali
  print(p2.age);  //32

  print(p3.name); //Usama
  print(p3.age);  //60
  
}

Dart Constructor

class Person {

  String name="";
  int age=0;
  
  Person(String inputName, int inputAge){
    this.name = inputName;
    this.age = inputAge;
  }//Person()
}//Person

void main() {

  var p1 = Person("Rizwan", 34);
  var p2 = Person("Ali", 32);
  
  print(p1.name); //Rizwan
  print(p1.age);  //34
  
  print(p2.name); //Ali
  print(p2.age);  //32
}

In this constructor we are using positional arguments i.e, inputName and inputAge instead of named arguments.

First Flutter Widget

What is Flutter?

Flutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, desktop, and embedded devices from a single codebase.

I assume that you have successfully installed flutter either with Android Studio IDE or VS Code and have successfully configured an emulator.

If you want to install flutter, kindly follow this official documentation.

https://flutter.dev/docs/get-started/install

//File Name: main.dart
//Flutter first Hello World! Widget
import 'package:flutter/material.dart';

void main() {

   runApp(MyApp());
}

class MyApp extends StatelessWidget {

   @override
   Widget build(BuildContext context){

      return MaterialApp(home: Text('Hello World!');

   }//build
}//MyApp
  1. Import Flutter package
  2. declare main function and run app i.e, runApp(MyApp)
  3. MyApp is a custom class that inherits from Stateless Widget(Later we’ll also look into StatfullWidget)
  4. MyApp class overrides build() method.
  5. build method returns a Widget and takes Build context as a parameter.
  6. MaterialApp() is a special widget that we should return. It does some base setup to turn combinational widgets into real app. The MaterialApp() takes a lot of named arguments, home is one of the named arguments where we use a text widget Text(‘Hello World!’).

Output: The output involves no UI and is a simple Hello World!

First Flutter Plugin – Output

I hope the logic and code behind the first flutter widget make sense.

Happy coding!