Simplify UI interactions, asynchronous data fetches, transitions and animations. Build a whole app using purely stateless widgets.

Floop is a library that provides dynamic values to widgets, or in other words, values that will always display in the UI as what they are and not as what they were when the widget was built. The goal is to provide a library as simple to use as it is possible within the Dart language constraints.

Dynamic values pose a straightforward way to:

Address UI interactions

Address asynchronous operations (fetching data)

Affect any widget from anywhere

Perform animations and transitions

All of this can be achieved in two steps:

Add with Floop to the widget’s class definition: class MyWidget extends StatelessWidget with Floop .

to the widget’s class definition: . Read dynamic values from the floop Map: floop['myValue']

Map: Optional step: use transition(ms) within the build method to have a value transition from 0 to 1 in ms milliseconds.

By evolving from the most basic “clicker app”, to fetching an image and transitioning its opacity and position, this article will feature the basics of the library. Finally a refactoring is done to show an organized way to write code when using dynamic values.

Step 1: Clicker app

This is how the basic “clicker app” can be implemented using Floop:

Whenever there is a click on the button, floop['clicks'] increases and the widget will update to display the new value.

Step 2: Fetching an image

The click counter is changed for an image fetched from Lorem Picsum:

floop['image'] will be null whenever an image is being fetched. Once the asynchronous fetching is done, an image widget is created and stored. The change on floop['image'] value causes the widget to rebuild, displaying the new Image as a result.

Step 3: Adding an opacity transition

The new class TransitionImage is added and is used to wrap the Image widget:

Whenever a new image is loaded, it’s opacity will transition from 0 to 1 in 1.5 seconds, creating a fade in effect.

Step 4: Restarting a transition

A transition is added to the ImageDisplay widget to make the image move from top to center in 2 seconds. The transition starts with a delay of 0.8 seconds, so that it starts moving once the image can clearly be seen (because TransitionImage has an opacity transition):

The onPressed function awaits the asynchronous fetchAndUpateImage function to finish before restarting the transitions from the context. The transitions don’t replay if child widgets are rebuilt, they are essentially a read from a map like floop , where an internal key (associated with the context) is created for every transition. Transitions.restart(context: context) forces all the context transitions to start over.

A detail

Looks like the app is done, but there is one slight detail that has not been addressed. Every time the FloatingActionButton is clicked a new asynchronous process starts. When the button is pressed many times in a row, several asynchronous processes will finish in quick succession, shortly displaying some images before the last one is done fetching. One straightforward fix is to “lock” the fetch function:

Step 5: One last tap action

Finally TransitionImage widget is changed to update the image when it’s tapped:

The context transitions are restarted to create the fade in effect again. The reason why when tapping on the FloatingActionButton is not necessary to reset the transitions of the child context, is because floop['image'] is set to null first, causing a different type of widget to be used as child. The context that built the TransitionImage widget gets therefore disposed and a new context is created once the image finishes fetching. In this case the same context is used, so the transition is forcefully restarted.

Refactoring

Reading from floop (with no static checks) in the middle of nowhere can look a bit disorganized to some. The way I like to organize the code when using Floop, is to create a class with static ‘getters’ and ‘setters’ that read and write from/in floop .

And that’s it for this example, the full code can be found here. Check the Github repository for more information and examples.

How to use

Add floop: ^1.0.0 to the dependencies in the pubspec.yaml file and run flutter pub get in the root folder of your project.

Flutter web

Floop is also available for Flutter web as floop_web .

Performance

Including Floop on a widget adds an overhead to the build time that goes from x1 (no perceivable impact on large widgets) to x1.2 on minimal widgets (a Container wrapping a Text widget). I would say it’s negligible, far less impactful than wrapping a widget with another one.

Each value read from `floop` while building a widget can be considered like doing five reads (x5) from a standard LinkedHashMap.

Inspiration and thoughts

My favorite “state management” paradigm is no state management at all. For me it is intuitive and natural that if you define a UI component as Text(myTextVar), Text should display whatever value myTextVar has, I shouldn’t have to manually alert some internal mechanism to trigger an update. In fact, I believe it should be the other way around, I should have to be explicit about not wanting a value change to update the UI. This approach greatly simplifies the development phase, by allowing me to focus completely on the layout, without having to think later on how to propagate changes, or having to redesign widgets to cope with UI interactions. Dynamism can be incrementally incorporated without making structural modifications, all that’s required is to store whatever values might vary in the form of a dynamic value by reading from floop[myValue] .