Separate state from widget

setState in stateful widgets is a default way to handle state in Flutter. It's great because it's easy to understand and use, though it is not perfect.

setState scalability problems

The easiest solution will probably be separating the state fields out of the widget class. Sadly, setState will not work in such solution since it's not meant for such use case, but worry not! There're a lot of solutions to this problem, today we'll look at subjects.

note

There's a great post by fireship.io describing scalable state management solutions, read it here.

What are subjects

Subjects are classes that offers state update methods and immutable access to current state. Key idea of subjects is making it easy to listen to changes of given state part while maintaining synchronous access. Thanks to rxdart's BehaviorSubject such approach is easy to implement and maintain.

Subject usage

Simple toggle subject usage example:

class ExampleSubject {
// Behavior subject itself is always private to the subject class.
// BehaviourSubject.seeded(false) inits value of the subject to false.
final BehaviorSubject<bool> _flag = BehaviourSubject.seeded(false);
// Stream of current value and the value itself is exposed
// to outer world via getters:
Stream<bool> get flag$ => _flag.stream;
bool get flag => _flag.value;
void updateFlag(bool newFlag) => _flag.add(newFlag);
}
class ExampleWidget extends StatelessWidget {
final _subject = ExampleSubject();
@override
Widget build(BuildContext context) => StreamBuilder<bool>(
stream: _subject.flag$,
builder: (context, snapshot) => IconButton(
icon: Icon(snapshot.data ? Icons.enable : Icons.disable),
onPressed: () => _subject.updateFlag(!snapshot.data),
),
);
}
note

StreamBuilder widget run's declared builder: function on each new value that is coming from provided stream:.