Stateful Widgets
1 App Development
1.1 Blank App
import 'package:flutter/material.dart';
void main(){
runApp(MaterialApp(
home: Home(),
));
}
class Home extends StatelessWidget{
@override
Widget build(BuildContext context)
{
return Scaffold(
);
}
}

1.2 Adding an App Bar
import 'package:flutter/material.dart';
void main(){
runApp(MaterialApp(
home: Home(),
));
}
class Home extends StatelessWidget{
@override
Widget build(BuildContext context)
{
return Scaffold(
appBar: AppBar(
title: Text('My Portfolio'),
backgroundColor: Colors.red,
centerTitle: true,
),
);
}
}

1.3 Adding a Photo, Name, and DoB
import 'package:flutter/material.dart';
void main()
{
runApp(MaterialApp(
home: Home(),
));
}
class Home extends StatelessWidget
{
Widget build(BuildContext context)
{
return Scaffold(
appBar: AppBar(
title: Text('My Portfolio'),
backgroundColor: Colors.red,
centerTitle: true,
),
// we will need several rows for the portfolio,
// so we use a column to contain them all
body: Column(
children: [
// the first row of the column, will contain
// a photo, name, and dob
Row(
children: [
// the photo
Image.asset(
'lib/assets/images/salah.jpeg',
width: 100,
),
// the name
Column(
children: [
Text('name'),
Text('Salah'),
],
),
// the dob
Column(
children: [
Text('DoB'),
Text('1138'),
],
),
],
),
],
)
);
}
}

1.4 Beautifying the Top Row (I)
We want to fix the following issues.
-
Space evenly the three components of the row. Right now, they are flushed to the left.
-
We need to left-align the text.
-
We need to use different font weights to enhance the style.
-
We need to add some vertical space on top of the first row.
import 'package:flutter/material.dart';
void main()
{
runApp(MaterialApp(
home: Home(),
));
}
class Home extends StatelessWidget
{
Widget build(BuildContext context)
{
return Scaffold(
appBar: AppBar(
title: Text('My Portfolio'),
backgroundColor: Colors.red,
centerTitle: true,
),
body: Column(
children: [
// quickly add vertical space
SizedBox(height: 30),
Row(
// add this property to add equal spaces between the
// elements of the row.
// "mainAxis" referes to the horizontal axis in the
// case of a row.
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// image
Image.asset(
'lib/assets/images/salah.jpeg',
width: 100,
),
// name
Column(
// flush the elements in the column to the left.
// the mainAxis of the column is the vertical axis.
// the crossAxis of the column is the horizontal axis.
// so, we want to move elements with respect to
// the horizontal axis, i.e. horizontally
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Name'),
// add styling
Text('Salaheddine Al-Ayyoubi',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
// dob
Column(
// flush the elements in the column to the left.
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('DoB'),
// add styling
Text('1138',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
],
),
],
)
);
}
}

1.5 Beautifying the Top Row (II)
We want to fix the following issues.
-
Make the row more prominent by giving it a background color and a border.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: Home(),
));
}
class Home extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Portfolio'),
backgroundColor: Colors.red,
centerTitle: true,
),
body: Column(
children: [
SizedBox(height: 20),
// add a container in order to have some background color
// and a border
Container(
// use decoration box to specify the styling of
// the container
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.grey[100]),
// add some padding to the row from all four directions
padding: EdgeInsets.all(10),
// add a margin to the left and right
margin: EdgeInsets.only(left: 10, right: 10),
// this is the row, now wrapped in a container
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset(
'lib/assets/images/salah.jpeg',
width: 100,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Name'),
Text(
'Salaheddine Al-Ayyoubi',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('DoB'),
Text(
'1138',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
],
),
),
],
));
}
}

1.6 Adding Likes
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: Home(),
));
}
class Home extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Portfolio'),
backgroundColor: Colors.red,
centerTitle: true,
),
body: Column(
children: [
SizedBox(height: 20),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.grey[100]),
padding: EdgeInsets.all(10),
margin: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset(
'lib/assets/images/salah.jpeg',
width: 100,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Name'),
Text(
'Salaheddine Al-Ayyoubi',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('DoB'),
Text(
'1138',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
],
),
),
// add some vertical space
SizedBox(height: 20),
// a container enables us to set padding/margins
Container(
// add some padding
padding: EdgeInsets.only(left: 10, right: 10),
// the row will contain text that displays the number of likes
// and a button
child: Row(
// this time, we align to push widgets inside the row
// to the edges
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// text is fixed right now
Text('Likes: 100'),
// there are few types of buttons in flutter,
// this is one of them
ElevatedButton(
// this is the function that specifies what happens
// when the button is clicked,
// right now it is empty
onPressed: () {},
// this is the text written on the button
child: Text('Like'),
)
],
),
),
],
));
}
}

1.7 Incrementing Likes Using a Stateless Widget
Our first attempt is to define a variable likes and set it to 0 inside the Home class. Then, change the onpressed() function to increment the variable. Finally, display the value of the variable in the Text widget.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: Home(),
));
}
class Home extends StatelessWidget {
// create a variable to contain likes
int likes = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Portfolio'),
backgroundColor: Colors.red,
centerTitle: true,
),
body: Column(
children: [
SizedBox(height: 20),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.grey[100]),
padding: EdgeInsets.all(10),
margin: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset(
'lib/assets/images/salah.jpeg',
width: 100,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Name'),
Text(
'Salaheddine Al-Ayyoubi',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('DoB'),
Text(
'1138',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
],
),
),
SizedBox(height: 20),
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// display the number of likes
Text('Likes: $likes'),
ElevatedButton(
// increase the number of likes each time the
// button is pressed
onPressed: () {likes++;},
child: Text('Like'),
)
],
),
),
],
));
}
}
The compiler will give us a warning that we are defining a non final variable inside a StatelessWidget class. However, we are able to build and run the app.

We can see that the value of likes is 0. However, no matter how many times we click the button, the value of likes does not change.
Now, do the following experiment.
-
Restart the application i.e. do not just hot-reload it. You should see
0as the number of likes. -
Click the
Likebutton 5 times. You should not witness any change in the value of likes. -
Hot reload the application. You should see that the number of likes is
5.
What happens is that things are changing inside the app, however, they are not being reflected in the widget tree. This is because we are using a StatelessWidget. Instead, we should use a StatefulWidget.
1.8 Incrementing Likes Using a Stateful Widget
The correct way to show the likes count in the app is to use a Stateful widget. From Visual Studio, click on StatelessWidget in class Home extends StatelessWidget {, then click on the action yellow lamp that shows to the left, and choose Convert to Stateful Widget.

You get the following code.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: Home(),
));
}
// stateless widget becomes stateful widget
class Home extends StatefulWidget {
@override
// a state class _HomeState for Home is created
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
// create a variable to contain likes
int likes = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Portfolio'),
backgroundColor: Colors.red,
centerTitle: true,
),
body: Column(
children: [
SizedBox(height: 20),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.grey[100]),
padding: EdgeInsets.all(10),
margin: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset(
'lib/assets/images/salah.jpeg',
width: 100,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Name'),
Text(
'Salaheddine Al-Ayyoubi',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('DoB'),
Text(
'1138',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
],
),
),
SizedBox(height: 20),
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// display the number of likes
Text('Likes: $likes'),
ElevatedButton(
// increase the number of likes each time the
// button is pressed
onPressed: () {likes++;},
child: Text('Like'),
)
],
),
),
],
));
}
}
If you restart the app and press the button, the likes counter will not change. The reason is that we need to use a special function setState() to assert the state changes. The function is added as shown in the following code.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: Home(),
));
}
// stateless widget becomes stateful widget
class Home extends StatefulWidget {
@override
// a state class _HomeState for Home is created
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
// create a variable to contain likes
int likes = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Portfolio'),
backgroundColor: Colors.red,
centerTitle: true,
),
body: Column(
children: [
SizedBox(height: 20),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.grey[100]),
padding: EdgeInsets.all(10),
margin: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset(
'lib/assets/images/salah.jpeg',
width: 100,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Name'),
Text(
'Salaheddine Al-Ayyoubi',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('DoB'),
Text(
'1138',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
],
),
),
SizedBox(height: 20),
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// display the number of likes
Text('Likes: $likes'),
ElevatedButton(
// increase the number of likes each time the
// button is pressed
onPressed: () {
setState((){
likes++;}
);
},
child: Text('Like'),
)
],
),
),
],
));
}
}
In order to understand the work of setState(), we will add two dummy buttons, one for dislikes, and for counting the difference between likes and dislikes. The variables likesdifference are updated using setState(), and the variable dislikes is updated without using setState().
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: Home(),
));
}
// stateless widget becomes stateful widget
class Home extends StatefulWidget {
@override
// a state class _HomeState for Home is created
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
// create a variable to contain likes
int likes = 0;
int dislikes = 0;
int difference = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Portfolio'),
backgroundColor: Colors.red,
centerTitle: true,
),
body: Column(
children: [
SizedBox(height: 20),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.grey[100]),
padding: EdgeInsets.all(10),
margin: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset(
'lib/assets/images/salah.jpeg',
width: 100,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Name'),
Text(
'Salaheddine Al-Ayyoubi',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('DoB'),
Text(
'1138',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
],
),
),
SizedBox(height: 20),
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// display the number of likes
Text('Likes: $likes'),
ElevatedButton(
// increase the number of likes each time the
// button is pressed
onPressed: () {
setState((){
likes++;}
);
},
child: Text('Like'),
)
],
),
),
SizedBox(height: 20),
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// display the number of likes
Text('Dislikes: $dislikes'),
ElevatedButton(
// increase the number of dislikes each time the
// button is pressed
onPressed: () {
dislikes++;
},
child: Text('Dislike'),
)
],
),
),
SizedBox(height: 20),
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// display the number of likes
Text('Difference: $difference'),
ElevatedButton(
// calculate the difference between likes and dislikes
// each time the button is pressed
onPressed: () {
setState((){
difference = likes - dislikes;}
);
},
child: Text('Difference'),
)
],
),
),
],
));
}
}

Now:
-
Pressing the dislike button, does not update the value of dislikes in the widget tree.
-
Pressing the like button, or the difference button, updates the widget tree including the value of dislikes.
This means that a call to setState() updates the widget tree for all widgets affected by the state change.
In the following, there is another way to use setState().
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: Home(),
));
}
// stateless widget becomes stateful widget
class Home extends StatefulWidget {
@override
// a state class _HomeState for Home is created
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
// create a variable to contain likes
int likes = 0;
int dislikes = 0;
int difference = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Portfolio'),
backgroundColor: Colors.red,
centerTitle: true,
),
body: Column(
children: [
SizedBox(height: 20),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.grey[100]),
padding: EdgeInsets.all(10),
margin: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset(
'lib/assets/images/salah.jpeg',
width: 100,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Name'),
Text(
'Salaheddine Al-Ayyoubi',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('DoB'),
Text(
'1138',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
],
),
),
SizedBox(height: 20),
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// display the number of likes
Text('Likes: $likes'),
ElevatedButton(
// increase the number of likes each time the
// button is pressed
onPressed: () {
setState((){
likes++;}
);
},
child: Text('Like'),
)
],
),
),
SizedBox(height: 20),
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// display the number of likes
Text('Dislikes: $dislikes'),
ElevatedButton(
// increase the number of dislikes each time the
// button is pressed
onPressed: () {
dislikes++;
setState((){});
},
child: Text('Dislike'),
)
],
),
),
SizedBox(height: 20),
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// display the number of likes
Text('Difference: $difference'),
ElevatedButton(
// calculate the difference between likes and dislikes
// each time the button is pressed
onPressed: () {
setState((){
difference = likes - dislikes;}
);
},
child: Text('Difference'),
)
],
),
),
],
));
}
}
1.9 Enhancing the Like Button
We can use an IconButton instead. We will also remove the dislikes and difference widgets.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: Home(),
));
}
class Home extends StatefulWidget {
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
int likes = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Portfolio'),
backgroundColor: Colors.red,
centerTitle: true,
),
body: Column(
children: [
SizedBox(height: 20),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.grey[100]),
padding: EdgeInsets.all(10),
margin: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset(
'lib/assets/images/salah.jpeg',
width: 100,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Name'),
Text(
'Salaheddine Al-Ayyoubi',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('DoB'),
Text(
'1138',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
],
),
),
SizedBox(height: 20),
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Likes: $likes'),
IconButton(
iconSize: 30,
icon: const Icon(Icons.favorite),
color: Colors.red,
onPressed: () {
setState(() {
likes++;
});
},
)
],
),
),
],
));
}
}

1.10 Adding Portfolio Items
We want to list the projects and achievements of the user. For this, we will gather the achievements in a list, and use ListView.builder() to build the items for us in the widget tree.
First of all, each portfolio item will have a date and a name. Our hero’s achievements are the real deal, they are battle wins. So, we define a class for it. The class in Battle and has two values for the time being. We create a file battle.dart for the class and put it in the same folder as main.dart.
class Battle
{
String? date;
String? name;
Battle(String date, String name)
{
this.date = date;
this.name = name;
}
}
Now, let’s go back to the file main.dart. It will look as follows.
import 'package:flutter/material.dart';
import 'battle.dart';
void main() {
runApp(MaterialApp(
home: Home(),
));
}
class Home extends StatefulWidget {
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
int likes = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Portfolio'),
backgroundColor: Colors.red,
centerTitle: true,
),
body: Column(
children: [
SizedBox(height: 20),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.grey[100]),
padding: EdgeInsets.all(10),
margin: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset(
'lib/assets/images/salah.jpeg',
width: 100,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Name'),
Text(
'Salaheddine Al-Ayyoubi',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('DoB'),
Text(
'1138',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
],
),
),
SizedBox(height: 20),
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Likes: $likes'),
IconButton(
iconSize: 30,
icon: const Icon(Icons.favorite),
color: Colors.red,
onPressed: () {
setState(() {
likes++;
});
},
)
],
),
),
// this is the widget responsible for showing the
// portfolio items
BattleView(),
],
));
}
}
// BattleView defines a list of battles and builds a widget tree
// that lists all the battles
class BattleView extends StatelessWidget {
// this is the list of battles
final List<Battle> battles = [
Battle('1183', 'Siege of Al-Kark'),
Battle('1187', 'Siege of Al-Quds'),
Battle('1187', 'Battle of Hittin'),
Battle('1187', 'Siege of Sour'),
Battle('1187', 'Battle of Marj Oyoun'),
Battle('1188', 'Siege of Safd'),
Battle('1189', 'Siege of Akka'),
Battle('1188', 'Siege of Borzia Fortress'),
Battle('1188', 'Siege of Sohyoun Fortress'),
];
// this builds the widget tree of battles
@override
Widget build(BuildContext context) {
// the root of the widget tree is the widget expanded
// that will contain all the list items
return Expanded(
// a bit of padding
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
// this is the actual list builder
child: ListView.builder(
// specify the number of items in the list
itemCount: battles.length,
// this is the function that returns the widget
// index will take the values 0 to battles.length-1
itemBuilder: (context, index) {
// one item of the list
final battle = battles[index];
// is wrapped in a contained
return Container(
// decorations
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.grey[200],
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
// inside the container, there is a column
child: Column(
// the column is flushed to the left
crossAxisAlignment: CrossAxisAlignment.start,
// the column shows the data and name
children: [
// show name
Text(
battle.name!,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
// some vertical space
SizedBox(height: 5),
// show the date
Text(battle.date!, style: TextStyle(fontSize: 14, fontStyle: FontStyle.italic)),
],
),
);
},
),
),
);
}
}
Underneath the container that contains the “likes" items, we add BattleView() which is a widget that we shall define ourselves, and it should return a view of the list of battles that the user has performed in the past.

1.11 Organising Files
Right now, our lib folder looks as follows.

An app should contain many screens or pages, not just one. Therefore, we want to start organising our code to reflect that. Right now, the file main.dart contains the main function together with our home screen. We want to move the home screen to a new file. We also want to add a little bit of folder organisation.
We want to perform the following organisation.
-
Separate the home screen from the main function file.
-
Add folders to group files into categories e.g.
pagesfor pages or screens of the app,widgetsfor any widgets that we build and want to plug in,utilsfor anything we can’t decide where it belongs, etc. -
Create a separate file for the constant data on battles.
The new lib folder should look like:

The following are the listings of the new files.
1.11.1 main.dart
import 'package:flutter/material.dart';
import 'package:course_app/pages/home.dart';
void main() {
runApp(MaterialApp(
home: Home(),
));
}
1.11.2 home.dart
import 'package:flutter/material.dart';
import 'package:course_app/widgets/battle_view.dart';
import 'package:course_app/utils/battle_data.dart';
class Home extends StatefulWidget {
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
int likes = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Portfolio'),
backgroundColor: Colors.red,
centerTitle: true,
),
body: Column(
children: [
SizedBox(height: 20),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.grey[100]),
padding: EdgeInsets.all(10),
margin: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset(
'lib/assets/images/salah.jpeg',
width: 100,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Name'),
Text(
'Salaheddine Al-Ayyoubi',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('DoB'),
Text(
'1138',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
],
),
),
SizedBox(height: 20),
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Likes: $likes'),
IconButton(
iconSize: 30,
icon: const Icon(Icons.favorite),
color: Colors.red,
onPressed: () {
setState(() {
likes++;
});
},
)
],
),
),
// this is the widget responsible for showing the
// portfolio items
BattleView(battles),
],
));
}
}
1.11.3 battle_view.dart
import 'package:flutter/material.dart';
import 'package:course_app/utils/battle.dart';
// BattleView defines a list of battles and builds a widget tree
// that lists all the battles
class BattleView extends StatelessWidget {
// this is the list of battles
final List<Battle> battles;
BattleView(this.battles);
// this builds the widget tree of battles
@override
Widget build(BuildContext context) {
// the root of the widget tree is the widget expanded
// that will contain all the list items
return Expanded(
// a bit of padding
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
// this is the actual list builder
child: ListView.builder(
// specify the number of items in the list
itemCount: battles.length,
// this is the function that returns the widget
// index will take the values 0 to battles.length-1
itemBuilder: (context, index) {
// one item of the list
final battle = battles[index];
// is wrapped in a contained
return Container(
// decorations
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.grey[200],
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
// inside the container, there is a column
child: Column(
// the column is flushed to the left
crossAxisAlignment: CrossAxisAlignment.start,
// the column shows the data and name
children: [
// show name
Text(
battle.name!,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
// some vertical space
SizedBox(height: 5),
// show the date
Text(battle.date!, style: TextStyle(fontSize: 14, fontStyle: FontStyle.italic)),
],
),
);
},
),
),
);
}
}
1.11.4 battle.dart
class Battle
{
String? date;
String? name;
Battle(String date, String name)
{
this.date = date;
this.name = name;
}
}
1.11.5 battle_data.dart
import 'package:course_app/utils/battle.dart';
final List<Battle> battles = [
Battle('1183', 'Siege of Al-Kark'),
Battle('1187', 'Siege of Al-Quds'),
Battle('1187', 'Battle of Hittin'),
Battle('1187', 'Siege of Sour'),
Battle('1187', 'Battle of Marj Oyoun'),
Battle('1188', 'Siege of Safd'),
Battle('1189', 'Siege of Akka'),
Battle('1188', 'Siege of Borzia Fortress'),
Battle('1188', 'Siege of Sohyoun Fortress'),
];