Smart / Dumb Components in Flutter (Forms)

In another article, I laid out a way to implement, in flutter, the presentation/container pattern. But my example was super simple; to show a list of items in a ListView. But there are other more complex cases than that. Let’s take forms for example.

Component characteristics

Let’s remember some of the characteristics that the presentation and the container component have.

Some of the presentation component characteristics are:

  • It should not hold any state
  • It should only use provided values
  • It should emit events or call callback functions

Some of the container component characteristics are:

  • Should hold state
  • Should be composed of one or more dumb components
  • Should provide the state as input to its dumb components
  • Should respond to events or callbacks from the dumb components

The model

A form is usually useful to create/alter some state, which is usually encapsulated in a model object. For our example let’s say that our model is just an Itemwith a name.

Container form component

According to the pattern the Item‘s state is going to be kept in the container component, which will pass it and get it back from the presentation component. So, we can implement the container component around a signature of the presentation one, in which it will get an Item as input and return one as an argument to a callback.

Presentation form component

The implementation of the presentation component is fairly straight forward.

Dealing with FormState

You can see right away what the problem in this approach is. It will never update the Item, as the only way to do so is to supply the onSaved callback in the RaisedButton.

onSaved: (value) => this.values['name'] = value,

But of course, this is not enough, because the onSavedcallback is going to be executed when you call method. Alright, where is the FormState then?

FormState from a Key

The most obvious way to get the FormState is to assign a Key to the Formcomponent and then call currentState on it.

Although the formKey is a great solution unfortunately, it does not fit the pattern needs. The reason is that StatelessWidgetis not the ideal place to store long-lived variables such as the Form‘s key. So, you would have to provide the formKeyfrom the container component, which feels a bit unnatural, because it leaks implementation details out of the presentation component.

FormState from Form.of()

There is actually a way to obtain the FormState without using a Key. The Form component has a static method of for this purpose and it will return the FormState by using an InheritedWidget. But this method has a caveat. As its documentation states, it “Returns the closest FormState which encloses the given context.” and in our case it will return null, as we obtain the context before we create the Form.

A nice workaround would be to introduce a StatelessWidget, which will wrap an element in the Formin order to obtain a BuildContext which contains it. The perfect candidate for this is the submit button.

How it all fits together

Introducing the FormSubmitButton

As I grew accustomed to this pattern while using Angular, I am continuing to use it in my flutter projects. Therefore I ended up doing it way too many times to not put it in its own library. So, I did just that and got form_submit_button published.

You have to define the type of button you want to wrap, as well as the child widget. You get the FormState in the submit callback.

In the end, the decision to use or not the container/presentation component pattern is up to you. But if you decide to give it a go, I hope you found this article useful and that you will give form_submit_button a try.

Please follow and like us: