Elvis
Posted on September 15, 2022
Good to have you here again
In this episode we shall go through the UI above together. I encourage you to check other episodes, for simpler ui designs.
Everything is on github, so you can download and check the code. Only portrait mode was considered but Its completely responsive on different screens.
Before we do a quick analysis of the architecture of the screen, lets look at the folder structure. We have three folders which are: Assets folder (located in the root folder), screens folder (inside lib folder) and widgets folder (also inside lib folder). Then we have the main.dart, where the app is run. The screen we are considering here is the details' screen. We are running this screen from main.dart.
Lets consider the design and how it was achieved.
Lets start with the app bar. For this app, we did not use the app bar widget, we created our custom app bar. If you notice, the screen has gradient and a background image. For flexibility, we did not use app bar widget
We went straight to the body and wrapped the body with a padding.
From the screen, you will notice two type of stack,
- The widgets are vertically aligned on each other making it a column
- Widgets also overlay on each other making it a Stack
So we started with a column as a direct child of the padding (remember we said the padding wraps the whole body?).
We calculated the screen height and width with media query. This makes it responsive on any screen.
Let consider the first component of the app
In this first component, we have the background image, the overlay for gradient, the app bar, the lease button, the apartment name and address, and the icon and text container.
First we used the stack widget. Like we explained above, the stack widget, takes children widgets that overlay on themselves.
The first child of the stack widget is the background image container, followed by the linear gradient container,
// background image
Container(
height: height / 2,
width: double.infinity,
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage(
'assets/images/details_img.png',
))),
),
// linear gradient
Container(
height: height * .6,
width: double.infinity,
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Colors.black45, Colors.transparent],
begin: Alignment.bottomCenter,
end: Alignment.topCenter),
),
),
Then, we have the app bar which we called the header. To position it at the top of the screen, we used positioned widget (stack widgets takes position widget, this is why we had to use stack widget).
// header
Positioned(
left: 0,
right: 0,
top: height * 0.06,
child: Padding(
padding: EdgeInsets.all(10),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
CircleIconButton(
height: height * 0.05,
icon: Icons.arrow_back_ios_new_rounded,
),
Row(
children: [
CircleIconButton(
icon: Icons.favorite_border,
height: height * 0.05,
),
SizedBox(
width: width * 0.07,
),
CircleIconButton(
icon: Icons.bookmark_border,
height: height * 0.05)
],
),
],
),
),
),
Positioned next to the header is the lease button and the apartment name and address
Positioned(
bottom: height * 0.22,
left: 0,
right: 0,
child: Center(
child: ClipRRect(
// Clip it cleanly.
borderRadius: BorderRadius.circular(20),
child: BackdropFilter(
blendMode: BlendMode.src,
filter: ImageFilter.blur(sigmaX: 2, sigmaY: 2),
child: Container(
padding: const EdgeInsets.all(8),
height: MediaQuery.of(context).size.height * 0.08,
width: MediaQuery.of(context).size.width * 0.5,
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.3),
),
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
'Lease',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 16),
),
Text(
"\$175,000.00",
style: TextStyle(
letterSpacing: 1.5,
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 16),
)
],
),
),
),
),
),
),
// apartment address
Positioned(
bottom: height * 0.15,
left: 0,
right: 0,
child: Column(
children: const [
Text(
'WestVille Apartments',
style: TextStyle(
fontSize: 20,
letterSpacing: 2.0,
fontWeight: FontWeight.bold,
color: Colors.white),
),
Text(
'3544 NW 24th Street Road',
style: TextStyle(
fontSize: 14,
letterSpacing: 2.0,
//fontWeight: FontWeight.bold,
color: Colors.white54),
),
],
)),
Lastly on the stack is the icontext container. You will notice from the screen that the icontext is in a row. check below;
We created the icon text container as a reusable widget since it repeats three time on the screen. Check the code below;
import 'package:flutter/material.dart';
import 'dart:math' as math;
class IconTextContainer extends StatelessWidget {
IconTextContainer({Key? key, required this.icon, required this.text})
: super(key: key);
IconData icon;
String text;
@override
Widget build(BuildContext context) {
return Transform(
//alignment: Alignment.topRight,
transform: Matrix4.skewY(0.4)..rotateZ(-math.pi / 12),
child: Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height * 0.09,
width: MediaQuery.of(context).size.height * 0.09,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(3),
color: Colors.black87,
border: Border.all(color: Colors.grey)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
icon,
color: Colors.white,
),
Container(
// padding: const EdgeInsets.all(8.0),
child: Text(
text,
style: const TextStyle(
color: Colors.white,
),
),
),
],
),
),
);
}
}
Again we used position widget to position it on the stack, then, use Row widget to horizontally align it. A Row widget takes children widget and align them horizontally.
// icon and text containers
Positioned(
bottom: height * 0.05,
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
IconTextContainer(text: '3', icon: Icons.hotel_sharp),
IconTextContainer(
text: '4', icon: Icons.shopping_cart_sharp),
IconTextContainer(text: '2500', icon: Icons.window),
],
),
)
Remember we are still in a Column widget? The Stack widget above is the first child.
Let see the other children of the column
For the first container, we have the agent image, details and icon.
The container wraps a row widget. The row widget carries another row widget and an Icon widget as children
// agent image, details and icon
Container(
// padding: EdgeInsets.all(10),
height: height * 0.09,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: const Color(0xFF2B2B2B),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// image and text row widget
Row(
children: [
// image
Image.asset('assets/images/shelly_img.png'),
// column of text widget
Padding(
padding: const EdgeInsets.all(4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
'Shelly Butcher',
style: TextStyle(color: Colors.white),
),
Text(
'Agent',
style: TextStyle(color: Colors.grey),
),
],
),
)
],
),
// icon
const Icon(
Icons.arrow_forward_ios_outlined,
color: Colors.grey,
),
]),
),
Then we have another column for description and details.
These are text widgets
// description and details
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.symmetric(
vertical: MediaQuery.of(context).size.height * 0.01),
child: Text(
'Description',
style: TextStyle(color: Colors.white, fontSize: 18),
),
),
const Text(
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Egestas ut consectetur integer aliquam integer scelerisque. Nibh malesuada lectus mattis aliquet eget elementum dictum non. Eu, viverra gravida leo vitae non eu laoreet. Egestas lorem amet, diam diam neque vestibulum semper. Dictum fusce tellus eu et viverra ac augue aliquam fusce. Pharetra laoreet arcu vitae interdum id',
style: TextStyle(color: Colors.white70),
),
],
),
Lastly is the apply button. This button is a container with a text widget as child.
Container(
alignment: Alignment.center,
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.06,
margin:
EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.01),
// padding: EdgeInsets.only(top: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.topRight,
stops: const [1, 1],
colors: [Colors.red.shade700, Colors.transparent])),
child: const Text(
'Apply',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18),
),
),
I know everything may not be clear, I understand. You can always drop your questions. Its my pleasure to help. Go through the code from the link below for more clarity.
Github link
Posted on September 15, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.