Before writing any code let’s just take a look at our widget and see what all components would be required to build it. I see the following components -

Taking all the animations into consideration, there are a few more things (let’s name them KEYNOTES as I will be referring to them time and time again in the article) we need to keep in mind:-

1) The newly selected option over the complete duration of the animation, ends up taking more space than all of the other options.

2) The Text is only visible in the currently selected option.

3) When a new option is selected, the text slides and fades from the currently selected option and then appears again, sliding its way into the newly selected option.

4) The newly selected option’s icon animates in 3 different ways — it slides up, it slides towards the left or the right, and it skews. This animation runs in complete reverse for the previously selected option.

Okay, so that’s a lot that we need to achieve. But don’t worry, we’ll build it brick by brick, going one step at a time.

First, let’s just try to implement the KEYNOTE NO. 1

Our need is to be able to resize the desired option along its width as and when we require it. For this, we use the concept of a Flex . The Flex widgets (ex- Flexible, Spacer etc) allow us to size the element in proportion to other Flex widgets at the same hierarchy level. For example, if I consider two Flexible widgets, and provide them with a flex of 1 and 2 respectively, then they will take the available space in a 1:2 ratio along a particular axis. This axis is defined by their parent. If the parent is a Column, then the widgets flex in the vertical direction to fill the Column and if the parent is a Row, then the widgets flex in the horizontal direction to fill the Row.

So let’s see how can we use this to achieve our first KEYNOTE.

We define a List of flex values as flexValues and assign these flex values to the respective Flexible widgets. You might be thinking that if it was about proportionally sizing the widgets, then why are we giving them such large flex values such as 100 and 150. Rather, we should just provide them with 2 and 3 as these values give the same ratio. Let me explain, the flex can only be an integer. We are going to use these flex values to animate the click event. So if we animate our flex value to go from 2 to 3, the result would be an abrupt change, a change straight from 2 to 3. Instead, if we animate the flex value to go from 100 to 150, we are toning down that abruptness by 1/50th.

We get the following output -

Now let’s include a small test animation. This animation shows us how the change in flex values affects the width of each of the individual options. We just make the following additions in the SimpleBottomAppBarState class -

We see the following output -

Can we now safely say that we now know how to implement the KEYNOTE NO. 1? Ummmm……well not really. So, what are we missing? A lot of widgets in Flutter take the dimensions of their parent if they do not have a child, but when they are provided with a child they take the dimensions of their child. So how does that affect us? As mentioned in the Documentation -

By default, the placeholder is sized to fit its container.

This basically means that the Placeholder widget is forcing the Flexible widget to expand to as much as possible without overflowing(along the direction specified by the Row). If we replace the Placeholder widget with a widget that doesn’t force the Flexible to expand, the Flexible will shrink to the size of its child.

To understand this, replace all the Placeholder widgets with the Icons(Home, Timeline, Events, History) that we need. Here is the code -

This is the output -

How to fix this?

We somehow need to find a way to make the Icons force the Flexible to take as much space as possible. The solution to this is the fit property of the Flexible widget. The fit property can have two values, loose or tight. As the documentation reads -

loose → const FlexFit

The child can be at most as large as the available space (but is allowed to be smaller). tight → const FlexFit

The child is forced to fill the available space.

Therefore, what we need is the ‘tight’ value for our fit property of the Flexible widget. So let’s make this change in the code -

This is the output -