Skip to content

Introduction

Adapted from internal documentation by Bridgecat

Welcome to dust. Please checkout the Quick Start Guide.

As you would probably know by now, dust handles three things at once:

  • State Management;
  • Persistence;
  • Synchronization (to be implemented).

We want to do this so that when we build new apps, we don't need to define our models multiple times using multiple libraries while keeping everything 1-1 mapped. Our earlier experience with such attempts were horrible, and it significantly hindered our development of the file management page and the preferences page.

Previously, we used:

The design of Qinhuai draws inspiration from MobX, Riverpod, Isar, as well as Freezed.

Reactive States

Note that this portion is rather a big-picture view of the reactivity of dust. For simplicity of use, many classes introduced here would be automatically generated by dust_generator. Reading through this part here would not likely be very applicable unless encountered with more advanced usecases. However, it would hopefully make you understand more about how dust works as a state management solution.

Ideally, when any value shown by a UI widget is changed, the widget should be rebuilt to reflect the latest state. In Qinhuai there are two closely-related interfaces: Observable<T> and Observer.

Observable<T>

Observable<T>: provides a get(Observer? o) function which retrieves its value and (optionally) registers an "observer" o.

  • The "observer" will be "notified" when there is a possible change to the underlying value.
  • For clarity, prefer calling the aliases watch(Observer o) or peek().

Concrete classes can implement Observable<T>. Here are a list of common implementations:

  • Active<T>: the simplest obserable thing. Its value can be changed using the set(T value) method. Data stored in Active<T> will be transient values, and they will not be persisted to local storage. This is ideal for temporary reactive values.
  • Atom<T>, Link<T>, BackLinks<T>: these are observables that store data that can and will automatically be persisted to the disk and synchronized.
  • Reactive<T>: computes its value from other Observable<T>s and caches it. When any of its dependencies is "notified", it will be "notified" as well. The value would be recomputed lazily, ad the next invocation of get.

Observer

Observer: provides a visit(List<VoidCallback> posts) function which allows it to be "notified".

  • It can choose to execute side effects inside the visit function, or (optionally) push "post-visit callbacks" into the posts list, having them executed after the graph traversal completes.
  • This visit is for implementation only. prefer calling notify().

Concrete classes can implement Observer:

  • Trigger<T>: watches an Observable<T> and triggers a callback each time it is notified.
  • Sometimes, the watched value may be notified without actually changing (e.g. when something is assigned the same value multiple times). The variant Comparer<T> would provide the previous value to the callback, allowing filtering of such events.

Widgets

There are two Flutter widgets that operate with the state management portion of dust:

ReactiveWidget: a widget that provides a "observer" o for use inside its builder parameter.

  • The widget will be flagged for rebuild when the "observer" is "notified".
  • Usage: inherit and override the build method.

ReactiveBuilder: a widget that provides a "observer" o for use inside its builder parameter.

  • The widget will be flagged for rebuild when the "observer" is "notified".
  • Usage: place it anywhere in a widget tree.

API Comparisons

The API of dust closely mimics that of Riverpod:

Data Node Widget Node Data B - Data A Widget B - Data A Update Data A Force Update Widget A
ChangeNotifier
(built-in)
ValueNotifier (immutable)
ChangeNotifier(mutable, manual)
AnimatedWidget (base)
AnimatedBuilder (builder)
A.addListener(() { /* Update B from A */ }) Include A in B's listenable/animation property (cannot be changed onece B is created) A.value = value (immutable)
A.notifyListeners() (mutable, manual)
(no special way to force update widgets)