warning: This is very much a draft, as I investigate how state can be implemented in Flutter

Validation: Where should it go?

In my current role we are working on a Flutter project where we were asked to take a previous application and evolve it for a new need. As a result we’ve ended up with two mechanisms to manage application state: Bloc and Form State. During development this has provided a few challenges:

  • A lot of thinking about where validation should go
  • Rebuild errors where a Bloc has triggered a rebuild, then a Widget inside the form has also trigged a rebuild at the same time
  • A duplication of state, as it’s held in both the Form Field Controllers and the Bloc’s state.
  • Typically the validation has been added to the form fields themselves meaning it’s not accessible to use if it’s requried for routing access guards

As a result of these challenges I’m wondering what is the easist way this could be done that resolves these challenges and makes it stupidly easy to implement and blatantly obvious to a developer whose starting fresh on the project, and or has to extend the application.

State and Stored Data

In a software system (micro services or monolith) there will be typically be 2 forms of stored data (excluding caching):

  1. Backend Data Storage such as: Databases, and File Storage
  2. Application State (for web you might refer to this as session state)

Generally speaking we’re not wanting to pass dirty data to our Backend Data Storage. Yes the backend needs to have it’s own validation to ensure it’s not being passed dirty data, but we can provide a better user exprience by providing prompt feed back to the user where we don’t need to wait or be connected to the systems backend.

Application State Management & Validation Requirements

  1. State is not duplicated within the Application
  2. Validation is in one place
  3. State and Validation are Unit Testable (business logic is separated from the UI)
  4. Rebuilding of Widgets in the tree are not repeated unnesscarily (rebuilds are placed as close to the leaves on the branchs as possible, and only happens when it needs to)
  5. It’s stupidly easy to implement new application features with
  6. The code is blantantly obvious to new developer

Now where do I find this framework?

Depending how you want you validation to work for the user may have an impact on framework that you choose to use. Consider the following.

When should validation message appear?

  1. On clicking a button to submit or save?
  2. As soons as the active field is not null? (or as soon as it could be valid i.e. number of characters, or full date entered)
  3. On exit from a field?
  4. On entry or focus to the field?
  5. On load of the form?

What to do with a form that includes a file upload?

Given you have a form to fill out and then a button to click to select and upload a file, how should it work?

  1. Disable the file upload button until the form is valid, and validate each field as soon as it’s not empty (note: clicking the button can’t validate as it’s disabled)
  2. Enable the file upload button, and validate on click. If invalid then it won’t allow file selection and the upload.
  3. Have a specific ‘Save’ button at the end.

In our application the file is uploaded, and posted to the backend immediately after it’s selected, and it has to include the form field data at the same time to enable an approval process downstream in the backend.

If we could hold onto a reference to the file to be uploaded, and then upload it with the form field data on clicking the ‘Save’ button, we could pull off the 3rd option (research required). Otherwise it’s option 1 or 2, and that probably will depend on what you’ve chosen for how you want your validation to happen to provide the best user exprience possible.

Where could / should the validation code be?

  1. Accompaning the UI Field code?
  2. In a Bloc?
  3. Encapsulated in code, enableing it to be used from anywhere as needed?

Options to Explore

  1. Single Bloc per form
  2. A Bloc per field

Single Bloc per form

There are 2 state structures you could emit from the Bloc with this that would prevent you from rebuilding the whole form each time a field changes:

  1. A state that contains all of the fields of the form. UI controllers to hold the interim state until the whole form is valid then add an event to the Bloc to save the data. Add the validation to the Bloc by including in the State emitted to each field an attached validtion message. This approach would mean you don’t hold on to a partially filled out form.
  2. A state for each of the individual field, a BlocBuilder could be wrapped around each field that listens for that state so it knows when to rebuild.

A Bloc per field

This seems like over kill. You’d need a Bloc for the form that listens to each field Bloc for when it’s valid. When they are all valid a BlocBuilder that is wrapped around the ‘Save’ button could change from Disabled to Enabled. Each field Bloc could validate as it sees changes.

Which packages are there, and which one(s) to choose?

How could you actuall implement this?

  1. A singleton with static variables would allow you to hold on to state, but it wouldn’t have a way for widgets to observe changes
  2. A singleton with a collection of stateful objects would allow you to have more than one of a particular type, but still wouldn’t have a way to observe changes

How do you observe changes? Is it Change Notifier?

Bring in Provider

package: provider
repo: rrousselGit/provider

Provider gives us the ability to notify listeners of changes. Both state and logic are bound into the same class that acts as the provider. It’s akin to traditional Obejct Orientated Programming. Some may refer too these types of classes as “hybrid”.

Bring in Cubit (from Bloc)

package: bloclibrary.dev
repo: felangel/bloc
site: bloclibrary.dev docs: Cubit

Bloc allows you to emit specific states, not just if your data is valid or not. Such as a loading state (represented as a subclass) to show that something is happening while a network call and or processing takes place. The state and the logic are seperated. There are 2 keys ways this can be implemented with Bloc.

  1. Using a Bloc: Events flow into the Bloc and States are emitted
  2. Using a Cubit: Method calls are made on the Cubit and States are emitted

A the likes of forms all you’ll need is a cubit. The value of Blocs will be investigated later.

RiverPod

RiverPod is supprising similar to Cubit in how your UI will interact with it. The key difference is that it the state and logic is placed outside of the widget tree altogether. Typically only one model (structure of state) is emitted. However, you can still create subclasses as need to represent different states.

Code Examples

To test these different options out I’ve started to create examples of each.

repo: hamishnorton/flutter_state_example

The value of Bloc?

So far I can’t really see any value in using Bloc, so why do people use it. Hmm, I need to find out. I’ll update you soon with what I discover.

So far I’ve spotted:

  • Bloc provides a stream for adding the incoming events, and sending out the states. (value to be investigated)
  • You can observe a Bloc’s transision. (value to be investigated)
  • emit or yeild in bloc

https://github.com/felangel/bloc/issues/1444

Bloc has an observer, but not one for Cubit RiverPod is like Cubit, and it has an observer for the state emitted