Flutter Intermediate Lesson 53 of 135 2 min read
MobX in Flutter: Observables, Actions, Computed and Observer
Learn MobX for Flutter with a simple counter: @observable, @action, @computed, the Observer widget, reactions and code generation with build_runner.
On this page
MobX is a reactive state management library. You mark data as observable, change it in actions, and any Observer widget that reads it rebuilds automatically. It also has computed values that update themselves.
MobX normally uses code generation to remove boilerplate. The packages are not available in the online editor, so run these examples in your own project.
Install
flutter pub add mobx flutter_mobx
flutter pub add --dev build_runner mobx_codegen
The counter
lib/counter_store.dart:
import 'package:mobx/mobx.dart';
part 'counter_store.g.dart'; // generated file
class CounterStore = _CounterStore with _$CounterStore;
abstract class _CounterStore with Store {
@observable
int count = 0;
@computed
bool get isEven => count.isEven;
@action
void increment() => count++;
}
Generate the code:
dart run build_runner build # once
dart run build_runner watch # or keep it running while you work
lib/main.dart:
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'counter_store.dart';
final store = CounterStore();
void main() => runApp(const MaterialApp(home: CounterPage()));
class CounterPage extends StatelessWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('MobX')),
body: Center(
child: Observer(
builder: (_) => Text(
'${store.count} (${store.isEven ? 'even' : 'odd'})',
style: const TextStyle(fontSize: 40),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: store.increment,
child: const Icon(Icons.add),
),
);
}
}
The four ideas
| Idea | Annotation | Meaning |
|---|---|---|
| Observable | @observable | Data that can change and be watched |
| Action | @action | A method that changes observables |
| Computed | @computed | A value calculated from observables, cached and updated automatically |
| Reaction | reaction, autorun, when | Run side effects (logging, saving, navigation) when data changes |
Lists: ObservableList
abstract class _TodoStore with Store {
final todos = ObservableList<String>();
@computed
int get count => todos.length;
@action
void add(String title) => todos.add(title);
@action
void remove(int index) => todos.removeAt(index);
}
ObservableList, ObservableMap and ObservableSet notify observers when their contents change.
Reactions
final dispose = reaction(
(_) => store.count, // what to watch
(int value) => debugPrint('Count is now $value'), // what to do
);
// Later: dispose();
Without code generation
For small cases you can use MobX directly:
final count = Observable(0);
final increment = Action(() => count.value++);
Observer(builder: (_) => Text('${count.value}'));
// onPressed: increment
Pros and cons
- Pros: very little UI code; computed values are powerful; familiar to developers from React MobX.
- Cons: code generation step; the “magic” can hide where changes come from; smaller Flutter community than Riverpod or Bloc.
Try it yourself
Create a TodoStore with an ObservableList, a @computed count of finished tasks, and show both in Observer widgets.