Try it Out: Math Riddle Game in Flutter

It’s time to play and build a simple math riddle game using the Flutter framework.
The game consists of five equations. Four display correct results and in the fifth one, the result will be overridden with an X.
Below the equations, we’ll build the input field for the result (X). So, when the player enters a correct answer in this input, a success toast will be shown and the player will be redirected to the next level.
Prerequisites for the Flutter game
If you would like to follow this blog, you should install Flutter SDK and choose the code editor. I will be using Visual studio code and the iOS simulator.
If you are using Windows or Linux, you can use the android emulator coming from Android studio. Also, before you start, you should know Dart basics and be familiar with Scaffold, Column, Row, Center, Padding, Text, TextField, Card, and AlertDialog widgets. You can find them all in the Flutter widget catalog.
Getting started with a math riddle game
We are going to start with creating a new Flutter project using the Flutter command in the terminal:
flutter create math_riddle
Inside the lib
folder in the main.dart
file default Counter app will be created. We don’t need that code. Delete all excess code and comments in MyHomePage
stateful widget. Keep the Scaffold widget which has the Column widget as a child, and wrap the Column with the Center widget.

import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[],
),),
);}}
Adding assets
After we’ve cleaned the code, we can add assets to our project. At the root of the project, we are going to create an assets folder, and inside we’ll add folders for icons and gifs. Like this:

Also, we’ll register a path to assets in our pubspec.yaml
file.
flutter:
uses-material-design: true
assets:
- assets/icons/
- assets/gifs/
In the lib folder, create assets.dart
file.
Generate a list that contains fruit icons and a map that contains mathematical operators.
List<String> fruitIconsList = [
'assets/icons/apple.svg',
'assets/icons/citrus.svg',
'assets/icons/tomato.svg',
];
Map<String, String> operationIconsMap = {
'addIcon': 'assets/icons/add.svg',
'substractIcon': 'assets/icons/substract.svg',
'equalIcon': 'assets/icons/equal.svg',
};
Adding packages
Now, let’s install the packages which we’ll need for this project.
Add packages with these commands:
flutter pub add google_fonts
flutter pub add flutter_svg
These commands will install google_fonts
and flutter_svg
packages in the project. You should be able to find them in pubspec.lock
file, that is if the installation went successfully.
We’ll be using the google_fonts
package inside the Text widget for styling purposes, and the flutter_svg
package for displaying SVG images.
Coding
Inside main.dart
file create Fruit class:
class Fruit {
final String icon;
final int value;
Fruit({
required this.icon,
required this.value,
});
}
Inside the build method create a fruits variable which will be a List of Fruit objects. Map through the fruitIconsList
and for every item return a Fruit object with an icon from fruitIconsList
, and a random value from 0 to 5.
List<Fruit> fruits = fruitIconsList.map(
(fruit) {
return Fruit(
icon: fruit,
value: Random().nextInt(5),
);
},
).toList();
We’ll be using this list for adding fruit icons to our equations widgets.
Also, we need a function for operands
so let’s create a function that returns a list of operations.
List<Operation> asignOperationValues() {
List<Operation> operations = [
Operation(icon: operationIconsMap['addIcon']!, value: '+'),
Operation(icon: operationIconsMap['substractIcon']!, value: '-'),
]..shuffle();
return operations.map(
(operation) {
return Operation(
icon: operation.icon,
value: operation.value,
);
},
).toList();
}
And, we need to create a function that will calculate the equation result.
String findResult(
Operation leadOperation,
Operation sufixOperation,
Fruit leadFruit,
Fruit midFruit,
Fruit sufixFruit,
) {
{
if (leadOperation.value == '+' && sufixOperation.value == '+') {
return (leadFruit.value + midFruit.value + sufixFruit.value).toString();
} else if (leadOperation.value == '+' && sufixOperation.value == '-') {
return (leadFruit.value + midFruit.value - sufixFruit.value).toString();
} else if (leadOperation.value == '-' && sufixOperation.value == '+') {
return (leadFruit.value - midFruit.value + sufixFruit.value).toString();
} else {
return (leadFruit.value - midFruit.value - sufixFruit.value).toString();
}
}
}
Now, we are ready to create _buildRow()
function which will return a Row widget with our equation. _buildRow()
function takes a list of Fruit objects, a list of operations, and isQuestion nullable boolean variable.
Widget _buildRow({
required List<Fruit> fruitValues,
required List<Operation> operations,
bool? isQuestion = false,
}) {
Fruit leadFruit = fruitValues[0];
Fruit midFruit = fruitValues[1];
Fruit sufixFruit = fruitValues[2];
Operation leadOperation = operations[0];
Operation sufixOperation = operations[1];
if (isQuestion!) {
result = findResult(
leadOperation,
sufixOperation,
leadFruit,
midFruit,
sufixFruit,
);
}
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SvgPicture.asset(
leadFruit.icon,
height: 45,
),
SvgPicture.asset(
leadOperation.icon,
height: 25,
),
SvgPicture.asset(
midFruit.icon,
height: 45,
),
SvgPicture.asset(
sufixOperation.icon,
height: 25,
),
SvgPicture.asset(
sufixFruit.icon,
height: 45,
),
SvgPicture.asset(
operationIconsMap['equalIcon']!,
height: 25,
),
Text(
isQuestion
? 'X'
: findResult(
leadOperation,
sufixOperation,
leadFruit,
midFruit,
sufixFruit,
),
style: GoogleFonts.play(
textStyle: const TextStyle(
fontSize: 46,
fontWeight: FontWeight.w800,
color: Colors.green,
)),
),
],
); }
We’ll call _buildRow()
function inside the build method, five times.
_buildRow(
fruitValues: fruits..shuffle(),
operations: asignOperationValues(),
isQuestion: false,
),
_buildRow(
fruitValues: fruits..shuffle(),
operations: asignOperationValues(),
isQuestion: false,
),
_buildRow(
fruitValues: fruits..shuffle(),
operations: asignOperationValues(),
isQuestion: false,
),
_buildRow(
fruitValues: fruits..shuffle(),
operations: asignOperationValues(),
isQuestion: false,
),
_buildRow(
fruitValues: fruits..shuffle(),
operations: asignOperationValues(),
isQuestion: true,
),
“.. ” is known as cascade notation.
This often saves you the step of creating a temporary variable, and allows you to write more fluid code.
and get this output:

Now it’s time to create _buildResultInput()
function which will check that the entered value is correct. If the entered value is correct, _buildResultInput()
function will show a dialog with a success message and lead you to the next level.
Widget _buildResultInput() {
return Column(
children: [
Text(
'Guess X:',
style: GoogleFonts.play(
textStyle: const TextStyle(
fontSize: 46,
fontWeight: FontWeight.w800,
color: Colors.black,
)),
),
const SizedBox(height: 10),
TextField(
controller: textEditingController,
onChanged: (String value) {
if (value == result) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
actionsAlignment: MainAxisAlignment.center,
content: Image.asset('assets/gifs/completed.gif'),
actions: <Widget>[
ElevatedButton(
child: Text('Start level ${level + 1}'),
onPressed: () {
textEditingController.clear();
setState(() {
level++;
});
Navigator.pop(context);
},
),
],
);
});
}
},
cursorColor: Colors.green,
decoration: const InputDecoration(
fillColor: Colors.white,
filled: true,
contentPadding: EdgeInsets.all(12),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.green),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.green),
),
),
keyboardType: TextInputType.number,
style: GoogleFonts.play(
textStyle: const TextStyle(
fontSize: 46,
fontWeight: FontWeight.w800,
color: Colors.black,
)),
textAlign: TextAlign.center,
)
],
);
}
Also, call this function inside the build method.

Finishing layout
Now it’s time to add some layout details. Let’s add two texts that will be displaying the current game level and the “Can you solve this riddle?” question. We also want to add some padding and background color.
In the build method, we want this:
Scaffold(
backgroundColor: Colors.blueGrey[100],
body: Padding(
padding: const EdgeInsets.all(12),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Level: $level',
style: GoogleFonts.play(
textStyle: const TextStyle(
fontSize: 46,
fontWeight: FontWeight.w800,
color: Colors.black,
),
),
textAlign: TextAlign.center,
),
Text(
'Can you solve this riddle ?',
style: GoogleFonts.play(
textStyle: const TextStyle(
fontSize: 46,
fontWeight: FontWeight.w800,
color: Colors.green,
),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
Card(
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 16, horizontal: 4),
child: Column(
children: [
_buildRow(
fruitValues: fruits..shuffle(),
operations: asignOperationValues(),
isQuestion: false,
),
_buildRow(
fruitValues: fruits..shuffle(),
operations: asignOperationValues(),
isQuestion: false,
),
_buildRow(
fruitValues: fruits..shuffle(),
operations: asignOperationValues(),
isQuestion: false,
),
_buildRow(
fruitValues: fruits..shuffle(),
operations: asignOperationValues(),
isQuestion: false,
),
_buildRow(
fruitValues: fruits..shuffle(),
operations: asignOperationValues(),
isQuestion: true,
),
],
),
),
),
const SizedBox(height: 12),
_buildResultInput()
],
),
),
);
Finally, the riddle game is done!
Starting the game
If you want to start your application, you can run the flutter devices
command in the terminal, this command will display a list of devices available for starting your flutter project.
You can pick one and type in terminal: flutter run -d iPhone
, and your application will start.
Or you can create a launch.json
file as shown below and run the simulator on the green play button.
{
"version": "0.2.0",
"configurations": [
{
"name": "iPhone Simulator DEV",
"request": "launch",
"type": "dart",
"args": [ "iPhone", "--dart-define", "ENVIRONMENT=dev"]
}
]
}
The application will start and you will see the game screens as shown here:

Working example
Link to the repository with the fully working example. Check it out and hope you had fun creating this little math riddle in Flutter!