Notes: 02. Create Your First Reusable Widget
The students materials have been reviewed and are updated as of September 2022.
The update material uses null safety and also proper use of const and final keywords as per the latest Flutter guidelines. This is to encourage the students to use the latest Flutter best practices.
- Design the widget
- Decompose the design
- Build the basic widget
- Customize the look
- Determine the user interaction
- Define the dependencies
- Implement the dependencies
- Test the widget
These are the steps we are going to take whenever we want to create a resusable widget. We’ll first build out the design, determine the different interactions for the widget, implements the required parameters or dependencies and finally we try it out.
The first widget we’re going to create is an episode card widget that would be reused in different parts of our app. Here’s what the design looks like. It is a basic card UI that is going to have a dynamic UI based on different conditions. Decomposing this design is quite simple. (Fade in the decompostion) This widget can be built using ready made flutter widgets. Let’s go ahead and build it out.
Demo
Inside Android Studio, open up the episode_card.dart file inside the widgets folder.
It is a stateless widget that displays the title of the podcast episode in a Text widget.
This text widget is a child of the container and the container has the box shadow effect.
The container is wrapped with a GestureDetector widget which is used to add a click interaction to the widget.
Clicking on it takes us to the corresponding podcast episode page.
Also, it has an episode parameter which is passed as an argument whenever this widget is called. The episode data is used as contents of the card UI and also passed to the episode page via the Navigator. Finally, the episode card widget is used inside home screen widget. (Open it up) In here, we have an episodes list which we map through and display the episode cards as children of the ListView. (Go back to episode card file)
Okay, let’s update this widget to match our design. Update your code to the following: (I’ll paste it)
child: Column(
children: [
Stack(
children: [
Container(
height: 200,
child: Icon(Icons.mic, size: 64),
),
],
),
Container(
height: 120,
padding: const EdgeInsets.all(16),
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
episode.title,
style: Theme.of(context).textTheme.headline6,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
Row(
children: [
Text("Host: ${episode.host}"),
Spacer(),
Text("Episode: ${episode.id}"),
],
),
],
),
),
],
)
(Save your work) The episode card is now composed of more widgets to create the desired design. But let’s just take a quick tour of the code i added. First, we have a column that takes two children: a container to display the podcast icon and a second container to display the card details. The card details container contains a column that displays the episode’s title and a row that displays the host name and episode id. Everything here is pretty basic widget composition.
Okay, now that we’ve got our design. Let’s see how we can customize it.
Remember, we want this card to be able to display these two interfaces. We dont want to create a separate widget for this. We want to be able to use this same episode card widget for both designs. We want to be able to use these designs in different scenarios.
Now, notice this section down here: the card details section. It is the same for both views. This portion here can be extracted and reused for both the tall and the wide card. Let’s go ahead and do that now.
Demo
The card detail section starts from this Container here. Go ahead and click on it then select extract widget and then name it CardDetail. It is now a separate widget that we can use for the two designs we’re creating.
Cool, so the next thing i’ll do is to also extract the section starting from the column and i’ll name it tall card. I’m doing this because we’ll be creating a second widget called wide card next. And later on, we’ll be swapping between these two widgets based on the given conditions.
Okay let’s create the widecard widget. For this, i’ll simply copy and paste the tallcard widget and rename it to widecard. This time around, we’ll be returning a Row. So i’ll change the column to a row and then update the first container to have the folowing dimensions:
Container(
height: 120,
width: 100,
...
Finally, let’s swap the tallcard with the wide card in the episode card widget above to see what the design looks like.
// child: TallCard(episode: episode),
child: WideCard(episode: episode),
Save your work. And you can see, the widecard matches the expected design. Plus, we reused the card detail widget for both widget designs. But is that all to it? Are we always going to be manually swapping the designs like we just did? Let’s talk about how we can make the designs dynamic in the next episode.