in

A lightweight, yet powerful way to bind your application state with your business logic



Pub
Codecov

Logo

A lightweight, yet powerful way to bind your application state with your business logic.

Data flow

We can see the whole application state as the agglomeration of a multitude of tiny states. Each state being independent from each other.
A view can be interested in some particular states and has to use a logic component to update them.

example file.


Counters

We could do this by having a global state being a list of integers, and a business logic component for adding counters and increment them:

final countersRef = StateRef(const <int>[]);

final countersLogic = LogicRef((scope) => CountersLogic(scope));

class CountersLogic with Logic {
  const CountersLogic(this.scope);

  @override
  final Scope scope;

  void addCounter() {
    write(countersRef, read(countersRef).toList()..add(0));
  }

  void increment(int index) {
    final counters = read(countersRef).toList();
    counters[index]++;
    write(countersRef, counters);
  }
}

We can then use the select extension method in a widget to watch the sum of this list:

final sum = context.watch(countersRef.select(
  (counters) => counters.fold<int>(0, (a, b) => a + b),
));

Now, for creating the counter view, we can have an index parameter in the constructor of this view.
This has some drawbacks:

  • If a child widget needs to access this index, we would need to pass the index for every widget down the tree, up to our child.
  • We cannot use the const keyword anymore.

A better approach would be to create a BinderScope above each counter widget. We would then configure this BinderScope to override the state of a StateRef for its descendants, with a different initial value.

Any StateRef or LogicRef can be overriden in a BinderScope. When looking for the current state, a descendant will get the state of the first reference overriden in a BinderScope until the root BinderScope.
This can be written like this:

final indexRef = StateRef(0);

class HomeView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final countersCount =
        context.watch(countersRef.select((counters) => counters.length));

    return Scaffold(
     ...
      child: GridView(
        ...
        children: [
          for (int i = 0; i < countersCount; i++)
            BinderScope(
              overrides: [indexRef.overrideWith(i)],
              child: const CounterView(),
            ),
        ],
      ),
     ...
    );
  }
}

The BinderScope constructor has an overrides parameter which can be supplied from an overrideWith method on StateRef and LogicRef instances.

Note: The whole code for the above snippets is available in the example file.

example file.

snippets.

Sponsoring

I’m working on my packages on my free-time, but I don’t have as much time as I would. If this package or any other package I created is helping you, please consider to sponsor me so that I can take time to read the issues, fix bugs, merge pull requests and add features to these packages.

issue.
If you fixed a bug or implemented a feature, please send a pull request.

GitHub

https://github.com/letsar/binder


Leave a Reply

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

GIPHY App Key not set. Please check settings

Apple iPhone SE 3 Could Replace iPhone 14 Mini Variant

How to send a web form without code or any backend