• Stars
    star
    2,365
  • Rank 18,663 (Top 0.4 %)
  • Language
    Dart
  • License
    MIT License
  • Created over 5 years ago
  • Updated 7 days ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

MobX for the Dart language. Hassle-free, reactive state-management for your Dart and Flutter apps.

Language: English | Portuguรชs | Chinese

mobx.dart



pub package pub package pub package

Build Status Publish Coverage Status Netlify Status

Join the chat at https://discord.gg/dNHY52k

MobX for the Dart language.

Supercharge the state-management in your Dart apps with Transparent Functional Reactive Programming (TFRP)

We are looking for maintainers. Reach out on Discord or GitHub Issues!

Introduction

MobX is a state-management library that makes it simple to connect the reactive data of your application with the UI. This wiring is completely automatic and feels very natural. As the application-developer, you focus purely on what reactive-data needs to be consumed in the UI (and elsewhere) without worrying about keeping the two in sync.

It's not really magic but it does have some smarts around what is being consumed (observables) and where (reactions), and automatically tracks it for you. When the observables change, all reactions are re-run. What's interesting is that these reactions can be anything from a simple console log, a network call to re-rendering the UI.

MobX has been a very effective library for the JavaScript apps and this port to the Dart language aims to bring the same levels of productivity.

Sponsors

We are very thankful to our sponsors to make us part of their Open Source Software (OSS) program. [Become a sponsor]

Past Sponsors

Get Started

Follow along with the Getting Started guide on the MobX.dart Website.

Go deep

For a deeper coverage of MobX, do check out MobX Quick Start Guide. Although the book uses the JavaScript version of MobX, the concepts are 100% applicable to Dart and Flutter.

Core Concepts

MobX Triad

At the heart of MobX are three important concepts: Observables, Actions and Reactions.

Observables

Observables represent the reactive-state of your application. They can be simple scalars to complex object trees. By defining the state of the application as a tree of observables, you can expose a reactive-state-tree that the UI (or other observers in the app) consume.

A simple reactive-counter is represented by the following observable:

import 'package:mobx/mobx.dart';

final counter = Observable(0);

More complex observables, such as classes, can be created as well.

class Counter {
  Counter() {
    increment = Action(_increment);
  }

  final _value = Observable(0);
  int get value => _value.value;

  set value(int newValue) => _value.value = newValue;
  Action increment;

  void _increment() {
    _value.value++;
  }
}

On first sight, this does look like some boilerplate code which can quickly go out of hand! This is why we added mobx_codegen to the mix that allows you to replace the above code with the following:

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++;
  }
}

Note the use of annotations to mark the observable properties of the class. Yes, there is some header boilerplate here but its fixed for any class. As you build more complex classes this boilerplate will fade away and you will mostly focus on the code within the braces.

Note: Annotations are available via the mobx_codegen package.

Readonly

If you want to reduce your code you may want to swap @observable for @readonly. For every private variable it generates a public getter such that the client of your store can't change its value. Read more about it here

Computed Observables

What can be derived, should be derived. Automatically.

The state of your application consists of core-state and derived-state. The core-state is state inherent to the domain you are dealing with. For example, if you have a Contact entity, the firstName and lastName form the core-state of Contact. However, fullName is derived-state, obtained by combining firstName and lastName.

Such derived state, that depends on core-state or other derived-state is called a Computed Observable. It is automatically kept in sync when its underlying observables change.

State in MobX = Core-State + Derived-State

import 'package:mobx/mobx.dart';

part 'contact.g.dart';

class Contact = ContactBase with _$Contact;

abstract class ContactBase with Store {
  @observable
  String firstName;

  @observable
  String lastName;

  @computed
  String get fullName => '$firstName, $lastName';

}

In the example above fullName is automatically kept in sync if either firstName and/or lastName changes.

Actions

Actions are how you mutate the observables. Rather than mutating them directly, actions add a semantic meaning to the mutations. For example, instead of just doing value++, firing an increment() action carries more meaning. Besides, actions also batch up all the notifications and ensure the changes are notified only after they complete. Thus the observers are notified only upon the atomic completion of the action.

Note that actions can also be nested, in which case the notifications go out when the top-most action has completed.

final counter = Observable(0);

final increment = Action((){
  counter.value++;
});

When creating actions inside a class, you can take advantage of annotations!

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++;
  }
}

Asynchronous Actions

MobX.dart handles asynchronous actions automatically and does not require wrapping the code with runInAction.

@observable
String stuff = '';

@observable
loading = false;

@action
Future<void> loadStuff() async {
  loading = true; //This notifies observers
  stuff = await fetchStuff();
  loading = false; //This also notifies observers
}

Reactions

Reactions complete the MobX triad of observables, actions and reactions. They are the observers of the reactive-system and get notified whenever an observable they track is changed. Reactions come in few flavors as listed below. All of them return a ReactionDisposer, a function that can be called to dispose the reaction.

One striking feature of reactions is that they automatically track all the observables without any explicit wiring. The act of reading an observable within a reaction is enough to track it!

The code you write with MobX appears to be literally ceremony-free!

ReactionDisposer autorun(Function(Reaction) fn)

Runs the reaction immediately and also on any change in the observables used inside fn.

import 'package:mobx/mobx.dart';

final greeting = Observable('Hello World');

final dispose = autorun((_){
  print(greeting.value);
});

greeting.value = 'Hello MobX';

// Done with the autorun()
dispose();


// Prints:
// Hello World
// Hello MobX

ReactionDisposer reaction<T>(T Function(Reaction) predicate, void Function(T) effect)

Monitors the observables used inside the predicate() function and runs the effect() when the predicate returns a different value. Only the observables inside predicate() are tracked.

import 'package:mobx/mobx.dart';

final greeting = Observable('Hello World');

final dispose = reaction((_) => greeting.value, (msg) => print(msg));

greeting.value = 'Hello MobX'; // Cause a change

// Done with the reaction()
dispose();


// Prints:
// Hello MobX

ReactionDisposer when(bool Function(Reaction) predicate, void Function() effect)

Monitors the observables used inside predicate() and runs the effect() when it returns true. After the effect() is run, when automatically disposes itself. So you can think of when as a one-time reaction. You can also dispose when() pre-maturely.

import 'package:mobx/mobx.dart';

final greeting = Observable('Hello World');

final dispose = when((_) => greeting.value == 'Hello MobX', () => print('Someone greeted MobX'));

greeting.value = 'Hello MobX'; // Causes a change, runs effect and disposes


// Prints:
// Someone greeted MobX

Future<void> asyncWhen(bool Function(Reaction) predicate)

Similar to when but returns a Future, which is fulfilled when the predicate() returns true. This is a convenient way of waiting for the predicate() to turn true.

final completed = Observable(false);

void waitForCompletion() async {
  await asyncWhen(() => _completed.value == true);

  print('Completed');
}

Observer

One of the most visual reactions in the app is the UI. The Observer widget (which is part of the flutter_mobx package), provides a granular observer of the observables used in its builder function. Whenever these observables change, Observer rebuilds and renders.

Below is the Counter example in its entirety.

import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
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++;
  }
}

class CounterExample extends StatefulWidget {
  const CounterExample({Key key}) : super(key: key);

  @override
  _CounterExampleState createState() => _CounterExampleState();
}

class _CounterExampleState extends State<CounterExample> {
  final _counter = Counter();

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(
          title: const Text('Counter'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text(
                'You have pushed the button this many times:',
              ),
              Observer(
                  builder: (_) => Text(
                        '${_counter.value}',
                        style: const TextStyle(fontSize: 20),
                      )),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: _counter.increment,
          tooltip: 'Increment',
          child: const Icon(Icons.add),
        ),
      );
}

Contributing

If you have read up till here, then ๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰. There are couple of ways in which you can contribute to the growing community of MobX.dart.

  • Pick up any issue marked with "good first issue"
  • Propose any feature, enhancement
  • Report a bug
  • Fix a bug
  • Participate in a discussion and help in decision making
  • Write and improve some documentation. Documentation is super critical and its importance cannot be overstated!
  • Send in a Pull Request :-)
  • Chime in and Join the chat at https://discord.gg/dNHY52k

Contributors โœจ

All Contributors

Thanks goes to these wonderful people (emoji key):

Pavan Podila
Pavan Podila

๐Ÿ’ป ๐Ÿ“– ๐Ÿ‘€
katis
katis

๐Ÿ’ป ๐Ÿค” ๐Ÿ‘€ โš ๏ธ
Scott Hyndman
Scott Hyndman

๐Ÿ’ป ๐Ÿค” ๐Ÿ‘€ โš ๏ธ
Michael Bui
Michael Bui

๐Ÿ’ป ๐Ÿ“– ๐Ÿ‘€ ๐Ÿ’ก
Remi Rousselet
Remi Rousselet

๐Ÿ’ป ๐Ÿ“– ๐Ÿ‘€
adiaKhaitan
adiaKhaitan

๐Ÿ“–
Jacob Moura
Jacob Moura

๐Ÿ’ป ๐Ÿ“– ๐ŸŒ
Daniel Albuquerque
Daniel Albuquerque

๐ŸŒ
Marco Scannadinari
Marco Scannadinari

๐Ÿ“–
lsaudon
lsaudon

๐Ÿ’ป ๐Ÿ“–
Efthymis Sarmpanis
Efthymis Sarmpanis

๐Ÿ’ป
Giri Jeedigunta
Giri Jeedigunta

๐Ÿ“– ๐Ÿ’ก
Hebri Ramnath Nayak
Hebri Ramnath Nayak

๐Ÿ“–
Robert Brunhage
Robert Brunhage

๐Ÿ“–
Brady Trainor
Brady Trainor

๐Ÿ“–
Kushagra Saxena
Kushagra Saxena

๐Ÿ“– ๐Ÿ’ก
Pedro Massango
Pedro Massango

๐ŸŒ
Peter Czibik
Peter Czibik

๐Ÿ’ป
Luan Nico
Luan Nico

๐Ÿ“–
Kobi
Kobi

๐Ÿ’ป
Ryan
Ryan

๐Ÿ“–
Ivan Terekhin
Ivan Terekhin

๐Ÿ’ป
Yoav Rofรฉ
Yoav Rofรฉ

๐Ÿ“–
Mateusz Wojtczak
Mateusz Wojtczak

๐Ÿ“–
Timur Artikov
Timur Artikov

๐Ÿ’ป
Saurabh Sohoni
Saurabh Sohoni

๐Ÿ“–
renanzdm
renanzdm

๐Ÿ“–
Rachman Chavik
Rachman Chavik

๐Ÿ’ป
Nathan Cabasso
Nathan Cabasso

๐Ÿ› ๐Ÿ’ป
geisterfurz007
geisterfurz007

๐Ÿ“– ๐Ÿ–‹
Romuald Barbe
Romuald Barbe

๐Ÿ’ป
Alexander Mazuruk
Alexander Mazuruk

๐Ÿ’ก
Alberto Bonacina
Alberto Bonacina

๐Ÿ“–
Roland Ibragimov
Roland Ibragimov

๐Ÿ“–
zyzhao
zyzhao

๐Ÿ“–
Xinhai Wang
Xinhai Wang

๐Ÿ“–
Henry Mayer
Henry Mayer

๐Ÿ’ป โš ๏ธ
Sergey
Sergey

๐Ÿ’ป โš ๏ธ
Jason Rai
Jason Rai

๐Ÿ“–
Joshua de Guzman
Joshua de Guzman

๐Ÿ’ก
Jan Hertlein
Jan Hertlein

๐Ÿ“–
Evo Stamatov
Evo Stamatov

๐Ÿ’ป
Davi Eduardo
Davi Eduardo

๐Ÿ“–
Leonardo Custodio
Leonardo Custodio

๐Ÿ’ป ๐Ÿ“–
Prince Srivastava
Prince Srivastava

๐Ÿ’ก ๐Ÿ’ป
Muhammad Muhajir
Muhammad Muhajir

๐Ÿ“–
D
D

๐Ÿ“–
David Martos
David Martos

๐Ÿ’ป
Issa Nimaga
Issa Nimaga

๐Ÿ“–
Ascรชnio
Ascรชnio

๐Ÿ’ป ๐Ÿ“–
Alex Isaienko
Alex Isaienko

๐Ÿ’ป
Moritz Weber
Moritz Weber

๐Ÿ’ป
Carter Snook
Carter Snook

๐Ÿ“–
Brian Robles
Brian Robles

๐Ÿ’ป โš ๏ธ
harrypunk
harrypunk

๐Ÿ“–
Ikko Ashimine
Ikko Ashimine

๐Ÿ“–
amond
amond

๐Ÿ’ป โš ๏ธ ๐Ÿ‘€ ๐Ÿ“–
fzyzcjy
fzyzcjy

๐Ÿ’ป ๐Ÿ“– ๐Ÿ‘€
Vandad Nahavandipoor
Vandad Nahavandipoor

๐Ÿ“–
Sergey Molchanovsky
Sergey Molchanovsky

๐Ÿ’ป
ko16a46
ko16a46

๐Ÿ’ป
Yatharth Chauhan
Yatharth Chauhan

๐Ÿ“–
Parth Baraiya
Parth Baraiya

๐Ÿ’ป ๐Ÿ›
Altynbek Aidarbekov
Altynbek Aidarbekov

๐Ÿ’ป

This project follows the all-contributors specification. Contributions of any kind welcome!

More Repositories

1

mobx

Simple, scalable state management.
TypeScript
27,224
star
2

mobx-state-tree

Full-featured reactive state management without the boilerplate
TypeScript
6,867
star
3

mobx-react

React bindings for MobX
TypeScript
4,864
star
4

awesome-mobx

A collection of awesome things regarding MobX.
2,176
star
5

mobx-react-lite

Lightweight React bindings for MobX based on React 16.8 and Hooks
TypeScript
2,132
star
6

mobx-react-devtools

[DEPRECATED] Tools to perform runtime analyses of React applications powered by MobX and React
JavaScript
1,228
star
7

mobx-utils

Utility functions and common patterns for MobX
TypeScript
1,168
star
8

mobx-react-boilerplate

Small project to quickly start with React, MobX, JSX, ES6, Babel
JavaScript
895
star
9

serializr

Serialize and deserialize complex object graphs to and from JSON and Javascript classes
TypeScript
760
star
10

mst-gql

Bindings for mobx-state-tree and GraphQL
JavaScript
676
star
11

mobx-react-todomvc

TodoMVC reference implementation on top of react-mobx-boilerplate
JavaScript
501
star
12

mobx-angular

The MobX connector for Angular.
TypeScript
479
star
13

mobx-devtools

Mobx Devtools (React, Chrome Extension) - Looking for maintainers! https://github.com/mobxjs/mobx-devtools/issues/55
JavaScript
479
star
14

mobx-vue

๐Ÿ‰ Vue bindings for MobX
TypeScript
475
star
15

mobx-examples

A collection of simple mobx examples
HTML
285
star
16

create-react-app-mobx

DEPRECATED. Use https://github.com/arackaf/customize-cra
JavaScript
146
star
17

mobx-preact

MobX bindings for Preact
JavaScript
125
star
18

babel-plugin-mobx-deep-action

Reduces `action` and `runInAction` boilerplates
JavaScript
109
star
19

mobx-reactive2015-demo

Runnable source code of the #GoReactive #mobservable talk
JavaScript
98
star
20

mobx-contacts-list

React Amsterdam 2016 Demo Project
JavaScript
77
star
21

mobx-vue-lite

Lightweight Vue 3 bindings for MobX based on Composition API.
TypeScript
64
star
22

mobx-angularjs

MobX connector to AngularJS
TypeScript
51
star
23

mobx-react-docz

DEPRECATED Documentation site for MobX in React
TypeScript
42
star
24

zh.mobx.js.org

Mobxไธญๆ–‡ๆ–‡ๆกฃ
HTML
39
star
25

ko.mobx.js.org

Mobx korean document
HTML
11
star
26

mobxjs.github.io

Redir to mobx documentation
HTML
1
star
27

.github

1
star
28

mst-codemod-to-0.10

A codemod to migrate to MobX-State-Tree 0.10 from previous versions
TypeScript
1
star