In this flutter UI Design tutorial, we will learn to design a modern Homepage Dashboard UI in a flutter. This Flutter dashboard UI will be divided into multiple segments and cover multiple core flutter design concepts. This dashboard UI tutorial is for all who are new or want to enhance their designing skills in a flutter.
This Dashboard UI tutorial will cover the following points.
- Responsive Text and Images
- Layouts in Horizontal and Vertical Direction
- Card & Chip Designs
- Scroll Behaviors
- Clean Code
- Custom Widgets
- Light and Dark Theme
- UnBounded Height and Width Errors
and much more…
1 – Project Structure
As we are creating Flutter Login App and at the start of this Course we had learned 2 types of Folder Structures.
Therefore, we are using Feature First Approach and as a Global theme we are using the theme we created in this theme tutorial.
After having basic setup, we will add all the assets and constants respectively. You can get assets from here and updated constants from Flutter Login App Homepage.
Now let’s create a new Feature inside Features Folder and name it whatever you like (Core in our case).
Inside Core folder add a new dart file called dashboard.dart and folder called widgets (for local widgets).
2 – Flutter Appbar
Inside our dashboard.dart create a StateLess Widget by simple writing stless and hit enter.
Name the Statless class as DashboardScreen.
Wrap the Container widget with Scaffold to apply Material design and to create appbar.
To design a modern and minimal Appbar in Flutter and to make it reusable component, we have to convert it into seprate Custom Widget.
But first, let’s design the appbar in this Dashboard.
This appbar contains 3 items.
- Leading Icon (Menu Icon Button)
- Title(Center as True)
- Action(Profile Icon Button)
AppBar(
elevation: 0,
centerTitle: true,
backgroundColor: Colors.transparent,
leading: Icon(
Icons.menu,
//For Dark Color
color: isDark ? tWhiteColor : tDarkColor,
),
title: Text(tAppName, style: Theme.of(context).textTheme.headline4),
actions: [
Container(
margin: const EdgeInsets.only(right: 20, top: 7),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
//For Dark Color
color: isDark ? tSecondaryColor : tCardBgColor,
),
child: IconButton(onPressed: () {}, icon: const Image(image: AssetImage(tUserProfileImage))),
)
],
),
3 – Search Bar Flutter
There are many types of search designs that commonly created in Flutter Homepages like Search Icon in AppBar, Search TextField at the top of Flutter Homepage, and clickable Search Box Design that will lead to open a search in new screen.
So, we will follow the last one which is modern and being used widely in professional apps.
To create this design, we need a ROW and inside we have 2 items (Text and Icon).
Whereas the left Border will be created using Container Widget (left border). To add the space between Text(Search…) and Icon, we will use row(mainAxisAlignment as spaceBetween()).
Container(
decoration: const BoxDecoration(border: Border(left: BorderSide(width: 4))),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(tDashboardSearch, style: txtTheme.headline2?.apply(color: Colors.grey.withOpacity(0.5))),
const Icon(Icons.mic, size: 25),
],
),
),
4 – Scrollable Categories
To create a scrollable cards as categories, we will use ListView.builder() with direction as horizontal.
Firstly, we will design a Card using Row with a Container as first child and Column as 2nd Child. Column will further contain 2 Texts.
After creating design card or chip, we will convert it into ListView and afterwards, convert it into ListView.builder().
In ListView.builder() we will get the dummy data from a model.
SizedBox(
height: 45,
child: ListView.builder(
itemCount: list.length,
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) => GestureDetector(
onTap: list[index].onPress,
child: SizedBox(
width: 170,
height: 45,
child: Row(
children: [
Container(
width: 45,
height: 45,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: tDarkColor),
child: Center(
child: Text(list[index].title, style: txtTheme.headline6?.apply(color: Colors.white)),
),
),
const SizedBox(width: 5),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(list[index].heading, style: txtTheme.headline6, overflow: TextOverflow.ellipsis),
Text(list[index].subHeading, style: txtTheme.bodyText2, overflow: TextOverflow.ellipsis)
],
),
)
],
),
),
),
),
),
5 – Banner Design
Banners are of many designs but the reason to design this type of banner is to keep design professional and make it easy to use for any type of app.
This banner design will contain 2 cards and 1 button.
We will divide the design into half using Row and inside 2 Expanded Widgets(to cover equal space).
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//1st banner
Expanded(
child: Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10),
//For Dark Color
color: isDark ? tSecondaryColor : tCardBgColor,
),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Flexible(child: Image(image: AssetImage(tBookmarkIcon))),
Flexible(child: Image(image: AssetImage(tBannerImage1))),
],
),
const SizedBox(height: 25),
Text(tDashboardBannerTitle1, style: txtTheme.headline4, maxLines: 2, overflow: TextOverflow.ellipsis),
Text(tDashboardBannerSubTitle, style: txtTheme.bodyText2, maxLines: 1, overflow: TextOverflow.ellipsis),
],
),
),
),
const SizedBox(width: tDashboardCardPadding),
//2nd Banner
Expanded(
child: Column(
children: [
//Card
Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10),
//For Dark Color
color: isDark ? tSecondaryColor : tCardBgColor,
),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Flexible(child: Image(image: AssetImage(tBookmarkIcon))),
Flexible(child: Image(image: AssetImage(tBannerImage2))),
],
),
Text(tDashboardBannerTitle2, style: txtTheme.headline4, overflow: TextOverflow.ellipsis),
Text(tDashboardBannerSubTitle, style: txtTheme.bodyText2, overflow: TextOverflow.ellipsis),
],
),
),
const SizedBox(height: 5),
SizedBox(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: OutlinedButton(onPressed: () {}, child: const Text(tDashboardButton)),
),
)
],
),
),
],
);
}
6 – Top Courses
This section can be utilized to create any type of Top Items, Courses, Top Products, and anything that suits.
We will create a SizedBox with specific width and height because we cannot cover it using Expanded to make it unbounded.
Then inside a Sizedbox create a Container.
Container will have a BoxDecoration with border Radius and background Color.
Create 2 rows inside it and each contain 2 items.
First Row will have 1 text and 1 Image whereas, 2nd row will have 1st ElevatedCircular Button and 2nd will have Column with 2 texts.
SizedBox(
height: 200,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: list.length,
itemBuilder: (context, index) => GestureDetector(
onTap: list[index].onPress,
child: SizedBox(
width: 320,
height: 200,
child: Padding(
padding: const EdgeInsets.only(right: 10, top: 5),
child: Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10),
//For Dark Color
color: isDark ? tSecondaryColor : tCardBgColor,
),
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Text(
list[index].title,
style: txtTheme.headline4,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
Flexible(child: Image(image: AssetImage(list[index].image), height: 110)),
],
),
Row(
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(shape: const CircleBorder()),
onPressed: () {},
child: const Icon(Icons.play_arrow),
),
const SizedBox(width: tDashboardCardPadding),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
list[index].heading,
style: txtTheme.headline4,
overflow: TextOverflow.ellipsis,
),
Text(
list[index].subHeading,
style: txtTheme.bodyText2,
overflow: TextOverflow.ellipsis,
),
],
)
],
)
],
),
),
),
),
),
),
)
6 – Clean Code
So far we were creating everything inside dashboard.dart but at the end you will see our code size has increased alot and become unmanageable.
Therefore, we have to convert our code into separate Custom widgets.
OTHER USEFUL LINKS
- Install Android Studio
- Install Flutter
- Create a new flutter app
- Create Folder Structure
- Setup Theme in Flutter for Light & Dark Mode
This is the tutorial of our Flutter Login App Series.