DEV Community

Cover image for Apartment listing Detail UI for Beginners with flutter
Elvis
Elvis

Posted on

Apartment listing Detail UI for Beginners with flutter

Image description

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,

  1. The widgets are vertically aligned on each other making it a column
  2. 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

First component

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),
                ),
              ),

Enter fullscreen mode Exit fullscreen mode

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)
                        ],
                      ),
                    ],
                  ),
                ),
              ),


Enter fullscreen mode Exit fullscreen mode

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),
                      ),
                    ],
                  )),

Enter fullscreen mode Exit fullscreen mode

Lastly on the stack is the icontext container. You will notice from the screen that the icontext is in a row. check below;

IconTextContainer

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,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

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),
                  ],
                ),
              )
Enter fullscreen mode Exit fullscreen mode

Remember we are still in a Column widget? The Stack widget above is the first child.

Let see the other children of the column

Image description

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,
                  ),
                ]),
          ),

Enter fullscreen mode Exit fullscreen mode

Then we have another column for description and details.
These are text widgets

Image description

  // 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),
              ),
            ],
          ),

Enter fullscreen mode Exit fullscreen mode

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),
            ),
          ),

Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)