# Building a Stateful App

  • The widgets we've seen so far are stateless widgets, meaning that once created they are immutable, as they do not keep any state information.
  • If you want to have an app that interacts with your users, you expect things to change. As an example, we will build a currency converter, where the result must change based on the user input. Therefore we will build a stateful widget.

# What is state?

DEFINITION

State is information that can be used when a widget is built and can change during the lifetime of a widget. It's not the widget itself that will change, it's the state.

# Stateless vs. stateful widget

Stateless widget Statefull widget
No mutable state Has mutable state
Overrides the build() method Overides the createState() method and returns State
Use when the UI depends on information in the object itself Use when the UI can change dynamically

# How to implement a stateful widget?

4 STEPS

  1. Create a class that extends a stateful widget, override the createState method that returns a State
  2. Create the state class itself, with properties that may change
  3. Implement the build() method that will actually create the UI
  4. Call the setState method to change the value of the properties and the UI will be automatically re-rendered

# Building a Currency Converter app

# Create the app

  • Create a new flutter application with name currency_converter
  • Create the following directories in the lib folder to organize your code:
    • models for our models (you figured out this one yourself)
    • pages for the pages (as you might have guessed)
    • widgets for our widgets (yes, what did you expect)
  • Delete widget_test.dart in the test folder
  • Replace the content of main.dart with the following code:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

import 'package:flutter/material.dart';
import 'package:currency_converter/pages/converter.dart';

void main() {
  runApp(const CurrencyConverterApp());
}

class CurrencyConverterApp extends StatelessWidget {
  // This widget is the root of your application.
  const CurrencyConverterApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Currency Converter',
      theme: ThemeData(
        primarySwatch: Colors.green,
      ),
      home: const ConverterPage(),
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
code description
import 'package:flutter/material.dart'; this package contains all Flutter widgets implementing Material Design
import 'package:currency_converter/pages/converter.dart'; import the file that contains the implementation of our first page (we will create this file in a moment)
main entry point of a Flutter application
runApp will run our Currency Converter app
class CurrencyConverterApp ... our app is a StatelessWidget
MaterialApp() defines that the app will use material design
debugShowCheckedModeBanner remove that uggly debug banner
theme we will use a green theme (for the banner, buttons, ...)
home: const ConverterPage() property to define the widget for the default route of the app, this will be the first (and only) page to show when the App starts

# Create a very simple page

  • In the pages folder, create a new file converter.dart
  • Paste the following code
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

import 'package:flutter/material.dart';

class ConverterPage extends StatefulWidget {
  const ConverterPage({Key? key}) : super(key: key);

  
  State<StatefulWidget> createState() => _ConverterPageState();
}

class _ConverterPageState extends State {
  String _name = '';

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Currency Converter"),
      ),
      body: Container(
        padding: const EdgeInsets.all(15.0),
        child: Column(
          children: <Widget>[
            TextField(
              decoration:
                  const InputDecoration(hintText: 'Please enter your name'),
              onChanged: (String string) {
                setState(() {
                  _name = string;
                });
              },
            ),
            Container(
              padding: const EdgeInsets.only(top: 15.0),
              child: Text("Hello $_name!"),
            ),
          ],
        ),
      ),
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  • After running the app, you should see the page with a textbox you can type your name in
  • When typing the name, the hello message changes immediately
Hello
  • Congrats, you've created your first stateful widget

# 4 Steps to create a stateful widget

Let's explain the code from above with a little more detail

# STEP 1

  • Since we need state, our converter page is a stateful widget and will extend a stateful widget.
  • Here we'll override the createState method, and using the fat arrow operator, we'll return a new _ConverterPageState.
class ConverterPage extends StatefulWidget {
  const ConverterPage({Key? key}) : super(key: key);

  
  State<StatefulWidget> createState() => _ConverterPageState();
}
1
2
3
4
5
6

# STEP 2

  • Next, we need to create the state.
  • As a naming convention, it's a good idea to put an underscore to emphasize that this class is private.
  • Also, we'll give it the same name as the widget's name, plus state. So, in this case, _ConverterPageState.
  • Create the state-properties that may change. In our case, the _name, which is also private.
class _ConverterPageState extends State {
  String _name = '';
1
2

# STEP 3

  • Implement the build() method that will actually create the UI
  • We'll return a Scaffold with an appBar property whose title is a Text "Currency Converter"
  • On the page, we use a TextField. We want to do something when the text in the TextField changes. So, we need to handle the onChanged event.

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Currency Converter"),
      ),
      body: Container(
        padding: const EdgeInsets.all(15.0),
        child: Column(
          children: <Widget>[
            TextField(
              decoration:
                  const InputDecoration(hintText: 'Please enter your name'),
              onChanged: (String string) {
    ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

TIP

We added a decoration to the TextField that takes an InputDecoration. Among other things, you can specify a hintText. More on this later.

# STEP 4

  • Inside the function that handles the onChanged event, we call the setState method to update the name property that we have defined above, with the new value of the TextField (string parameter).
  • Under the TextField we placed a Text widget to give feedback to our user. This text will be updated when the name changes.
    ...
              onChanged: (String string) {
                setState(() {
                  _name = string;
                });
              },
            ),
            Container(
              padding: const EdgeInsets.only(top: 15.0),
              child: Text("Hello $_name!"),
            ),
    ...
1
2
3
4
5
6
7
8
9
10
11
12
  • We have completed the steps, required to use the state in our app.

CODE SNIPPETS

It is very easy to insert some Flutter code snippets in Visual Studio Code. Just type st and from the intellisense popup, choose the snippet you want to insert and type tab.

snippet

# Improving the UI

  • Since the goal of our app was to create a currency converter, let's change the UI so the user can input an amount. We'll add some buttons and make life for our user a little bit easier using some text decorations. A lot of UI modifications, so let's get started.

  • Add a default textstyle and a formpadding variable to the build method



 
 




  Widget build(BuildContext context) {
    TextStyle? textStyle = Theme.of(context).textTheme.bodyText1;
    double formPadding = 5.0;
    return Scaffold(
    ...
1
2
3
4
5
6

# Using a TextEditingController

  • In the hello example, we used the onChanged event to write the changes in the name from the TextField widget to the _name property. Now we will use a TextEditingController. Whenever a user modifies a text field with an associated TextEditingController the text field updates its value, and the controller can notify any listeners.
  • Delete the _name property and add a TextEditingController instead.

 


class _ConverterPageState extends State {
  TextEditingController amountController = TextEditingController();
  ...
1
2
3
  • Define two state properties _fromamount and _toamount. They will be used to display the amount to be converted and the converted amount respectively.



 
 

class _ConverterPageState extends State {
  TextEditingController amountController = TextEditingController();

  String _fromamount = "";
  String _toamount = "";
1
2
3
4
5

# A Textfield with InputDecoration

  • Replace the TextField with the Padding widget. We use the default top and bottom padding. Our TextField has some nice user-friendly decorations and a rounded BorderStyle.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

            Padding(
              padding: EdgeInsets.only(bottom: formPadding, top: formPadding),
              child: TextField(
                controller: amountController,
                style: textStyle,
                keyboardType: TextInputType.number,
                decoration: InputDecoration(
                  hintText: "e.g. 17.85",
                  labelText: "Amount",
                  labelStyle: textStyle,
                  border: OutlineInputBorder(
                    borderRadius: BorderRadius.circular(5.0),
                  ),
                ),
              ),
            ),
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Remark

We removed the onChanged event from the TextField and used our amountController instead. More on this right away.

  • Replace the one Container with two Containers. The Containers will be used to display the Text's with the amount to be converted and the converted amount. As you can see, we've increased the size of the text.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

            Container(
              margin: EdgeInsets.only(top: formPadding * 5),
              child: Text(
                _fromamount,
                style: textStyle,
                textScaleFactor: 1.2,
              ),
            ),
            Container(
              margin: EdgeInsets.only(top: formPadding),
              child: Text(
                _toamount,
                style: textStyle,
                textScaleFactor: 3,
              ),
            ),
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Adding Buttons

  • To check if everything is still working we add two buttons above the two containers. One button to calculate the conversion. The other one to reset the amount. We will also add some extra padding space above the buttons. Since we like the buttons to be side by side, we use a Row widget with two ElevatedButton children and a Container to add some space between the buttons.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

            Container(
              height: formPadding,
            ),
            Row(children: [
              Expanded(
                child: ElevatedButton(
                  onPressed: () {
                    _calculate();
                  },
                  child: const Text(
                    'Convert',
                    textScaleFactor: 1.5,
                  ),
                ),
              ),
              Container(
                width: formPadding,
              ),
              Expanded(
                child: ElevatedButton(
                  onPressed: () {
                    _reset();
                  },
                  child: const Text(
                    'Reset',
                    textScaleFactor: 1.5,
                  ),
                ),
              ),
            ]),
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# Programming the onPressed event handlers

  • In order the buttons to work, we need to implement the event handlers _reset and _calculate.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

  void _reset() {
    amountController.text = '';
    setState(() {
      _fromamount = '';
      _toamount = '';
    });
  }

  void _calculate() {
    double amount = double.parse(amountController.text);
    double result = amount;

    setState(() {
      _fromamount = '${amount.toStringAsFixed(3)} amount';
      _toamount = '${result.toStringAsFixed(3)} TODO';
    });
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Remark

  • As you can see, the amount you type is automatically updated in the amountController. So you don't need to program the onChanged any more.
  • To update the Text widgets, you need to use the setState method to change _fromamount and _toamount. The calculation of the currency conversion is a TODO.
  • Try to run your app. It should look like a currency converter a little bit more.
Todo
  • There are two things missing. We can't select the currencies we want to be converted. And the conversion as well needs to be programmed. Let's fix this in the next part.

# Using Models

# Assets

  • Let us first add the flags of the currencies that will be supported within our app.
  • Download assets
  • Create a folder assets in the root of your application and copy the png's into it.
  • Next open pubspec.yaml and add the assets to your application








 
 
 
 
 
 
 
 

flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  assets:
    - assets/CAD.png
    - assets/CNY.png
    - assets/EUR.png
    - assets/GBP.png
    - assets/INR.png
    - assets/SGD.png
    - assets/USD.png
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Currency class

  • Create a new file currency.dart in the models folder
  • Next create a Currency class
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

class Currency {
  String name;
  String shortname;
  double _value;

  Currency(this.name, this.shortname, this._value);

  double get value {
    return _value;
  }

  set value(double value) {
    if (value > 0) {
      _value = value;
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • Our Currency class has three fields: name, shortname and _value
  • For name and shortname, we use the default getter and setter, since there's no extra code.
  • Because the value of a currency can't be negative, we need to add some extra code to the setter. So we need to override the default setter and getter.
  • We use an underscore for the private _value and value for the public getter and setter.
  • Because we use the default setter an getter, we just have to write name and shortname.
  • There's also a constructor with the same name as the class. The three parameter will be assigned automatically to the three fields.

# Adding the list of currencies

  • In a normal situation our app would use an api to get the currency data. This will be covered later in this course. For now, we will use a limited list of hardcoded currencies.
  • Import the Currency class and fill an array with seven currencies


 












 
 
 
 
 
 
 
 
 

import 'package:flutter/material.dart';

import 'package:currency_converter/models/currency.dart';

class ConverterPage extends StatefulWidget {
  
  State<StatefulWidget> createState() => _ConverterPageState();
}

class _ConverterPageState extends State {
  TextEditingController amountController = TextEditingController();

  String _fromamount = "";
  String _toamount = "";

  final _currencies = [
    Currency('US Dollar', 'USD', 1),
    Currency('Euro', 'EUR', 1.16),
    Currency('British Pound', 'GBP', 1.2715),
    Currency('Indian Rupee', 'INR', 0.0135),
    Currency('Chinese yuan', 'CNY', 0.1466),
    Currency('Canadian Dollar', 'CAD', 0.7458),
    Currency('Singapore Dollar', 'SGD', 0.7268)
  ];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  • The shortname of the currency is the same as the name of the asset with the flag. We will use this later to make our UI more attractive.
  • The currency value is the exchange rate in relation to the dollar. So 1 euro is worth 1,16 dollar. Normally these values should be retrieved real time using an api. We'll cover this later in the course.

# Extra state-properties

  • We need to add two extra state-properties: _from for the currency of the amount that needs to be converted, _to for the currency the amount needs to be converted to. More info about the late keyword... later.
  • Let's add an initState method to initialize the state-properties. We choose euro for the source currency and dollar for the destination currency.





 
 











 
 
 
 
 
 

class _ConverterPageState extends State {
  TextEditingController amountController = TextEditingController();

  String _fromamount = "";
  String _toamount = "";
  late Currency _from;
  late Currency _to;

  final _currencies = [
    Currency('US Dollar', 'USD', 1),
    Currency('Euro', 'EUR', 1.16),
    Currency('British Pound', 'GBP', 1.2715),
    Currency('Indian Rupee', 'INR', 0.0135),
    Currency('Chinese yuan', 'CNY', 0.1466),
    Currency('Canadian Dollar', 'CAD', 0.7458),
    Currency('Singapore Dollar', 'SGD', 0.7268)
  ];
  
  
  void initState() {
    super.initState();
    _from = _currencies[1];
    _to = _currencies[0];
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

LATE

  • Declaration of variables that will be initialize later is done using late modifier.
  • When using late before variables make sure that the variables must be initialized later. Otherwise you can encounter a runtime error when the variable is used.

INITSTATE

  • The framework will call the initState method exactly once when this object (ConverterPage) is inserted into the tree.
  • Override this method if you want to perform initializations.
  • If you override this method, make sure the method starts with a call to super.initState().

# Currency conversion

  • We now can complete our method that calculates the conversion between the two currencies. Remember, it was on our todo list.
  • Replace the old calculate methode with this new one
 
 
 
 
 
 
 
 
 

  void _calculate() {
    double amount = double.parse(amountController.text);
    double result = amount * _from.value / _to.value;

    setState(() {
      _fromamount = '${amount.toStringAsFixed(3)} ${_from.shortname} =';
      _toamount = '${result.toStringAsFixed(3)} ${_to.shortname}';
    });
  }
1
2
3
4
5
6
7
8
9
  • Try to run your app and convert an amount from euro to dollar (the default currencies, see initState)
euro to dollar
  • We're almost there. The only thing we need to do, is create two DropdownButton's so the user can choose his own currencies. Otherwise it would be a rather useless app. This will be our final step.

# Create a custom widget

  • Since we need to have two DropdownButton's, it's a good idea to create a custom widget and use this two times: for the source and destination currency.

TIP

It is always a good idea to build your app in a modular way. So try to build your own small widgets instead of putting all your code nested in one big widget. We cannot say this enough, splitting your page into seperate custom widgets, will improve the readability, simplicity and ease of maintenance of your code.

# Inherit a StatelessWidget

  • Create a new file currencydropdownbutton.dart in the widgets folder
  • Paste the code to create you own custom DropdownButton widget
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

import 'package:flutter/material.dart';

import 'package:currency_converter/models/currency.dart';

typedef MyCallback = void Function(Currency currency);

class CurrencyDropdownButtonWidget extends StatelessWidget {
  final String labelText;
  final Currency selectedCurrency;
  final List<Currency> currencies;
  final MyCallback onCurrencySelected;

  const CurrencyDropdownButtonWidget(
      {Key? key,
      required this.labelText,
      required this.selectedCurrency,
      required this.currencies,
      required this.onCurrencySelected})
      : super(key: key);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
code description
class CurrencyDropdownButtonWidget extends StatelessWidget Our CurrencyDropdownButtonWidget inherits from StatelessWidget. But if you would like to build a stateful widget, this is possible too.
final String labelText; When building your own widget, the first thing you've got to do is figuring out what data your widget needs to work properly. In our case we need a label ("from" or "to"),
final Currency selectedCurrency; the currency to show in the Dropdown in the beginning,
final List<Currency> currencies; a list of currencies for the user to choose from (we will show these in the Dropdown)
final MyCallback onCurrencySelected; and a callback function to use when the users selects a currency from the list.
typedef MyCallback = void Function(Currency currency); Because we very probably want to know which currency the user has selected the callback function has the selected currency as a parameter.
const CurrencyDropdownButtonWidget({ ... }); The constructor of the widget. We will use the constructor to pass the necessary properties.

# Using a DropdownButton and DropdownMenuItems

  • Next we need to implement the build method to render the widget
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

  
  Widget build(BuildContext context) {
    return DropdownButton<Currency>(
      isExpanded: true,
      items: currencies.map((Currency currency) {
        return DropdownMenuItem<Currency>(
          value: currency,
          child: Text(currency.name),
        );
      }).toList(),
      value: selectedCurrency,
      onChanged: (value) {
        onCurrencySelected(value);
      },
    );
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
code description
return DropdownButton<Currency> DropdownButton is a generic type. Here we'll use it to show Currency objects.
items: currencies.map((Currency currency) We'll iterate over all currencies and create a DropdownMenuItem for each of them.
return DropdownMenuItem<Currency> Every DropdownMenuItem represents one currency.
value: currency When the user selects a DropdownMenuItem, Flutter will return the whole currency object.
child: Text(currency.name) We'll show the currency name for every currency in the list.
value: selectedCurrency The selectedCurrency will be selected in the beginning.
onCurrencySelected(value); When the user selects a currency, the callback function will be triggered.

NULL CHECK

  • As you have probably noticed, there's a problem with the value parameter: "The argument type 'Currency?' can't be assigned to the parameter type 'Currency'."
  • This is because the value parameter of the onChanged method is optional and the currency parameter of our callback function isn't.
  • We'll add a null check to fix this. This can be done short handed by adding an exclamation mark behind the value parameter: onCurrencySelected(value!);

# Integrate the custom widget in our page

  • Our widget is ready. Let's try it on our page. Open converter.dart and import the custom widget



 



import 'package:flutter/material.dart';

import 'package:currency_converter/models/currency.dart';
import 'package:currency_converter/widgets/currencydropdownbutton.dart';

...
1
2
3
4
5
6
  • Add two custom CurrencyDropdownButtonWidget widgets (with padding) to the form (after textfield with the Padding widget)




 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 






...

              ),
            ),
            Padding(
              padding: EdgeInsets.only(bottom: formPadding, top: formPadding),
              child: CurrencyDropdownButtonWidget(
                  labelText: "From",
                  selectedCurrency: _from,
                  currencies: _currencies,
                  onCurrencySelected: (currency) {
                    _onFromChanged(currency);
                  }),
            ),
            Padding(
              padding: EdgeInsets.only(bottom: formPadding, top: formPadding),
              child: CurrencyDropdownButtonWidget(
                  labelText: "To",
                  selectedCurrency: _to,
                  currencies: _currencies,
                  onCurrencySelected: (currency) {
                    _onToChanged(currency);
                  }),
            ),
            Container(
              height: formPadding,
            ),

...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  • Add the two event handlers _onFromChanged and _onToChanged. These event handlers will be executed when the user selects a source or destination currency.
  • In the event handlers we will update the two state-properties with the selected currency
 
 
 
 
 
 
 
 
 
 
 

  void _onFromChanged(Currency currency) {
    setState(() {
      _from = currency;
    });
  }

  void _onToChanged(Currency currency) {
    setState(() {
      _to = currency;
    });
  }
1
2
3
4
5
6
7
8
9
10
11

REMARK

As you can see, our page widget stays clean and we can simply reuse the code from our CurrencyDropdownButtonWidget widget and generate two of them.

  • It should be possible now, to select the currencies
gbp to inr

# The final touch

  • Not bad, but we can do better. Let's try to pimp our CurrencyDropdownButtonWidget a little bit.
  • Replace the build method of the custom widget with the code below
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

  
  Widget build(BuildContext context) {
    TextStyle? textStyle = Theme.of(context).textTheme.bodyText1;
    return InputDecorator(
      decoration: InputDecoration(
        contentPadding: const EdgeInsets.all(10),
        labelText: labelText,
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(5.0),
        ),
      ),
      child: DropdownButton<Currency>(
        isExpanded: true,
        items: currencies.map((Currency currency) {
          return DropdownMenuItem<Currency>(
            value: currency,
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                Image(
                    image: AssetImage("assets/${currency.shortname}.png"),
                    width: 40.0,
                    height: 40.0),
                const Padding(
                  padding: EdgeInsets.only(left: 10.0),
                ),
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      currency.shortname,
                      textScaleFactor: 1.3,
                    ),
                    Text(currency.name),
                  ],
                )
              ],
            ),
          );
        }).toList(),
        value: selectedCurrency,
        style: textStyle,
        onChanged: (value) {
          onCurrencySelected(value!);
        },
        underline: const SizedBox.shrink(),
      ),
    );
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  • Since you are already quite familiar with building a UI in dart, maybe you can figure out the code yourself? As you can see, we've fully customized the DropdownMenuItem.

  • Run the app and you should get this beautiful professional looking currency converter app. Well done!

inr to cad

# Exercise 1 - One two three (part 2)

  • In the first part of this exercise you have completed the UI.
inr to cad

# Stateful Widget

  • Open home.dartin the pages folder. As you can see, your HomePage widget is a stateless widget. Refactor the code and make your HomePage a stateful widget.
  • Create four properties in your stateful widget:
    • _operation: the arithmatical operation that has to be calculated, "34 + 22 =" in the above example
    • _result: the correct answer, 56 on the page above
    • _answer: the answer of the child performing the calculus, still empty in the above screenshot
    • _score: the current score, 2

# Complete the app

  • When opening the app, generate a sum of two random numbers. The result is less then 100. After that you can do the subtraction, division and multiplication as well. Always below 100 and whole numbers.
  • If the user pushes the number buttons, the number of the button pressed is appended to the answer.
  • When the user chooses the "X" button, cut off the last number from the answer.
  • After tapping the "?" button, check if the answer is correct (add 1 to the score if so), and show the next operation.

# Exercise 2 - Flags

  • In this exercise you will develop an app with which you can test your flag knowledge. The app looks like this
Flags

# Create the Flags app

  • Create a new flutter application with name flags
  • Create the following directories in the lib folder to organize your code: models, pages and widgets
  • Delete widget_test.dart in the test folder
  • In main.dart open a MaterialApp with the right colors. The name of the app is FlagsApp. Remove that uggly debug banner. In the home property call the page widget for the default route of the app: QuizPage

# Design the UI of the QuizPage

  • Create a new file quiz.dart in the pages folder
  • Create QuizPage, a stateful widget. Design the UI of the page. For now, don't include the row with the three flags. Later in the exercise we will create a separate widget for the flag and use the widget.
  • Your app looks like this (Spain and score hardcoded)
Spain

# Load the Assets

  • Create a folder assets in the root of your application and copy the png's into it.
  • Download flags
  • Next open pubspec.yaml and add the assets to your application
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

  assets:
    - assets/ar.png
    - assets/au.png
    - assets/br.png
    - assets/ca.png
    - assets/ch.png
    - assets/ci.png
    - assets/cm.png
    - assets/cy.png
    - assets/eg.png
    - assets/es.png
    - assets/gh.png
    - assets/gr.png
    - assets/jm.png
    - assets/jp.png
    - assets/kz.png
    - assets/mt.png
    - assets/mx.png
    - assets/pt.png
    - assets/sy.png
    - assets/tr.png
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Country class

  • Create a new file country.dart in the models folder
  • Next create a Country class with these properties: flag and name
  • In the QuizPage, create an array with all countries
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

  List<Country> _countries = [
    Country('ar', 'Argentina'),
    Country('br', 'Brazil'),
    Country('ca', 'Canada'),
    Country('es', 'Spain'),
    Country('mx', 'Mexico'),
    Country('pt', 'Portugal'),
    Country('tr', 'Turkey'),
    Country('gr', 'Greece'),
    Country('eg', 'Egypt'),
    Country('au', 'Australia'),
    Country('ci', 'Côte d\'Ivoire'),
    Country('cm', 'Cameroon'),
    Country('cy', 'Cyprus'),
    Country('gh', 'Ghana'),
    Country('jm', 'Jamaica'),
    Country('jp', 'Japan'),
    Country('kz', 'Kazachstan'),
    Country('mt', 'Malta'),
    Country('sy', 'Syria'),
    Country('ch', 'Switzerland')
  ];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# FlagImage Widget

  • In the folder widgets create a new file flagimage.dart
  • Create a custom widget FlagImageWidget with two properties: the name of the flag and a callback function when the flag is tapped. This callback function has one parameter: the name of flag that has been tapped.

TIP

You will need a GestureDetector with an onTap method.

  • Now modify the UI, add a Row with three FlageImageWidget widgets with the - for now - hardcoded countries Argentina, Egypt and Turkey.
Argentina, Egypt and Turkey

# Random Flags

  • When you start the game, show three random flags. To do this, shuffle the array with the countries and take the first three countries.
  • You can use this shuffle function
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  List shuffle(List items) {
    var random = new Random();

    // Go through all elements.
    for (var i = items.length - 1; i > 0; i--) {
      // Pick a pseudorandom number according to the list length
      var n = random.nextInt(i + 1);
      // Shuffle
      var temp = items[i];
      items[i] = items[n];
      items[n] = temp;
    }

    return items;
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • Choose at random one of those three as the flag you need to find and show the name of the country to the user.
Flags
  • When the user taps a flag, check if the answer is correct. If so, add one to the score and show the next three random flags
Last Updated: 9/12/2023, 1:54:12 PM