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.
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
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
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
onSaved: (value) => this.values['name'] = value,
But of course, this is not enough, because the
onSavedcallback is going to be executed when you call
FormState.save method. Alright, where is the
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.
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
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
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.
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.