tree: c64271514132f023f1e3dd9f65858ea144497a88 [path history] [tgz]
  1. lib/
  2. pubspec.yaml
  3. README.md
mobx/example/README.md

Examples

Annotations

Annotations drastically simplify the usage of MobX by tastefully tucking away all the boilerplate in the generated files (*.g.dart).

These examples use annotations, which is supported by the mobx_codegen package. To run the code-generator, we are using the following command:

$> cd $PATH_TO_MOBX_DART/mobx/example
$> flutter packages pub run build_runner build

Counter (counter.dart)

A really simple Counter. After all this is how you start off a Dart/Flutter project :-)

import 'package:mobx/mobx.dart';

part 'counter.g.dart';

class Counter = CounterBase with _$Counter;

abstract class CounterBase with Store {
  @observable
  int value = 0;

  @action
  void increment() {
    value++;
  }
}

Notice the @observable value property and the @action increment()that mutates it.

Todos (todos.dart)

This example showcases some of the core features of MobX such as Observable, Computed and Action. We have intentionally left out Reaction to keep it simple.

The Todos example is a classic way of showcasing a framework or a library. Here, you can see how MobX can simplify your code.

Todo

A Todo entity is at the heart of the Todos example. A bit of boilerplate is needed to make the code-generator do the rest for you.

import 'package:mobx/mobx.dart';

part 'example.g.dart';

class Todo = TodoBase with _$Todo;

abstract class TodoBase with Store {
  TodoBase(this.description);

  @observable
  String description = '';

  @observable
  bool done = false;
}

Observables

The TodoList manages the list of Todo. Notice the use of annotations to make the code more readable. MobX follows the princple:

What can be derived, should be derived. Automatically.

This is achieved with a small core-state (the @observable properties) and the derived-state (the @computed properties).

enum VisibilityFilter { all, pending, completed }

class TodoList = TodoListBase with _$TodoList;

abstract class TodoListBase with Store {
  @observable
  ObservableList<Todo> todos = ObservableList<Todo>();

  @observable
  VisibilityFilter filter = VisibilityFilter.all;

  @observable
  String currentDescription = '';

  @computed
  ObservableList<Todo> get pendingTodos =>
      ObservableList.of(todos.where((todo) => todo.done != true));

  @computed
  ObservableList<Todo> get completedTodos =>
      ObservableList.of(todos.where((todo) => todo.done == true));

  @computed
  bool get hasCompletedTodos => completedTodos.isNotEmpty;

  @computed
  bool get hasPendingTodos => pendingTodos.isNotEmpty;

  @computed
  String get itemsDescription =>
      '${pendingTodos.length} pending, ${completedTodos.length} completed';

  @computed
  ObservableList<Todo> get visibleTodos {
    switch (filter) {
      case VisibilityFilter.pending:
        return pendingTodos;
      case VisibilityFilter.completed:
        return completedTodos;
      default:
        return todos;
    }
  }

  // ...
}

Notice how you can have derived-state that depends on other derived-state. For example, itemsDescription depends on pendingTodos and completedTodos, which are also computed (aka derived) properties.

Actions

The operations that can be performed on the TodoList are marked with @action. These are simple functions, which can be invoked like normal dart functions! There is no extra ceremony to invoke an action.

class TodoList = TodoListBase with _$TodoList;

abstract class TodoListBase with Store {
  // ...

  @action
  void addTodo(String description) {
    final todo = Todo(description);
    todos.add(todo);
    currentDescription = '';
  }

  @action
  void removeTodo(Todo todo) {
    todos.removeWhere((x) => x == todo);
  }

  @action
  void changeDescription(String description) =>
      currentDescription = description;

  @action
  void changeFilter(VisibilityFilter filter) => this.filter = filter;

  @action
  void removeCompleted() {
    todos.removeWhere((todo) => todo.done);
  }

  @action
  void markAllAsCompleted() {
    for (final todo in todos) {
      todo.done = true;
    }
  }
}