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

Last update: Jun 22, 2022

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)

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]

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.

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';

String 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';

String 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';

String 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

πŸ’» πŸ“– πŸ‘€

katis

πŸ’» πŸ€” πŸ‘€ ⚠️

Scott Hyndman

πŸ’» πŸ€” πŸ‘€ ⚠️

Michael Bui

πŸ’» πŸ“– πŸ‘€ πŸ’‘

Remi Rousselet

πŸ’» πŸ“– πŸ‘€

adiaKhaitan

πŸ“–

Jacob Moura

πŸ’» πŸ“– 🌍

Daniel Albuquerque

🌍

Marco Scannadinari

πŸ“–

lsaudon

πŸ’» πŸ“–

Efthymis Sarmpanis

πŸ’»

Giri Jeedigunta

πŸ“– πŸ’‘

Hebri Ramnath Nayak

πŸ“–

Robert Brunhage

πŸ“–

Brady Trainor

πŸ“–

Kushagra Saxena

πŸ“– πŸ’‘

Pedro Massango

🌍

Peter Czibik

πŸ’»

Luan Nico

πŸ“–

Kobi

πŸ’»

Ryan

πŸ“–

Ivan Terekhin

πŸ’»

Yoav RofΓ©

πŸ“–

Mateusz Wojtczak

πŸ“–

Timur Artikov

πŸ’»

Saurabh Sohoni

πŸ“–

renanzdm

πŸ“–

Rachman Chavik

πŸ’»

Nathan Cabasso

πŸ› πŸ’»

geisterfurz007

πŸ“– πŸ–‹

Romuald Barbe

πŸ’»

Alexander Mazuruk

πŸ’‘

Alberto Bonacina

πŸ“–

Roland Ibragimov

πŸ“–

zyzhao

πŸ“–

Xinhai Wang

πŸ“–

Henry Mayer

πŸ’» ⚠️

Sergey

πŸ’» ⚠️

Jason Rai

πŸ“–

Joshua de Guzman

πŸ’‘

Jan Hertlein

πŸ“–

Evo Stamatov

πŸ’»

Davi Eduardo

πŸ“–

Leonardo Custodio

πŸ’» πŸ“–

Prince Srivastava

πŸ’‘ πŸ’»

Muhammad Muhajir

πŸ“–

D

πŸ“–

David Martos

πŸ’»

Issa Nimaga

πŸ“–

AscΓͺnio

πŸ’»

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

GitHub

https://github.com/mobxjs/mobx.dart
Comments
  • 1. Code generator design

    Since we've started working on the code generation, I thought I'd open a discussion thread about its design.

    We need at least three working annotations, @observable, @computed and @action.

    The generator will find all classes with a specific annotation (@observable, @store?) and creates a private subclass _$ParentClass that overrides the annotated fields, getters and methods with Mobx magic.

    I'll use parts of this annotated class in the next sections:

    @store // tbd
    class User {
      @observable
      String firstName = 'Jane';
    
      @observable
      String lastName = 'Doe';
    
      @computed
      String get fullName => '$firstName $lastName';
    
      @action
      void updateNames({String firstName, String lastName }) {
        ...
      }
    }
    

    @observable

    The first approach I tried was with a hidden Observable<T> field:

    class _$User {
      final _$firstName = Observable<String>(null, name: 'User.firstName');
    
      @override
      String get firstName => _$firstName.value;
    
      @override
      set firstName(String value) => _$firstName.value = value;
    }
    

    This had some downsides. One was that the User creates storage for the original firstName field which then ends up unused. Second is that the initialization becomes slightly more complicated. The initial value must be copied from the superclasses field, and the field must be nulled so that it doesn't keep the initial value in memory. Code would look something like this:

    class _$User {
     _$User() {
       _$firstName.value = super.firstName;
       super.firstName = null;
     }
    
     final _$firstName = Observable<String>(null, name: 'User.firstName');
    
      @override
      String get firstName => _$firstName.value;
    
      @override
      set firstName(String value) => _$firstName.value = value;
    }
    

    My second attempt was with an Atom which made the initialization a non-issue, since it just uses the original field:

    class _$User {
     final _$firstNameAtom = Atom(name: 'User.firstName');
    
      @override
      String get firstName {
         _$firstNameAtom.reportObserved();
        return super.firstName;
      }
    
      @override
      set firstName(String value) {
        super.firstName = value;
        _$firstNameAtom.reportChanged();
      }
    }
    

    I think the Atom method is better, for the reasons above.

    @computed

    Computed should be simple, just override a getter and use the original one:

    class _$User {
      _$User() {
        _$fullNameComputed = Computed(() => super.fullName);
      }
    
     Computed<String> _$fullNameComputed;
    
      @override
      String get fullName => _$fullNameComputed.value;
    }
    

    @action

    The @action annotation has a name conflict with the current action() builder. We could remove action() and replace it with multiple action variants for different function arities: action0<R>(() => ...), action1<A, R>((a: A) => ...), action2<A, B, R>((a: A, b: B) => ...). The names are ugly, but they could be used by the codegen and I think that once the code generator is done, people won't use the raw versions much.

    class _$User {
      _$User() {
        _$updateNames = action2((String a, String b) => super.updateNames(firstName: a, lastName: b));
      }
    
      Action2<String, String ,void> _$updateNames;
    
      @override
      void updateNames({String firstName, String lastName}) {
        _$updateNames(firstName, lastName);
      }
    }
    

    Named vs. positional arguments will add complexity in the generator.

    Automatic conversions

    Mobx JS makes objects and arrays automatically observable when you use the @observable decorator. You can opt-out by using @observable.ref decorator instead.

    Dart can't support the feature for classes, but it would be possible (sort of) for collections like Maps and Lists.

    I'm not a fan of it as a default, and using an ObservableList is not much harder than using a List.

    class ShoppingCart {
      @observable
      List<Item> items = [];
    }
    

    vs.

    class ShoppingCart {
      @observable
      List<Item> items = ObservableList();
    }
    

    Since we can't modify the actual list assigned to the variable like in JS but only wrap it, it might be confusing that the non-observable List you passed to ShoppingCart.items is suddenly a reactive list when accessed from items.

    final items = [];
    
    final cart = ShoppingCart();
    cart.items = items; // Supposedly items and cart.items are the same list
    
    cart.items.add(Item()); // Mobx reacts to the change :)
    
    items.add(Item()); // Mobx misses the change :(
    

    Constructors

    The generator should be able to create a constructor that matches the superclass factory that delegates to the subclass:

    class User {
      // 
      User._(this.firstName, this.lastName);
    
      // Dart can delegate to the subclass constructor with this syntax
      factory User({String firstName, String lastName}) => _$User; // This could also determine the name of the subclass?
    }
    
    class _$User {
      _$User({String firstName, String lastName})
        : super._(firstName, lastName}) {
      }
    }
    

    observe() support in annotated classes

    We would want the generated classes to be supported in devtools etc, but the problem is that the Atoms, Computeds etc are hidden in the subclass. It would be ugly to expose them as public, and you'd have to cast the User to _$User.

    Another way would be to define an interface like:

    abstract class Store {
      void observe(String field, listener);
    }
    

    make the annotated class abstract, and implement Store:

    abstract class User implements Store {
    }
    

    Then the code generation could implement Store automatically:

    class _$User extends User {
      void observe(String field, listener) {
        if (field == 'firstName') { ... }
        // etc.
      }
    }
    

    Store interface could also expose a Map of observable fields, so devtools could iterate and analyze them.

    Reviewed by katis at 2019-01-19 08:02
  • 2. Migrate MobX for NNBD / null safety

    MobX will need to be migrated to NNBD. Most packages have started releasing pre-release -nullsafety versions now that it's in beta support.

    https://medium.com/dartlang/announcing-dart-null-safety-beta-87610fee6730

    Reviewed by acoutts at 2020-11-24 02:32
  • 3. Flutter API

    Last night I thought I'd try to use Mobx with Flutter and created a simple widget based on the design of mobx-react and Flutter's ValueListenableBuilder:

    import 'package:flutter/widgets.dart';
    import 'package:mobx/mobx.dart';
    
    void noOp() {}
    
    class Observer extends StatefulWidget {
      const Observer({Key key, @required this.builder}) : super(key: key);
    
      final Widget Function(BuildContext) builder;
    
      @override
      State<StatefulWidget> createState() => _ObserverState();
    }
    
    class _ObserverState extends State<Observer> {
      bool _isBuildPending = false;
    
      Reaction _buildReaction;
    
      @override
      void initState() {
        super.initState();
    
        _buildReaction = Reaction(mobxContext, () {
          if (_isBuildPending) {
            return;
          }
    
          _isBuildPending = true;
          if (!mounted) {
            return;
          }
    
          setState(noOp);
        });
      }
    
      @override
      Widget build(BuildContext context) {
        _isBuildPending = false;
        Widget result;
        _buildReaction.track(() {
          result = widget.builder(context);
        });
        return result;
      }
    }
    

    usage:

    class Greeter extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => _GreeterState();
    }
    
    class _GreeterState extends State<Greeter> {
      final name = observable('Jane');
    
      @override
      Widget build(BuildContext context) => Observer(builder: (context) {
        return Text('Hello, ${name.value}');
      });
    }
    

    It does seem to work and could be a starting point for the flutter_mobx API. Probably the life cycle of widgets will bring all kinds of problems like in mobx-react.

    Reviewed by katis at 2019-01-06 08:54
  • 4. [Request] More manageable Form at scale

    I'm going with MobX on my new project which involves a lot of Forms (hundreds) and Fields (10 to 50)

    At core MobX solve a lot of concerns but it's not easy to copy the Form example at scale when you have a lot of fields.

    There are two stores to handle, values and errors also you should not forget to init disposers.

    There's an ObservableList that handle specific modifications and react accordingly. I'd like to have an ObservableFormField that would handle Fields.

    So much handwritten code is really error prone when working on real business forms entries

    Thank you for your feedback !

    I'm not sure how to architect all this but I'll be happy to find logic per field at one place only.

    @observable
    ObservableFormField<String> name = ObservableFormField<String>(
       name:'firstname',
       validator : ...
       error : errorBuilder<String>(..) => ...
    )
    
    @action
    setName(String value) => name.value = value
    
    Reviewed by Solido at 2019-08-21 11:20
  • 5. References to classes generated by other build-generated types are resolved to dynamic (Moor, SuperEnum, BuiltValue)

    mobx_codegen resolves a moor generated class to dynamic. I suspect this is a limitation in the build framework, and how the analyzer is set up to resolve against generated code.

    image

    image

    Versions:

    flutter_mobx 0.3.6
    mobx 0.4.0+1
    mobx_codegen 0.4.0+1
    build_runner 1.7.3
    

    Originally posted by @irvine5k in https://github.com/mobxjs/mobx.dart/issues/381#issuecomment-577308858

    Reviewed by shyndman at 2020-01-22 20:54
  • 6. observable list isn't a valid override of

    After update the mobx version I'm having issue with all of my Stores that have a observable list. Lets say that I have the following code:

    class EventsStore = _EventsStore with _$EventsStore;
    
    abstract class _EventsStore with Store {
    
      final EventsService eventsService;
    
      _EventsStore(this.eventsService) : assert(eventsService != null);
    
    //...
    
      @observable
      List<Event> _events = <Event>[];
    
    //...
    }
    

    I'm receiving a error saying:

    error: '_$EventsStore._events' ('dynamic Function()') isn't a valid override of '_EventsStore._events' ('List<Event> Function()').

    If I remve the _events property and run flutter run build_runner again the error disappear and it goes back to normal

    Reviewed by pedromassango at 2020-01-02 11:02
  • 7. Usage example with Firebase FireStore

    The most common usecase for most of the developers will be using state management with a cloud database source.. like Firebase FireStore.

    Any example of implementing such CRUD actions will be great. I am willing to use MobX for the same usecase.

    Reviewed by Purus at 2019-02-15 18:55
  • 8. Add generation of toString method when @tostring annotation is used

    Be careful: pubscpec.yaml contain dependency_overrides in order to expose @tostring annotation from new mobx version !

    Remove @store annotation from mobx lib and test Add @tostring annotation in mobx lib and test Add @tostring annotation recognition in mobx_codegen Add toString method generation on computed and observables when @tostring is used Add test on toString method generation

    Feature request: mobx_codegen: allow generating toString method for Store class mobxjs#174 Previous pull request closed by error: mobxjs#391

    Sorry for keeping all my local history details :(

    Reviewed by hawkbee1 at 2020-01-28 10:24
  • 9. dart:ui types not handled by mobx_codegen properly on Flutter stable/beta

    Some people in the community are reporting to me that the Color class when placed in Observable is generating Dynamic.

      @observable
      Color colorPrimary = null;
    

    And generated it:

      @override
      dynamic get colorPrimary {
        _$colorPrimaryAtom.context.enforceReadPolicy(_$colorPrimaryAtom);
        _$colorPrimaryAtom.reportObserved();
        return super.colorPrimary;
      }
    

    The Color class is blocked somehow and we are unable to solve it, has anyone had this problem?

    Reviewed by Bwolfs2 at 2020-01-08 00:19
  • 10. Simplify Store creation with the @store annotation

    Based on the discussion in #217, we can simplify the Store creation with the use of an annotation: @store.

    The ideal way given the code-gen constraints is to have something like this:

    part 'counter.g.dart'; // This file will contain the _$Counter mixin
    
    @store
    class Counter with _$Counter {
      @observable
      int value = 0;
    
      @action
      void increment() {
        value++;
      }
    
    }
    

    The generated code will be as before with some slight changes on the mixin declaration:

    // GENERATED CODE - DO NOT MODIFY BY HAND
    
    part of 'counter.dart';
    
    // **************************************************************************
    // StoreGenerator
    // **************************************************************************
    
    // ignore_for_file: non_constant_identifier_names, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars
    
    mixin _$Counter on Counter {
      final _$valueAtom = Atom(name: '_Counter.value');
    
      @override
      int get value {
        _$valueAtom.context.enforceReadPolicy(_$valueAtom);
        _$valueAtom.reportObserved();
        return super.value;
      }
    
      @override
      set value(int value) {
        // Since we are conditionally wrapping within an Action, there is no need to enforceWritePolicy
        _$valueAtom.context.conditionallyRunInAction(() {
          super.value = value;
          _$valueAtom.reportChanged();
        }, _$valueAtom, name: '${_$valueAtom.name}_set');
      }
    
      final _$_CounterActionController = ActionController(name: '_Counter');
    
      @override
      void increment() {
        final _$actionInfo = _$_CounterActionController.startAction();
        try {
          return super.increment();
        } finally {
          _$_CounterActionController.endAction(_$actionInfo);
        }
      }
    }
    
    

    @rrousselGit, can you confirm this is doable?

    Reviewed by pavanpodila at 2019-07-12 06:52
  • 11. no outputs on build_runner build

    Hello, I can't seem to get build_runner to generate anything.

    Running

    flutter packages pub run build_runner clean && flutter packages pub run build_runner build --delete-conflicting-outputs
    

    outputs the following:

    [INFO] Generating build script...
    [INFO] Generating build script completed, took 454ms
    
    [INFO] Creating build script snapshot......
    [INFO] Creating build script snapshot... completed, took 18.6s
    
    [INFO] Initializing inputs
    [INFO] Building new asset graph...
    [INFO] Building new asset graph completed, took 1.0s
    
    [INFO] Checking for unexpected pre-existing outputs....
    [INFO] Checking for unexpected pre-existing outputs. completed, took 1ms
    
    [INFO] Running build...
    [INFO] 1.1s elapsed, 0/16 actions completed.
    [INFO] 2.1s elapsed, 0/16 actions completed.
    [INFO] 3.3s elapsed, 0/16 actions completed.
    [INFO] 4.4s elapsed, 0/16 actions completed.
    [INFO] 5.5s elapsed, 0/16 actions completed.
    [INFO] 6.5s elapsed, 0/16 actions completed.
    [INFO] 7.5s elapsed, 0/16 actions completed.
    [INFO] 8.7s elapsed, 0/16 actions completed.
    [INFO] 19.6s elapsed, 0/16 actions completed.
    [WARNING] No actions completed for 19.6s, waiting on:
      - json_serializable:json_serializable on test/widget_test.dart
      - json_serializable:json_serializable on lib/view_models/event_selected_view_model.dart
      - json_serializable:json_serializable on lib/repositories/UserRepository.dart
      - json_serializable:json_serializable on lib/models/Organization.dart
      - json_serializable:json_serializable on lib/models/User.dart
      .. and 11 more
    
    [INFO] 21.0s elapsed, 2/18 actions completed.
    [INFO] 22.0s elapsed, 12/27 actions completed.
    [INFO] 23.4s elapsed, 18/33 actions completed.
    [INFO] 24.5s elapsed, 60/76 actions completed.
    [WARNING] mobx_codegen:mobx_generator on lib/mobx/Authentication.dart:
    Missing "part 'Authentication.g.dart';".
    [INFO] 26.2s elapsed, 134/141 actions completed.
    [INFO] Running build completed, took 26.4s
    
    [INFO] Caching finalized dependency graph...
    [INFO] Caching finalized dependency graph completed, took 110ms
    
    [INFO] Succeeded after 26.5s with 14 outputs (148 actions)
    

    The only outputs are from json_serializable.

    My store file looks like this:

    import 'package:mobx/mobx.dart';
    part "authentication.g.dart";
    
    class Authentication = AuthenticationBase with _$Authentication;
    
    abstract class AuthenticationBase with Store {
    
    }
    

    I saw

    Missing "part 'Authentication.g.dart';".

    So I removed that line + the class line:

    import 'package:mobx/mobx.dart';
    
    abstract class AuthenticationBase with Store {
    
    }
    

    I then get this:

    [INFO] Generating build script...
    [INFO] Generating build script completed, took 586ms
    
    [INFO] Creating build script snapshot......
    [INFO] Creating build script snapshot... completed, took 22.5s
    
    [INFO] Initializing inputs
    [INFO] Building new asset graph...
    [INFO] Building new asset graph completed, took 1.7s
    
    [INFO] Checking for unexpected pre-existing outputs....
    [INFO] Checking for unexpected pre-existing outputs. completed, took 2ms
    
    [INFO] Running build...
    [INFO] 1.1s elapsed, 0/16 actions completed.
    [INFO] 2.2s elapsed, 0/16 actions completed.
    [INFO] 3.2s elapsed, 0/16 actions completed.
    [INFO] 4.3s elapsed, 0/16 actions completed.
    [INFO] 5.5s elapsed, 0/16 actions completed.
    [INFO] 6.6s elapsed, 0/16 actions completed.
    [INFO] 7.6s elapsed, 0/16 actions completed.
    [INFO] 8.9s elapsed, 0/16 actions completed.
    [INFO] 9.9s elapsed, 0/16 actions completed.
    [INFO] 20.3s elapsed, 1/16 actions completed.
    [INFO] 21.4s elapsed, 1/17 actions completed.
    [INFO] 23.3s elapsed, 17/29 actions completed.
    [INFO] 24.7s elapsed, 48/63 actions completed.
    [INFO] 25.7s elapsed, 116/123 actions completed.
    [INFO] Running build completed, took 26.1s
    
    [INFO] Caching finalized dependency graph...
    [INFO] Caching finalized dependency graph completed, took 106ms
    
    [INFO] Succeeded after 26.2s with 14 outputs (148 actions)
    

    Still without generated files.

    Pubspec.yaml:

    name: buggerino_flutter
    description: A new Flutter project.
    
    # The following defines the version and build number for your application.
    # A version number is three numbers separated by dots, like 1.2.43
    # followed by an optional build number separated by a +.
    # Both the version and the builder number may be overridden in flutter
    # build by specifying --build-name and --build-number, respectively.
    # In Android, build-name is used as versionName while build-number used as versionCode.
    # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
    # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
    # Read more about iOS versioning at
    # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
    version: 1.0.0+1
    
    environment:
      sdk: ">=2.2.2 <3.0.0"
    
    dependencies:
      flutter:
        sdk: flutter
    
      # The following adds the Cupertino Icons font to your application.
      # Use with the CupertinoIcons class for iOS style icons.
      cupertino_icons: ^0.1.2
      flutter_keychain: ^1.0.0
      http: ^0.12.0+2
      bloc: ^2.0.0
      flutter_bloc: ^2.0.0
      equatable: ^0.6.1
      json_annotation: ^3.0.0
      mobx: ^0.4.0+1
      flutter_mobx: ^0.3.6
    
    dev_dependencies:
      flutter_test:
        sdk: flutter
      build_runner: ^1.7.2
      json_serializable: ^3.2.0
      mobx_codegen: ^0.4.0+1
    
    
    # For information on the generic Dart part of this file, see the
    # following page: https://dart.dev/tools/pub/pubspec
    
    # The following section is specific to Flutter.
    flutter:
    
      # The following line ensures that the Material Icons font is
      # included with your application, so that you can use the icons in
      # the material Icons class.
      uses-material-design: true
    
      # To add assets to your application, add an assets section, like this:
      # assets:
      #  - images/a_dot_burr.jpeg
      #  - images/a_dot_ham.jpeg
    
      # An image asset can refer to one or more resolution-specific "variants", see
      # https://flutter.dev/assets-and-images/#resolution-aware.
    
      # For details regarding adding assets from package dependencies, see
      # https://flutter.dev/assets-and-images/#from-packages
    
      # To add custom fonts to your application, add a fonts section here,
      # in this "flutter" section. Each entry in this list should have a
      # "family" key with the font family name, and a "fonts" key with a
      # list giving the asset and other descriptors for the font. For
      # example:
      # fonts:
      #   - family: Schyler
      #     fonts:
      #       - asset: fonts/Schyler-Regular.ttf
      #       - asset: fonts/Schyler-Italic.ttf
      #         style: italic
      #   - family: Trajan Pro
      #     fonts:
      #       - asset: fonts/TrajanPro.ttf
      #       - asset: fonts/TrajanPro_Bold.ttf
      #         weight: 700
      #
      # For details regarding fonts from package dependencies,
      # see https://flutter.dev/custom-fonts/#from-packages
    
    Reviewed by jordyvandomselaar at 2020-01-05 21:33
  • 12. [BUG] Reaction won't trigger on an Object (With == Operator & hash code) with observable fields

    Problem: When i write a reaction on a complex data class with (==) operator and Hash-code, the reaction is not getting triggered.

    After some digging with equals function in reaction I found that the object is updating itself even before comparing (like the fields of the previous Value are == current value). This seems wrong (or maybe i did messed up something). Please Help me!.


    • This is My Data Class:
    
    import 'package:json_annotation/json_annotation.dart';
    import 'package:mobx/mobx.dart';
    
    import 'enum/tnc_type.dart';
    
    part 'invoice_tnc_model.g.dart';
    
    @JsonSerializable()
    class InvoiceTnc extends _InvoiceTncBase with _$InvoiceTnc {
      InvoiceTnc({
        super.categoryId,
        super.id,
        super.isSelected,
        super.organisationId,
        super.termAndCondition,
        super.tncType,
      });
    
      factory InvoiceTnc.fromJson(Map<String, dynamic> json) =>
          _$InvoiceTncFromJson(json);
      Map<String, dynamic> toJson() => _$InvoiceTncToJson(this);
    }
    
    abstract class _InvoiceTncBase with Store {
      @observable
      String? categoryId;
      @observable
      String? id;
      @observable
      bool? isSelected;
      @observable
      String? organisationId;
      @observable
      String? termAndCondition;
      @observable
      @JsonKey(
        fromJson: TncType.fromJson,
        toJson: TncType.toJson,
      )
      TncType? tncType;
      _InvoiceTncBase({
        this.categoryId,
        this.id,
        this.isSelected,
        this.organisationId,
        this.termAndCondition,
        this.tncType,
      });
    
      @override
      String toString() {
        return '_InvoiceTncBase(categoryId: $categoryId, id: $id, isSelected: $isSelected, organisationId: $organisationId, termAndCondition: $termAndCondition, tncType: $tncType)';
      }
      @override
      bool operator ==(Object other) {
        if (identical(this, other)) return true;
    
        return other is _InvoiceTncBase &&
            other.categoryId == categoryId &&
            other.id == id &&
            other.isSelected == isSelected &&
            other.organisationId == organisationId &&
            other.termAndCondition == termAndCondition &&
            other.tncType == tncType;
      }
    
      @override
      int get hashCode {
        return categoryId.hashCode ^
            id.hashCode ^
            isSelected.hashCode ^
            organisationId.hashCode ^
            termAndCondition.hashCode ^
            tncType.hashCode;
      }
    }
    
    • This is my reaction
          reaction<Invoice>(
            (_) => invoiceStore.currentInvoiceTnc,
            (_) {
              currentInvoice.currentInvoiceTnc.termAndCondition = "Hello";
              currentInvoice.currentInvoiceTnc.id = "Hi;
            },
          ),
    

    This is my action

      @action
      Future<void> submit() async {
        invoiceStore.currentInvoiceTnc
        ..categoryId = "ds"
        ..id ="sd"
        ..isSelected =true
        ..organisationId = "HI";
      }
    

    Ive edited the fields so that it is easy and short.

    Thanks


    Another Question:

    • The one instant solution i could think of is to make the data class vars final and assign new var with copy-with function every time

    This is the Dilemma that i have been facing. should i declare my data classes as final vars without stores or use stores with data class. ( Cause we don't exactly adding any new performance weight by declaring them as stores ) .

    Reviewed by DattatreyaReddy at 2022-06-24 03:49
  • 13. Observer not reacting to changes

    I'm invoking an action in form onchange event, and the action is doing ok. But the observer is not triggering the build method. Anyone have experienced this issue?

    code in the widgets tree:

    Form(
                    key: _formKey,
                    onChanged: () {
                      var ok = _formKey.currentState!.validate();
                      _vm.setFormOK(ok);
                    },
    
    ....
    
    Observer(
                            builder: (_) => MxButton(_vm.formOK.toString(),
                                _vm.formOK == true ? _vm.logar : null))
    
    

    code in Store class:

      @observable
      bool formOK = false;
      @action
      void setFormOK(bool value) => formOK = value;
    
    

    code in .g file (code behind)

    @override
      bool get formOK {
        _$formOKAtom.reportRead();
        return super.formOK;
      }
    
      @override
      set formOK(bool value) {
        _$formOKAtom.reportWrite(value, super.formOK, () {
          super.formOK = value;
        });
      }
    
      late final _$_LoginPageVMActionController =
          ActionController(name: '_LoginPageVM', context: context);
    
      @override
      void setFormOK(bool value) {
        final _$actionInfo = _$_LoginPageVMActionController.startAction(
            name: '_LoginPageVM.setFormOK');
        try {
          return super.setFormOK(value);
        } finally {
          _$_LoginPageVMActionController.endAction(_$actionInfo);
        }
      }
    

    login_page.dart.txt login_page_vm.dart.txt login_page_vm.g.dart.txt

    Reviewed by ejdoming at 2022-06-21 18:21
  • 14. ci: if the test fails, github action should fail.

    Describe the changes proposed in this Pull Request.

    If the test fails, github action should fail. https://github.com/mobxjs/mobx.dart/pull/784 has failed tests, but check succeeded.


    Pull Request Checklist

    • [x] If the changes are being made to code, ensure the version in pubspec.yaml is updated.
    • [x] Increment the major/minor/patch/patch-count, depending on the complexity of change
    • [x] Add the necessary unit tests to ensure the coverage does not drop
    • [x] Update the Changelog to include all changes made in this PR
    • [x] Run the set:versions command using npm or yarn. You can find this command in the package.json file in the root directory
    • [x] Include the necessary reviewers for the PR
    • [x] Update the docs if there are any API changes or additions to functionality
    Reviewed by amondnet at 2022-06-20 02:32
  • 15. async action behaves differently from 2.0.7+3.

    1. example
      @observable
      String stuff = '';
      
      @observable
      loading = false;
      
      @action
      Future<void> loadStuff() async {
        loading = true;
        stuff = await fetchStuff();
        loading = false;
      }
      
    2. test
      test('run allows updating observable values in an async function',
              () async {
            final action = AsyncAction('testAction');
      
            final counter = Observable<int>(0);
      
            final values = <int>[];
            autorun((_) {
              values.add(counter.value);
            });
      
            await action.run(() async {
              await sleep(10);
              counter
                ..value = 1
                ..value = 2;
              await sleep(10);
              counter.value = 3;
            });
      
            expect(counter.value, equals(3));
            expect(values, equals([0, 2, 3]));
         });
      

    https://github.com/mobxjs/mobx.dart/pull/89

    mobx: <= 2.0.7+2

    1. example
      Future<void> loadStuff() async {
        runInAction(() {
          loading = true;
        })
        final _stuff = await fetchStuff();
        runInAction(() {
          stuff = _stuff;
          loading = false;
        });
      }
      
    2. test values : [0, 2, 3]

    https://github.com/mobxjs/mobx.dart/pull/784

    mobx: >= 2.0.7+3

    1. example
      Future<void> loadStuff() async {
        runInAction(() {
          loading = true;
          final _stuff = await fetchStuff();
          stuff = _stuff;
          loading = false;
        });
      }
      
    2. test values : [0, 3]

    https://github.com/mobxjs/mobx.dart/runs/6912223161?check_suite_focus=true

    https://mobx.netlify.app/api/action/

    Changes to the observables are only notified at the end of the action. This ensures all mutations happen as an atomic unit. This also eliminates noisy notifications, especially if you are changing a lot of observables (say, in a loop).

    https://mobx.js.org/actions.html#updating-state-using-actions

    They are run inside transactions. No reactions will be run until the outer-most action has finished, guaranteeing that intermediate or incomplete values produced during an action are not visible to the rest of the application until the action has completed.

    https://mobx.js.org/actions.html#asynchronous-actions

    According to the docs, the 2.0.7+3 seems to be working correctly.

    Reviewed by amondnet at 2022-06-16 05:10
  • 16. test: action should wrap in transaction

    Signed-off-by: Minsu Lee [email protected]

    https://github.com/mobxjs/mobx/blob/63698d0681988194bac5fc01851882b417b35f18/packages/mobx/tests/v5/base/action.js#L4


    Pull Request Checklist

    • [x] If the changes are being made to code, ensure the version in pubspec.yaml is updated.
    • [x] Increment the major/minor/patch/patch-count, depending on the complexity of change
    • [x] Add the necessary unit tests to ensure the coverage does not drop
    • [x] Update the Changelog to include all changes made in this PR
    • [x] Run the set:versions command using npm or yarn. You can find this command in the package.json file in the root directory
    • [x] Include the necessary reviewers for the PR
    • [x] Update the docs if there are any API changes or additions to functionality
    Reviewed by amondnet at 2022-06-16 04:21
  • 17. Rename asObservable() to obs() (primitive types only)

    *Fixes 2.0.7

    I want to push a little fix for my 2.0.7 update. It was very recent, and this fix won't touch many users. I found in practice that 1.asObservable() is ugly and has no big difference to ObservableInt(1) So I changed it to .obs() It is short and convenient for GetX users (which use .obs getter) to jump to MobX.

    final x = 1.obs();
    
    Reviewed by subzero911 at 2022-06-13 07:47

Related

A flutter boilerplate project with GetX state management.
A flutter boilerplate project with GetX state management.

flutter_getx_boilerplate Languages: English (this file), Chinese. Introduction During my study of Flutter, I have been using the flutter_bloc state ma

Jun 30, 2022
Flutter MVU architecture/state management package

mvu_flutter No mutability. No builders. No connectors. No reducers. No StreamControllers and subscription management. A truly declarative state manage

Jul 29, 2021
Simple global state management for Flutter

Slices Slices is a minimalist state manegement, focused specifically for applications that needs a global state where different "pieces" of the applic

Jun 15, 2021
The modular state management solution for flutter.
The modular state management solution for flutter.

The modular state management solution for flutter. Easy debugging : each event is predictable and goes into a single pipeline Centralized state : soli

Jan 5, 2022
Flutter State Management with provider :rocket:
Flutter State Management with provider :rocket:

Flutter - Gerenciamento de Estados com Provider Objetivos ao completar os estudos Aprenda a gerenciar o estado da sua aplicação com Single Source of T

Dec 6, 2021
A predictable state management library that helps implement the BLoC design pattern
A predictable state management library that helps implement the BLoC design pattern

A predictable state management library that helps implement the BLoC design pattern. Package Pub bloc bloc_test flutter_bloc angular_bloc hydrated_blo

Jun 27, 2022
Another state management solution

VxState VxState is a state management library built for Flutter apps with focus on simplicity. It is inspired by StoreKeeper & libraries like Redux, V

Jun 20, 2022
London App Brewery State Management Project

todey_flutter A new Flutter application. Getting Started This project is a starting point for a Flutter application. A few resources to get you starte

Nov 1, 2021
Manage the state of your widgets using imperative programming concepts.

Imperative Flutter Manage the state of your widgets using imperative programming concepts. Setup Intall imperative_flutter package in pubspec.yaml dep

Jan 9, 2022
A simple way to access state while robust and testable.
A simple way to access state while robust and testable.

A state-management library that: catches programming errors at compile time rather than at runtime removes nesting for listening/combining objects ens

Jun 29, 2022
Example of use bloc + freezed with a state that contains a list

blocfreezedlistexample A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you s

Mar 21, 2022
Timer based on provider state manager

timer_provider Timer based on provider state manager Getting Started This project is a starting point for a Flutter application. A few resources to ge

Nov 6, 2021
Trip management mobile Application
Trip management mobile Application

HereYouGO Trip management mobile Application This app will help you Track your expense during your trips. Track your trip destinations and the sub tri

Apr 12, 2022
Functional programming in Dart and Flutter. All the main functional programming types and patterns fully documented, tested, and with examples.

Fpdart Functional programming in Dart and Flutter. All the main functional programming types and patterns fully documented, tested, and with examples.

Jun 24, 2022
Megaflixz is a movie app for the movie lover in you. Includes all new and upcoming movies and TV series. Also has an option to save movies to watch later list
Megaflixz is a movie app for the movie lover in you. Includes all new and upcoming movies and TV series. Also has an option to save movies to watch later list

MegaflixZ MegaflixZ is a movie app for the movie lover in you. It is an app that displays new and upcoming movies and tv series. Team members Deepak M

Aug 23, 2021
Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get.
Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get.

Languages: English (this file), Indonesian, Urdu, Chinese, Brazilian Portuguese, Spanish, Russian, Polish, Korean, French. About Get Installing Counte

Jul 2, 2022
App to Watch Animes And Read Manga.
App to Watch Animes And Read Manga.

App To Watch Anime And Read Manga NOTE: Manga Section Contains 18+ content. Sometimes App may take some time to load. Constants Folder is removed from

Sep 12, 2021
A powerful state machine for MobX management, that can be used in almost any application state.

A powerful state machine for MobX management, which can be used in almost any application state. It has 3 states - loading, success, error - and is pe

Apr 25, 2022
Nexus is a state management library that makes it easy to create and consume your application's reactive data to the user interface.

Nexus ?? Nexus is a state management library that makes it easy to create and consume your application's reactive data to the user interface. With nex

Jan 6, 2022
Open-source, cross-platform, hassle-free file sharing with AES-256 encryption made with Flutter & Dart.
Open-source, cross-platform, hassle-free file sharing with AES-256 encryption made with Flutter & Dart.

Odin ⚑ Open source easy file sharing for everyone. ⚑ Cross-platform hassle-free file sharing with AES-256 encryption made with Flutter & Dart. Getting

Jun 28, 2022