Mobx.dart: Using Mobx without code generation

Created on 23 Dec 2020  路  7Comments  路  Source: mobxjs/mobx.dart

Thank you for your work! Is there a way to use Mobx with Flutter without code generation? This article shows a way how to do it using dart's extension: https://itnext.io/experimenting-with-mobx-and-dart-extensions-444d1d5e1779

We had to switch from Mobx to another state manager because of the code generation. Mobx would be much easier to use without it.

Most helpful comment

This is a direction I want to explore for sure. I am currently occupied with client projects but will start thinking on this

All 7 comments

This is a direction I want to explore for sure. I am currently occupied with client projects but will start thinking on this

I would love to see that happening ! I agree that code generation is generally a pain... I don't mind giving a hand @pavanpodila if you can provide some guidance

Hello @jaumard, firstly thanks for the offer! Let me give some general steps we would have to take to create a convenient API for using MobX without code-gen:

  1. As pointed out, create a bunch of extensions to create Observables, Actions and Reactions. That is the core of MobX. You can see this in the test cases for Mobx where these entities are created with constructors.
  2. Since they need to become part of a class, we need a way to make them as fields and allow simple instantiations, either in the constructor or directly at the point of declaration in the class.

It is all about finding the right balance of API so that using MobX without code-gen becomes as intuitive as possible. I am personally very interested in exploring this further considering all the Dart features that are available today. Extension methods are definitely going to be used heavily, along with others which I have not even explored yet. I would encourage thinking along those lines.

HTH.

Nice thanks for those information! What do you think about the article? Because look like pretty good between the extensions and the late instantiations. I'll give it a try see how it goes :)

Another way would be to wait meta static programming of dart. That can be quite useful to replace code generation

I love the ideas in the article and surely extensions are the way to go forward. Would be great to create a PR with examples and tests. This would be a really awesome thing to add to MobX!

Ok cool :) I'll try to play around with all that and come up with a draft PR :D

So I've tried few things with extensions and look like most of them already exist in the current codebase

Counter example would look like:

import 'package:mobx/mobx.dart';

class Counter with Store {
  late final increment = _increment.asAction();

  Observable<int> counter = 0.asObservable();

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

What I don't like here is that the usage is more verbose.

import 'counter_extension.dart';

main() {
  final test = Counter();
  print(test.counter.value);
  test.increment();
  print(test.counter.value);
}

So with extensions no way to access the value directly as you have everything as observable.
And that's the dart usage, I think it will be even more constraining in a Flutter app.

I've tried the todo example in extension too and it was really painful lol here is what it look like:

import 'package:mobx/mobx.dart';

class Todo with Store {
  Todo(String description) {
    this.description.value = description;
  }

  final description = ''.asObservable();

  final done = false.asObservable();

  @override
  String toString() {
    return 'Todo{description: ${description.value}, done: ${done.value}}';
  }
}

enum VisibilityFilter { all, pending, completed }

class TodoList with Store {
  final todos = <Todo>[].asObservableList();

  final filter = VisibilityFilter.all.asObservable();

  final currentDescription = ''.asObservable();

  late final pendingTodos = _pendingTodos.asComputed();

  ObservableList<Todo> _pendingTodos() =>
      ObservableList.of(todos.where((todo) => todo.done != true));


  late final completedTodos = _completedTodos.asComputed();

  ObservableList<Todo> _completedTodos() =>
      ObservableList.of(todos.where((todo) => todo.done == true));

  late final hasCompletedTodos = _hasCompletedTodos.asComputed();

  bool _hasCompletedTodos() => completedTodos.value.isNotEmpty;

  late final hasPendingTodos = _hasPendingTodos.asComputed();

  bool _hasPendingTodos() => pendingTodos.value.isNotEmpty;


  late final itemsDescription =
      _itemsDescription.asComputed();

  String _itemsDescription() =>
      '${pendingTodos.value.length} pending, ${completedTodos.value.length} completed';

  late final visibleTodos = _visibleTodos.asComputed();

  ObservableList<Todo> _visibleTodos() {
    switch (filter.value) {
      case VisibilityFilter.pending:
        return pendingTodos.value;
      case VisibilityFilter.completed:
        return completedTodos.value;
      default:
        return todos;
    }
  }

  late final addTodo = _addTodo.asAction();
  late final removeTodo = _removeTodo.asAction();
  late final changeDescription = _changeDescription.asAction();
  late final changeFilter = _changeFilter.asAction();
  late final markAllAsCompleted = _markAllAsCompleted.asAction();

  void _addTodo(String description) {
    final todo = Todo(description);
    todos.add(todo);
    currentDescription.value = '';
  }

  void _removeTodo(Todo todo) {
    todos.removeWhere((x) => x == todo);
  }

  void _changeDescription(String description) =>
      currentDescription.value = description;

  void _changeFilter(VisibilityFilter filter) => this.filter.value = filter;

  void _removeCompleted() {
    todos.removeWhere((todo) => todo.done.value);
  }

  void _markAllAsCompleted() {
    for (final todo in todos) {
      todo.done.value = true;
    }
  }
}

And simple usage:

import 'todos_extension.dart';

main() {
  final test = TodoList();
  print(test.todos);
  test.addTodo.call(['description']);//if actions have parameters you must call them like this now...
  print(test.todos);
}

I really would like to have MobX without code generation, but I don't think that currently doable with only extensions.

To me static meta programming could be the solution: https://github.com/dart-lang/language/issues/1482

But it's not here yet :D

Was this page helpful?
0 / 5 - 0 ratings

Related issues

thedejifab picture thedejifab  路  5Comments

omneimneh picture omneimneh  路  4Comments

JCKodel picture JCKodel  路  3Comments

viktorszekeress picture viktorszekeress  路  4Comments

benzsuankularb picture benzsuankularb  路  3Comments