Flutter Tutorial

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

IdeaAnnotationMeaning
Observable@observableData that can change and be watched
Action@actionA method that changes observables
Computed@computedA value calculated from observables, cached and updated automatically
Reactionreaction, autorun, whenRun 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.

Practise in the playground Updated