๐ Everyone!
Welcome to the 1st part of the Flutter TODO app-building series. In this article, we are going to build the main screen on which all the TODOs from a user are shown.
To know more about the project you can go to Introduction article.
This article is complementary to the video series on YouTube, so if you are more of a video person then you can go and watch the video ๐๐
Breaking The UI Into Parts.
| How a user will look at it | How a DEV should look at it |
|---|---|
![]() |
![]() |
Each part is marked with a number and I have explained what they are and which widget I have used in Flutter.
So as you can see that we have Scaffold as the parent and then we have appBar, body, and floatingActionButton.
- We have an
AppBarin which we have atitleproperty which is a type ofWidgetso we use aTextwidget. - On the trailing(actions) side of the
AppBarwe have anIconButtonwhich on clicking will log the user out of the app. - Moving to
bodywe can see that the date button and our TODO list are vertically aligned, so which widget we can use for this? Yes, you have guessed it right we are going to use aColumnso the first child is a simpleTextButton(Added in Flutter 1.22). - Next child in our
Columnwill beListViewto be specific as we are going to add data dynamically so we need to use aListView.builder(This works like aRecyclerViewin Android). - Inside our
ListView.buildereach child is aDismissibleso that the user can dismiss them to delete.Cardis the child of it which gives it an aesthetic look and child is aListTile. - Getting to
ListTilewe havetitlewhich also takes the type ofWidgetso again we are using aTextwidget. - Just below the title we have a description and date of creation.
ListTilealso has a property for this which issubtitleagain you have guessed it we need aWidgetbut this time we are going to pass aColumnnot aTextwidget because we need to have them vertically aligned. Next inColumn, we have 2Textwidget that is for description and date of creation. - To the left end we have an Icon, to get this we add
leadinginListTilethat is anIconButton. - To the right end we have again an icon but this time it is a simple
Iconwhich is in thetrailingofListTileposition. - Finally we have a
FloatingActionButtonwith anIconas a child.
Oof, this was so looonnng!!, now that you can read the UI we can start building it!! ๐
It's CODE TIME!!!
First of all, we are going to start by making a StatefulWidget.
class TODOScreen extends StatefulWidget {
TODOScreen({Key key}) : super(key: key);
@override
_TODOScreenState createState() => _TODOScreenState();
}
class _TODOScreenState extends State<TODOScreen> {
@override
Widget build(BuildContext context) {
// We are going to start building here
}
}
Then we can work on Scaffold
return Scaffold(
appBar: // This is where we are going to add our `AppBar`,
body: // This is where most of the code will go,
floatingActionButton: // This is where we will add `FloatingActionButton`,
);
For the AppBar we will have a title property and also in action we will have IconButton for logout
appBar: AppBar(
title: Text("My TODO app"),
actions: [
IconButton(
onPressed: () {},
icon: Icon(Icons.exit_to_app),
)
],
),
In the body, we have Column which contains TextButton and ListView.builder.
body: Column(
children: <Widget>[
// Here we will have `TextButton` and `ListView.builder`
],
),
For time being we can just make a simple button with a Placeholder text, then we can work on logic and show the actual date.
TextButton(
onPressed: () {}, // Here we will call `DatePicker`
child: Text("Date"), // In place of "Date" we will add the date which is selected
),
Below the button, we have our ListView.builder
We need to make
shrinkWrapastrueinListView.buildersince it is inside aColumnotherwise this will shower you with errors (I hope you don't like them๐) and will makeitemCountas 3.
ListView.builder(
shrinkWrap: true,
itemCount: 3,
itemBuilder: (context, index) {
// Here we have to return our list item
},
)
To make our Tasks(items in ListView.builder) have swipe to delete we need to start by wrapping it with a Dismissible which will give us properties such as direction to select the direction of dismissing, background which is stacked behind the child of Dismissible and only show up when the child is swiped.
Dismissible(
// This should be unique for each item hence we are using index
key: ValueKey(index),
// As written above this will be stacked behind the `child`
background: Container(
alignment: Alignment.centerLeft,
color: Colors.red,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(Icons.delete),
),
),
direction: DismissDirection.startToEnd, // Only allow to swipe in reading direction
child: // Widget which is actually shown on screen
);
For the child we are going to have a Card -> ListTile then we can add:
-
titlewhich will show task title -
subtitlewhich will be aColumnand have task description and created date -
leadingwhich will be anIconButtonto show which task is completed and mark a task as completed -
trailingwhich will be anIcon
Card(
child: ListTile(
leading: IconButton(
onPressed: () {},
icon: Icon(Icons.check_circle_outline_outlined),
),
trailing: Icon(Icons.arrow_forward_ios),
title: Text("Title"),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("Desc"),
Text("Date"),
],
),
),
),

What you can see on your screen?... Yes, this looks kinda weird, Let's fix it.
To fix it first we need to remove the default margin from Card.
Card(
margin: const EdgeInsets.all(0.0),
.
.
),

Now cards are sticking to each other. This could be easily solved by wrapping Padding around Dismissible.
return Padding(
padding: const EdgeInsets.all(8.0),
child: Dismissible(
.
),
),

Yes, this looks much better๐
Finally, we can work on our FloatingActionButton! It has a single child which is an Icon.
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
Congratulations you have completed your UI building!! ๐๐ฅณ
Adding Some Logic
Now we can add some logic to change the date which was in the TextButton.
Remember I told you that in place of "Date" we can put the actual date? Now we are going to work on that.
Let's start by making an instance variable in our State class and initializing it with DateTime.now() which will assign it today's date.
DateTime _date = DateTime.now();
Now we need to format our _date variable and show it to users. For this, we are going to use a package from pub.dev named intl into pubspec.yaml.
dependencies:
# Other dependencies
intl: ^0.16.1
intl gives us a lot of formatting options you can look at them by yourselves over here.
For formatting date, we are going to pass _date to format method from DateFormat class which will format it.
We can now replace the "Date" with it so the user will get a clear idea of which tasks are for user on which date.
TextButton(
onPressed: () {},
child: Text(
DateFormat("dd/MM/yyyy").format(_date).toString(),
),
),
Now as the user can see the date they also might want to change, so we are going to implement a date picker in it. As material library already gives a date picker we are going to use that. For that, we will make another method in our class.
_showDatePicker() async {
// To make a variable with today's date as we are going to use it a lot of time
final today = DateTime.now();
// This will return a `Future<DateTime>` hence we are awaiting it.
final selectedDate = await showDatePicker(
context: context, // We get it from the `State` class
initialDate: _date, // This is the date which is selected in the date picker
firstDate: DateTime(2020, 11, 1), // This is the start range in date picker which is selectable
lastDate: DateTime(today.year + 1, today.month, today.day), // This is the end range in date picker which is selectable
);
// if user have selected any date and selected date and current date
// is not same then update the date and update the UI
if (selectedDate != null && selectedDate != _date) {
setState(() {
_date = selectedDate;
});
}
}
As we have written the method _showDatePicker() we can call it when the user clicks on the Button.
TextButton(
onPressed: () {
_showDatePicker();
},
child: Text(
DateFormat("dd/MM/yyyy").format(_date).toString(),
),
),
Woah you have completed your first screen!! ๐
You can go over here to see the whole code.
Thanks for reading, if you wish you can also join our community on Discord.
See you soon! ๐ Happy Fluttering.


Top comments (0)