Flutter UI Widgets

Nov 9 2021 · Dart 2.14, Flutter 2.5, VS Code 1.61

Part 1: Flutter UI Widgets

03. Build Layouts

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 02. Explore Basic Widgets Next episode: 04. Work with Images
Transcript: 03. Build Layouts

The rest of this course would focus on building a news reader app. In this episode, we’ll combine what we’ve learnt so far to build an article card widget. I’ve cleared out the previous code in the MainPage widget and the body currently returns an ArticleCard widget.

Open up the widget’s definition clicking on it and pressing the F12 key to go to its definition. This file is contained inside the widgets folder. This folder would contain custom widgets that can be used in multiple places.

The ArticleCard is a StatelessWidget that curently returns an empty Container. This widget would be used to display an individual Article. For this, i have created a model and some dummy data.

Head over to article.dart inside the models folder. Here you can see the Article class with its members. Below it, you have List of Articles. We would be using the articles variable to access the data.

Close the file and head back to the ArticleCard widget. Add the article property:

...
  final Article article;

  const ArticleCard({
    Key? key,
    required this.article,
  }) : super(key: key);
...

This exposes the article member in the constructor as a named parameter so we can pass an article when using this widget. We also added the required keyword to make sure we pass an article to the card.

Head over to main_page.dart. And you can see it currently complains that it expects a required argument. Go ahead and pass a single article from the articles List. And make sure you remove the const keyword because this class is no longer a constant value.

ArticleCard(article: articles[0])

This passes the first index from the articles list to the named parameter called article. Save this file and head back to the article card class.

Okay, replace the Container with the following code:

...
return Card(
  margin: const EdgeInsets.all(16),
  elevation: 4,
  child: Column(
    children: <Widget>[
      CardBanner(),
      CardDetail(),
    ]
  ),
);
...

First we return a Card widget with a margin of 16 and an elevation of 4. The elevation is used to create depth by adding a shadow around the Card. The Card has a Column as its child. The Column would have an banner and a Detail region below it.

Let’s create the CardDetail widget. Comment the CardBanner() widget and then paste the following code below ArticleCard class:

class CardDetail extends StatelessWidget {
  final Article? article;

  const CardDetail({Key? key, this.article}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
          Text(
            article!.title,
            style: const TextStyle(fontSize: 24),
            maxLines: 2,
            overflow: TextOverflow.ellipsis,
          ),
          const SizedBox(height: 16),
          Row(
            children: <Widget>[
              Text(article!.source),
              const Spacer(),
              const Text('45 Comments'),
            ],
          )
        ],
      ),
    );
  }
}

Update the call to this widget by passing in the article argument like so:

...
CardDetail(article: article),
...

Save you work. I noticed that the UI doesnt show up. This could be a sign that we have an error. Go ahead and open up the debug console.

And you can see that we need to do a hot restart. Go ahead and do that now. And you can see, the UI displays as expected. Now, you should be familiar with this type of code but I’ll explain some little additions to it.

This creates a widget that accepts an article as an argument. It returns a Padding with a padding of 16. A padding is used instead of a Container here because we only want to add a padding. It’s child is a Column with a title text, a SizedBox and a Row. The text is given a maxLine of 2 which limits the text to span 2 lines. If the text is longer than two lines, the overflow would be represented by an ellipsis.

Next is the SizedBox with a height of 16. This widget is an empty box used to add a fixed spacing between the top and bottom children of the column.

Finally we have the Row. It has three children. The first is the source text and the last child is the comments text. In between them is a Spacer() widget. This widget adds an empty space between flex widgets. Currently, it takes all the available space in between the other two children.

And by the way, Column and Row are examples of flex widgets. An alternative to achieving the same effect would be remove the Spacer and wrap the first child of the Row with an Expanded widget.

And if you notice, this card currently stretches from the top to the bottom of the available space. To correct this, head over to the MainPage widget and place the ArticleCard widget inside a Column. And this corrects it by limiting the height of the card to its content.

Let’s head by to the ArticleCard class and create the CardBanner. This widget would display an image. First, uncomment it where it is called inside the ArticleCard widget.

Then scroll down and enter the following code:

class CardBanner extends StatelessWidget {
  final String? imageUrl;

  const CardBanner({Key? key, this.imageUrl}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Placeholder(fallbackHeight: 200);
  }
}

Save your work and you should see the changes. We intend returning an image but since we’re not yet ready to code it up, a Placholder widget is returned with the expected dimensions. Then whenever we want to add the image, we’ll just replace this widget with an image and be rest assured that our UI won’t break. A very handed widget indeed.

Finally, let’s add a bookmark icon button at the top-right corner of the article card. For this we’ll be using a Stack widget. As the name suggests, it arranges widgets on top of each other. Wrap the Placeholder widget with a Stack and update your code as follows:

...
Stack(
  children: [
    const Placeholder(
      fallbackHeight: 200,
    ),
    Positioned(
      top: 10,
      right: 10,
      child: IconButton(
        icon: const Icon(Icons.bookmark_border, size: 32),
        onPressed: (){},
      ),
    ),
  ],
),
...

Save your work. You can see that the bookmark icon is placed at top-right corner on top the placeholder widget. Okay, lets break down what’s going on here.

The Stack takes in a List of children. The first child is the base of the Stack and any other widget is placed on top of it. The bookmark icon is the next child so it placed on top of the placeholder widget. And this structure continues in that order.

So the arrangement of the children is very important. A good example of a Stack layout is a stack of playing cards. Last In, Firt Out or Last In, First Seen. Next, to position the icon where we want it inside the Stack, we use a Positioned widget. We set its top and right distance to 10. This aligns the icon to the top-right corner and insets it on both the top and right edges of Stack by a value of 10.

We need to update the Placeholder with an image. Let’s do that in the next episode.