I Am Rick (Episode 2): Rick’s Business Card

Learning Flutter with Rick Grimes.

Alexandros Baramilis
16 min readNov 10, 2019

Motivation

One of the best ways for me to learn something is to write about it, so I’m making this series with the notes that I take as I go through App Brewery’s Flutter Bootcamp. Publishing my notes and code also forces me to maintain a high standard and allows me to easily go back to review or update them.

I’m keeping the Rick Grimes theme alive for as long as I can, because I’m really enjoying having Rick on my phone’s home screen and looking at his pretty face every day. 😄 Don’t worry, App Brewery’s bootcamp doesn’t have Rick in it, but I still highly recommend it if you want to learn Flutter.

If you’re loving Rick though, check out I Am Rick (the first episode), or if you’re having trouble installing Flutter on macOS Catalina, check out Setting up Flutter on macOS Catalina.

ALERT: Next episodes are out!

and I also added a Github repo for the series.

Rick’s business card

Since Rick left the show (inside a helicopter!), we need to get him a new job. For this, he will need a new business card.

To maximise his chances of making it back on screen, we’ll make him a cool Flutter app to show off his brand new business card to both iOS and Android users.

Hot reload

To make our life easier, we’ll first upgrade our app from the previous episode, to make full use of one of Flutter’s coolest features, hot reload.

What is hot reload?

If you’ve done any native Android/iOS development before, you know how long it takes from making a change to actually seeing it on a phone/simulator screen. If you haven’t, well, it’s essentially a lot of wasted time and a disruption of focus, especially if you open other websites to kill your time while you wait…

With Flutter, you make a change, save your project (since hot reload is hardwired with save) and see it appear almost instantaneously on screen! 🤯

This allows you to massively minimise the step from change to test, so you can develop faster and with fewer errors.

Since this change-test cycle is so fast, you can practically design your app on the fly, while you’re writing the code. It’s almost as fast as using a dedicated designer tool like Sketch. (don’t worry Sketch, I still love you and won’t replace you anytime soon)

So you could say that with Flutter you can play the role of an iOS/Android/web/desktop developer AND designer. And if you couple Flutter with Firebase, you can even play backend developer. One-man teams have never been that easy. (or lonely?)

Anyway, let’s get a bit more technical.

Not all changes will work with hot reload. In order for hot reload to work, you need to have the code that you modify wrapped inside a Flutter stateless or stateful widget.

This is the code we had from the first episode:

import 'package:flutter/material.dart';

void main() {
runApp(
MaterialApp(
home: Scaffold(
backgroundColor: Colors.lightGreen[200],
appBar: AppBar(
title: Text("I Am Rick"),
backgroundColor: Colors.red[800],
),
body: Center(
child: Image(
image: AssetImage('images/rick_gun.jpg'),
),
),
),
),
);
}

To have all our changes work with hot reload, we need to wrap our MaterialApp inside a StatelessWidget.

To bring up the autocomplete for StatelessWidget we type stless and hit enter. We’ll name our new class IAmRick. This class now extends StatelessWidget.

Now we’ll cut all the MaterialApp code and paste it over the Container() code after the return statement of the build method inside the IAmRick class. Delete the extra comma after the closing parenthesis of MaterialApp and hit save to get everything nicely formatted again.

If you haven’t done so already, you can also hardwire the code formatting with the save action by going to Android Studio ➝ Preferences ➝ Languages & Frameworks ➝ Flutter, and ticking ‘Format code on save’ under the Editor section.

Finally, we need to call IAmRick() inside runApp().

import 'package:flutter/material.dart';

void main() {
runApp(IAmRick());
}

class IAmRick extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.lightGreen[200],
appBar: AppBar(
title: Text("I Am Rick"),
backgroundColor: Colors.red[800],
),
body: Center(
child: Image(
image: AssetImage('images/rick_gun.jpg'),
),
),
),
);
}
}

Now if we wanted to, say, change the AppBar colour to a darker shade of red, we could type Colors.red[900], hit save and the new colour would show up immediately!

This applies to any changes that we make inside the build method of IAmRick, or any other class that extends StatelessWidget.

Note: If you were already running the app while you wrapped your code in a StatelessWidget, you need to stop it and run it again in order for hot reload to work.

Finally, another big advantage of hot reload that is worth mentioning, is that it doesn’t lose the state of the app. For example, if you’re editing a form and have entered some data and suddenly want to change the UI, you don’t have to enter that data again every time you reload the app! I’m sure many people can relate to the pain of creating a login screen and having to login every time you want to make a small UI change and test it.

Hot restart

If we don’t want to keep the state of the app after making a change, we can use hot restart instead. This works like hot reload, but also resets the app state.

This will propagate changes that are not wrapped inside a stateless or stateful widget and can also be used in cases where hot reload doesn’t work.

You can find the commands and options for Hot reload and Hot restart under the Run menu in the toolbar in Android Studio, or you can click the little thunder icons next to the Console at the bottom.

Hot restart takes just a bit longer than hot reload, but nowhere near as long as native development or stopping and starting your app.

SafeArea

No, this is not a safe area from Walkers, but a safe area for your content, so it doesn’t overlap with the phone’s bezels, status bars, bottom bars, etc.

We’ll now remove the Center widget including the pretty Image of Rick from the body property of the Scaffold and replace it with a SafeArea widget.

The SafeArea can have a child just like the Center widget that we had before.

Containers, Columns and Rows

The Flutter team has put together a neat Widget catalog.

In there, we can find all sorts of widgets, organised in various categories. Since we’re exploring layout in this episode, we’ll go into the Layout section.

In there, we can find two types of widgets, single-child layout widgets and multi-child layout widgets.

The names are quite self-explanatory here.

Container

Single-child layout widgets, like the Center widget, contain the child property that can take only one widget.

The Container widget is also a single-child layout widget that acts pretty much like a layout box that you can paint, position and size.

A Container’s layout behaviour is somewhat complicated since it depends on interactions with many other widgets. I won’t go through it here but it’s worth reading about it in the documentation.

Adding margins to the Container with EdgeInsets

We can use the margin property to add EdgeInsets:

There are many options, such as:

  • EdgeInsets.all(20.0) // adds margin on all sides
  • EdgeInsets.symmetric(vertical: 50.0, horizontal: 20.0) // adds one value to the top and bottom and another to the left and right
  • EdgeInsets.fromLTRB(30.0, 10.0, 50.0, 20.0) // from left, top, right, bottom
  • EdgeInsets.only(left: 10.0) // adds margin only on specified side

Adding padding to the Container

While margin insets the container itself, padding insets the child of the container relative to the container.

To add padding, we use the padding property of the container and we add EdgeInsets like before.

Columns and Rows

If you want to layout widgets with multiple children, an easy and powerful way is to use the Column and Row widgets.

Column and Row widgets are multi-child layout widgets, so they have the children property, which takes a list of widgets that looks like <Widget>[]. You can enter your widgets inside the square brackets, separated by commas.

It’s also worth reading more about their layout algorithm in the documentation, but one thing to keep in mind is that the Column widget takes up as much vertical space as is available, but horizontally it takes only as much space as its children need. (and vice-versa for Row)

We can change that setting with the mainAxisSize property. For example, we can set it to MainAxisSize.min so it takes up only as much space as needed to fit its children.

We can set the direction that the children are ordered. By setting verticalDirection to VerticalDirection.up the children are placed from bottom to top. If we set it to VerticalDirection.down they are placed from top to bottom (the default setting).

We can change the alignment of the children by setting the mainAxisAlignment property. The default is MainAxisAlignment.start which places the children as close to the start as possible, but we can change it to .end, .center, or if we want them to be spaced out, we can use .spaceEvenly, .spaceBetween, etc.

To change the alignment of the children on the other axis, we can set the crossAxisAlignment property. The cross axis is the opposite of the main axis. So for Row, the main axis is the horizontal axis and the cross axis is the vertical axis. For Column, the main axis is the vertical axis and the cross axis is the horizontal axis.

To align the children to the end of the screen, we can create an empty Container and give it a width of infinity by setting its width property to double.infinity. This creates a Container with zero height and width as wide as the screen.

We can also stretch all the children to the maximum width that the column can take by setting the crossAxisAlignment property to CrossAxisAlignment.stretch

To provide some space between the children of the column, we can enter a SizedBox widget in between the children, which as the name implies is a simple box with height and width, no paint or other fancy stuff. We only need to set its height property in a Column, or its the width in a Row.

Back to Rick’s business card

Since we want to layout Rick’s business card from top to bottom, we’ll give SafeArea a Column child.

CircleAvatar

Inside the Column, the first child of the children property will be a CircleAvatar, a handy widget used to represent a circular photo of a user, or his initials in the absence of a photo.

Handy shortcut: If we click on CircleAvatar, or any other widget in the code and tap control + J on macOS (or ctrl + Q on Windows), we get the quick docs, a popup that shows a short description and all the properties that a widget can have. Using this shortcut we can quickly identify what properties we can and want to set.

We’ll begin by setting the radius of the avatar to 50.0 and the backgroundImage to AssetImage(‘images/rick_profile.jpg’). You’ll have to find a nice profile pic of Rick and add it to your images folder as described in episode 1 and then update the pubspec.yaml file to include the new asset, remembering to get the indentation right.

flutter:// ... comments and other stuff ...  assets:
- images/rick_gun.jpg
- images/rick_profile.jpg

CircleAvatar does the circular cropping automatically for you, so you can just include a square picture.

If you save your project now, you should be able to see Rick’s face inside a circle.

It’s nice, but I would like to centre it. Before we do that, it will be handy to learn how to use the Flutter Inspector to inspect our widget sizes.

Inspecting widgets with Flutter Inspector’s Widget Mode

If you click on Flutter Inspector on the right sidebar on Android Studio you will get a visualisation of the widget tree.

By clicking on the first button on the toolbar (above Widgets), the one with the target icon, we can toggle Widget Mode. In Widget Mode, we can have the Simulator side by side with Android Studio and when we tap on a widget in the widget tree, it gets highlighted on the Simulator. This is quite handy when inspecting our layout, to visually identify the widgets sizes, etc.

We can also locate a widget in the widget tree by entering Widget Mode and then tapping on a widget in the Simulator. This only works once though. To select another widget, we need to exit and enter Widget Mode again.

Stretching the Column to centre its children horizontally

If we use the method above to inspect our Column, we can see that although in the vertical direction it stretches all the way down to the end of the screen, in the horizontal direction it takes only as much space as Rick needs.

Since the default property value for crossAxisAlignment is CrossAxisAlignment.centre, if we simply stretch the Column to take up all the horizontal space, that should centre all its children horizontally as well.

To do that we need to simply add a SizedBox as a child and give it a width of double.infinity.

Now Rick is centred horizontally.

If we want him to be centred vertically too, we need to set the mainAxisAlignment property of the Column to MainAxisAlignment.center.

Text with TextStyle

For the next child in the Column, we’ll add a Text widget with the title of ‘Rick Grimes’. This just adds some plain text.

To customise the text style, we can set the Text widget’s style property to a TextStyle widget. We can then set the various properties of TextStyle, such as color, fontSize, fontWeight, etc.

I’m going to set the fontSize property to 36.0 and the fontWeight property to FontWeight.w600.

Adding custom fonts

The title is already much better with TextStyle, but I want to make it more Walking Dead by using a custom font.

Specifically, I want to add The Walking Dead’s Dead Kansas font, created by The Wondermaker.

Download and extract the ‘Dead Kansas.ttf’ file, create a new directory called fonts (by right-clicking on the i_am_rick folder under the Project navigator, selecting New ➝ Directory, and naming it fonts) and drag and drop the file into that directory. You can easily locate said directory by right-clicking on it and selecting ‘Reveal in Finder’.

Next, we need to declare that font in the pubspec.yaml file like:

flutter:// ... comments and other stuff ...  fonts:
- family: Dead Kansas
fonts:
- asset: fonts/Dead Kansas.ttf
- family: Writing You A Letter
fonts:
- asset: fonts/Writing You A Letter.ttf

Now we can go back to the TextStyle widget and set the fontFamily property to ‘Dead Kansas’. I will remove the fontWeight here since this font is already quite bold!

I will also add another SizedBox between the CircleAvatar and the Text with a height of 10.0 to create some space between the avatar and the title.

For the subtitle, I’m going to use another one of The Wondermaker’s fonts, called Writing You A Letter. This font reminds me of the handwritten messages that often appear in walls throughout the whole Walking Dead universe.

The process is exactly the same as before, with the difference that we have to capitalise the text in this case, as lower case letters will show various drawings instead of letters.

I will also set the Text widget’s textAlign property to TextAlign.center in order to align the text at the centre of the Text widget. This wasn’t needed for the title before because the text was much smaller.

I’m going to use a 24 font size, a fontWeight of FontWeight.bold, and add another SizedBox in between with a height of 10.0.

Note that I renamed the file from ‘Writing_You_A_Letter_update.ttf’ to ‘Writing You A Letter.ttf’ for consistency.

Important: To make the new fonts appear, we have to stop the app and run it again.

Using Material Icons

By using material icons instead of images, we can easily customise their size, colour, etc.

We can easily add them to our widget tree by using the Icon widget. If you scroll down the documentation of the Icon widget, you can find all sorts of commonly used icons that are ready to use inside your project.

A handy website to help you select colour palettes and icons based on material design is materialpalette.com.

To add Rick’s phone number, we’ll add a Container as the next child of Column. We’ll set a Row as the Container’s child and for the Row’s children we’ll add an Icon widget.

Then it’s really simple to add an icon. All we have to do is type Icons.phone or Icons.email or whatever icon that we need inside Icon’s parentheses.

We can also edit these icons by setting the Icon’s properties. For example, we can resize them by setting the size property. This won’t cause them to pixelate even at very large sizes because these icons are drawn from an icon font instead of displayed from a static asset. However, Google recommends a size of 24px.

Then we’ll add another Text to the Row and style it.

Since we have a Container here, we can use the margin property instead of adding another SizedBox. Simply set it to EdgeInsets.only(top: 10.0) to achieve the same effect.

To create this written on wooden panel effect, I will give a color of Colors.brown[600] to the container, a width of 360.0 and a padding of EdgeInsets.all(10.0). I will also make the Icon white by setting its color property to Colors.white.

Rinse and repeat for the email and we get:

(Optional) Rounded corners

To add rounded corners to our Containers, we can use the decoration property like:

decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.brown[600],
),

You can’t set both the Container’s color and decoration properties, so if you set a decoration, set the decoration’s color property instead and remove the Container’s color property.

The Card widget

The Flutter team has thought about all the common use cases and made handy widgets for all these occasions, such as the Card widget.

The Card widget acts kind of like a Container, but has built-in rounded corners and nice shadows.

We can swap our Container widgets for the Card widgets. We just have to make a few modifications, like removing some properties that are not supported by the Card widget.

  1. Remove the padding property
  2. Remove the decoration property, but add back the color property
  3. Remove the width property

The Padding widget

To add padding to a Card, we can wrap its child inside a Padding widget.

To do this easily, click on the Row and wait for the lightbulb icon to appear. Click on it, select ‘Wrap with new widget’ and then type Padding.

The Padding widget has a padding property that we can set to EdgeInsets.all(10.0) like before.

The ListTile widget

There is a final improvement we can make, by replacing our Row widgets with ListTile widgets.

ListTile widgets do exactly what we want here. From the docs, a ListTile widget is:

‘A single fixed-height row that typically contains some text as well as a leading or trailing icon.’

And they have padding too, so we don’t need to wrap anything in a Padding widget.

Add a ListTile widget in place of the Padding widget and set its leading property to the Icon you had inside the Row before and its title property to the Text you had inside the Row before.

Using the Card and ListTile widgets we made this look better than before and with less code.

Final code

The final code for the app is here. I also made a main Github repo to gather all the code together.

Rick’s new job

Rick’s business card app was so cool, that it landed him a new job as the star of an epic movie trilogy in The Walking Dead universe! You can watch the first teaser here.

Survival

This was the longest article I’ve ever written.

If you made it this far, you’re a true survivor and you would do great in the post-apocalyptic world of the Walking Dead.

Thank you for reading!

Next episodes are out!

and I also added a Github repo for the series.

--

--