BMI Calculator in Flutter – Part 2 – Weight

by Sep 3, 2018BMI Calculator, Flutter13 comments

Hi again! I’m coming back with another part of the implementation of Jhony Vino‘s awesome BMI Calculator design. If you didn’t see the previous part, where I implemented gender picking, you can find it here. In this post, I will focus on creating a widget for weight input. Let’s see how it goes ?

The design

Starting point

. . .

Title widget

First, we need to update Title widget we created for the previous part, both gender and height are having a subtitle with the unit displayed. In order to have two texts side by side but with different fonts, we will use RichText widget:

When we got the Title widget, we should create WeightCard widget and include the title in it:

After we put the WeightCard inside InputPage from the previous post, we get this view:

Card title

. . .

Background widget

Now let’s add a background for the picker. To draw a pointer arrow we will use provided svg asset. Like in the previous part, Dan Field‘s flutter_svg package will help us with that. The background itself will be a simple container with proper decoration. I designed it to accept child parameter which will be the actual slider.

To show that widget, we put it below the title. We simply wrapped it inside ExpandedCenter and Padding to achieve this look:

Slider background

. . .

Slider

Now it’s time to get to the core of the widget, which is a slider which allows the user to pick the weight. In the design, it is shown that the user clicks on numbers to change the value but it seems reasonable to assume that the user can also swipe horizontally. I think the easiest approach would be to use horizontal ListView. The feel is similar to the Numberpicker package I created so I will base the implementation on that. For now, let’s focus on creating a simple ListView:

So, let’s break it down:

  • The WeightSlider accepts min and max value. Based on those, we can calculate how many items there are to display.
  • We add 2 extra items to the ListView at the start and at the end so that we can have the boundary values displayed in the middle of the widget.
  • We specify scroll physics to BouncingScrollPhysics which is the default iOS one (we don’t want Android’s way there).
  • We also pass width of the widget. We always want to display only 3 items, so we need to know how much space one item needs to place (and set it in itemExtent). Theoretically we could obtain this metric inside the widget, however, later on, we will use ScrollController with initial scroll offset which will be needed before the first build.

We’ll use this widget like this:

And this is what we get:

Slider listview

. . .

Highlighting selected value

Well, the ListView itself is not very impressive. I think it’s time to make it actual picker. But how are going to do that? I will use the same approach as in the Numberpicker – let the WeightSlider remain Stateless and just pass a value to it and also get the callback when the value in the middle changes.

Basically what we do is adding a NotificationListener to a ListView. It will invoke _onNotification on every scroll event. Then, we calculate the value of the middle element based on the notification’s Offset. If the calculated value is different than the passed value, we call onChanged method.

Alright, but what happens then? We will store the selected weight in WeightCard and simply update state:

But wait, we still don’t highlight the middle element. Even though we change the value, the user will not know that he actually selected his weight. We can fix it by changing the current _getTextStyle method:

Now we can see what we got:

Highlighting slider

. . .

Bugs

Not everything works like it should at this moment, let’s go through bugs and fix them!

Number wrapping

In the previous gif, I intentionally didn’t show you what happens when you go over 100. Unfortunately, in those cases, numbers are being wrapped. The first idea would be to just lower the font size for the highlighted item but it will never assure us that on some specific resolution the number will look the same. To handle this problem, we need to wrap the Text inside FittedBox:

Making the text scale down results in such difference:

Before

After

Centering position

What we can also notice is a situation when after user selected value, it is not being centered. I think it should be. To achieve that we will use ScrollController. We can detect if the user stopped scrolling and if so, we can animate to the selected value. Let’s see that in code:

Before

After

The initial position and overscroll

Last two issues we need to address are initial position and overscroll. So far our listview always started at the beginning even though I set initial weight as 70. We can easily fix it by adding an initial offset to the ScrollController:

I’ve also noticed that when I run the app in release mode LayoutBuilder is returning (0;0) constraints on the first build. I am not sure why it only happens in release mode but we can assure that only non-zero constraints are passed with this code:

Also, when the user scrolled to the edge the calculated value was higher than maxValue. It could be easily fixed with this line:

. . .

Tap to select

The last feature we need to add for this component is handling tapping on the numbers. Since we already have ScrollController and animate method, all we need to do is wrap list items with GestureDetectors and handle taps.

What is worth noticing is that we are using translucent hit test behavior so that both taps on texts as well as outside of them are being captured.

. . .

The final result for this part looks like this:

EDIT: You cannot see on tap animations because gif frame-rate is too low but believe me, they’re there ?

And that’s it!

If there is anything unclear or there is something I did wrong, please leave a comment, let’s learn together!

You can find the full code for this stage in here and other posts related to this project in here.

Cheers ?

? BMI Calculator ?

 

Be sure to check out the other posts in the series!

Marcin Szałek

Founder of Fidev

Flutter enthusiast since Alpha release in 2017. Believes that sharing is caring, which lead him to start a technical blog dedicated fo Flutter in its early days. Loves to see beautiful designs become real apps and is willing to help make it happen. Enjoys sunny beaches far from home.

Join the newsletter!

Join the newsletter to keep track with latest posts and get my special Widget to animate views' entrances without any hassle for FREE!

Check out other posts!

Stripe Checkout in Flutter Web

Stripe Checkout in Flutter Web

Flutter Web is getting more mature every day. If you want to accept payments using Stripe Checkout in your Flutter web application, this article is just for you!

Stripe Checkout in mobile Flutter app

Stripe Checkout in mobile Flutter app

Have you ever struggled to integrate card payments into your mobile Flutter app? If so, today is your lucky day! In this post, I present how to use Stripe Checkout in the Flutter app without any hassle!

Interacting with Widgets using Framy

Interacting with Widgets using Framy

Have you ever developed a widget or a page and you wanted to make sure it works correctly in different scenarios but then it turned out that you can’t just reproduce all the cases you want to cover? Framy may solve such problems!

Share This

Share This

Share this post with your friends!