Creating a Flutter widget Part 1: Should be easy… right?

Important: Most of this post is written as I am writing the code as a mean to put my thoughts into words, so the style is not very polished.


Alright, I have created a simple package in the past, a Flutter package, but it was not a Flutter widget. And when I say simple it was REALLY simple. Less than 100 lines of code, just a couple helper methods.

Every time I got some piece of code I thought could be a package, it was almost immediately shutdown because there was already a package published. Until I got an issue with editing a Duration property in an app I have been developing. I did find a package that could solve my problem this time. But it was not exactly what I needed. So this little problem became my excuse to start this little project.

Context

I had a form that needed to capture a Duration. This is very easily achievable using a couple of TextField’s but the result wasn’t that good. The duration_picker was an option, but I thought it would not fit my use case. I wanted something more like the flutter_spinbox. And since that doesn’t exists, it was my chance to build it, and build it right.

Planning

I needed to take into account a few considerations. First that I only can dedicate a few hours a week to this project. Meaning the progress could be slow. Second, I don’t want the project to take forever so delivering incrementally is a must.

This gives me some direction on how to approach the project:

  • Start small, to support only the most basic use case at first
  • Think before adding any parameters to the widget. To avoid introducing breaking changes in the future as much as possible.
  • This is a good opportunity to learn how to properly test in Flutter.

Starting point

Thinking about starting small, the initial plan is to create a widget that:

  • Consists of a center Text representing the duration, and two icon buttons, to increase and decrease the value respectively.
  • Receives a Duration as value
  • Receives the value of the steps to add or subtract when pressing the buttons
  • Only shows in the format mm:ss. So only showing minutes and seconds.

Instead of starting from scratch I remembered there was a brick I could use from our friends at Very Good Ventures. Has some basic CI stuff and templates for GitHub.

Once the repo is created, the brick executed, added a Text, and 2 Icon Buttons, and a minimal functional example. This is enough for the initial commit.

Basic functionality

Next day, started by implementing displaying the duration in a Text widget. Nothing fancy, just the parsing the minutes and the seconds, padding with zero’s and that’s it.

Added a StepUnit enum to determine how much the duration will be increased or decreased. Then used the step unit and the step value to write the calculation method.

Once this was done, I thought that was it and it was just a matter of adding some style, adding some test and it was ready. But just noticed something I didn’t quite like…

Optimizing already?

I am not “optimizing”, but when I was testing the widget I noticed that I could get negative values if I go low enough, I didn’t like this as a default behavior, so I decided to change it.

That’s how I added the min and max values, both with of Duration type

const DurationSpinbox({
  required this.value,
  this.stepUnit = StepUnit.seconds,
  this.stepValue = 1,
  super.key,
  this.min = Duration.zero,
  this.max,
});Code language: Dart (dart)

And of course added the verification to not allow this widget to be built if the max is less than the min

@override
void initState() {
  super.initState();
  final minD = widget.min;
  final maxD = widget.max;
  if (minD != null && maxD != null){
    if (maxD < minD) throw ArgumentError('max is less than min');
  }
  _minutes = widget.value.inSeconds ~/ 60;
  _seconds = widget.value.inSeconds % 60;
}Code language: Dart (dart)

After that, I updated the calculations when the duration is changed to fall into the new boundaries.

Duration _getNewDuration(int millisDuration){
  final minD = widget.min;
  final maxD = widget.max;
  var newMillisDuration = millisDuration;
  if (minD != null){
    newMillisDuration = max(minD.inMilliseconds, newMillisDuration);
  }
  if (maxD != null){
    newMillisDuration = min(maxD.inMilliseconds, newMillisDuration);
  }
  final newDuration = Duration(milliseconds: newMillisDuration);
  return newDuration;
}Code language: Dart (dart)

Wrapping up our first approximation

Now it was time to add some color to our widget, and make sure the buttons are disabled when one of the limits was reached.

And with these changes, I’m kind of satisfied. Next step would be to write some tests and fix anything that was not addressed in this chapter. If you reached this point I encourage you to continue to follow along.

Tags :

Leave a Reply

Your email address will not be published. Required fields are marked *