Your First Flutter App: An App From Scratch

Feb 22 2022 · Dart 2.14.1, Flutter 2.5, Visual Studio Code 1.6

Part 3: Create UI with Flutter

20. Layout Widgets

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: 19. Set an Orientation Next episode: 21. Add a Slider

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Transcript: 20. Layout Widgets

Here’s what the Bullseye screen will look like at the end of this course, but with all the visible widgets highlighted and labeled. Note that some of the widgets are invisible, and I’ll point them out to you soon. Note, the follow up course, we will complete the app there.

Remember, a widget is anything that gets drawn on the screen. In this screenshot, it seems that just about everything is a widget: the text items, the buttons and the slider are all widget. In fact, every user interface control is a widget.

Some widget can act as containers for other widget. The biggest widget in the screenshot is one of these: it’s the widget representing the screen itself, the GamePage widget, and it contains all the other widget on the screen: the text items, the buttons and the slider.

There are different types of widget in Flutter. These different types of widget have one thing in common: they can all be drawn on the screen.

What makes each type different is a combination of what they look like and what they do. So far, you’ve worked with a few different types of widgets.

Some widgets, like Text, are directly visible on the screen, and show some type of content. Other widgets, like Row and Padding alter the layout of other widgets like Text and buttons.

In this code snippet, Row, Padding and Text are all widgets. MainAxisAlignment, EdgeInsets, TextStyle, and FontWeight are not.

Let’s look at those Bullseye screen widgets again, but this time with the specific types of widgets called out. You can see that the user interface for Bullseye is mostly made up of Text and Button widgets.

The Slider widget lets a user enter a number by sliding a control. You’ll learn about this widget in the next episode.

Some of the widgets that we will use on the game screen are invisible. One of them is a Columns, who’s job is to arrange its children views in rows. Effectively, the user interface for Bull’s eye is just four rows of widgets in a Columns, with a little bit of spacing added in.

To get started, open your project in progress or download the starter episode. The game works by the user moving the slider between one and a hundred. The target value is random so we need to provide that to the user.

We’re going to create a new prompt widget. We could create it in main.dart, but you’ll notice that main.dart is getting a bit crowded. A better approach is to create a separate file for each of your custom widgets. That way, it’s easier to organize your code and find things that are broken.

Create a new file by selecting the lib folder and pressing the new file button. Give it the name, prompt.dart.

The first thing we need to do is import the material library.

import 'package:flutter/material.dart';

The material library gives us access to our widget collection. As experiment, let’s create a new stateless widget. Type the keys, ST and give it the name, ‘Prompt’.

class Prompt extends StatelessWidget {
  const Prompt({ Key? key }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      
    );
  }
}

You’ll see that a constructor is provided in our new widget. A constructor is used to create an object and set values to it. This just takes in a key which is a way for Flutter to identify the widget. That’s not important now.

Let’s add a field for the random value. We’ll call this targetValue.

final int targetValue;

By setting it to final, we signify that the value won’t change. You’ll notice we get a compile error. We need to update the constructor to take in the targetValue. Update it to the following:

const Prompt({Key? key, required this.targetValue}) : super(key: key);

Okay, now comes the layout. We’re going to layout our prompt in a column. This is will place our widgets on top of each other. Add the following to your build method:

Widget build(BuildContext context) {
    return Column(
        children: <Widget>[

        ]
    );

We’re passing in an array of widgets. Arrays can come in lots of different types. For an example, an array of integers or an array of Strings. The angled brackets just indicates the array types. Now let’s add our first widget. A text widget.

Text('PUT THE BULLSEYE AS CLOSE AS YOU CAN TO')

Okay, it’d be nice for us to style the text to make it look nice instead of using the default style.

The material library comes with a set of text themes. This theme includes font type, size and even color. Using a theme provides a consistent look and feel in your app. Thankfully, you have liberty to customize these themes. One way to customize a theme is to a copy theme, and then manually make your adjustments. We’ll do this now and store our changes in a file that contains all of our themes.

We’ll be using static properties in Dart. Don’t worry about what this means. This is just a language feature that makes it easier to access the properties. Don’t worry about the syntax. Just follow along. In time, you’ll come to understand Dart’s syntax.

Create a new dart file and call it text_styles.dart. First thing to do is import the material library.

import 'package:flutter/material.dart';

Now, we’ll create a new class called LabelTextStyle.

class LabelTextStyle {

}

Next we create a new style for bodyText1 that returns a text style.

static TextStyle? bodyText1(BuildContext context) {

}

Then we copy the bodyText1 theme passing in the properties we want to override.

return Theme.of(context).textTheme.bodyText1?.copyWith(
    fontWeight: FontWeight.bold,
    fontSize: 12.0,
    color: Colors.black,
    letterSpacing: 2.0,
    );

We sent the weight to bold. The size to 12. The color to black. And the spacing to 2.0. Switch back to prompt.dart. Import text_styles.dart.

import 'text_styles.dart';

Now add your new style.

Text(
    'PUT THE BULLSEYE AS CLOSE AS YOU CAN TO',
    style: LabelTextStyle.bodyText1(context)),

Now let’s add another Text widget.

Text('$targetValue')

The dollar sign in front of the targetValue is used to access the value of the targetValue variable inside the string. And if in case the variable isn’t a string, then convert it automatically to string for simple data types like int, double, etc.

In main.dart, add an import for prompt.dart

import 'prompt.dart';

Now scroll down to _GamePageStateState, and in the build method, replace the first Text widget with your prompt widget.

const Prompt(targetValue: 100),

Now build and run your app on the emulator. Now you have your new text in place. Notice the top of the text is bolded. Let’s add some padding. Open up Prompt.dart.

Select the Text widget with your target value. Right click and choose the refactor option. Finally, choose the wrap with Padding option. It provides a default padding of 8 which works great for us.

Save and you’ll see the emulator automatically update. Nice work!