Modulisto
A advanced state management system that based on the concepts of Trigger and Store, which are handled by groupings and subscriptions
Quick Start
1. Create a module that extends Module
class SimpleCounter extends Module {
...
}
2. Declare in this module a Units that you will use
final state = Store(0);
// Generic <()> means that trigger doesn't have any payload
final increment = Trigger<()>();
final decrement = Trigger<()>();
// Trigger can be private to describe some internal functionality
final _setValue = Trigger<int>();
3. Initialize module and create subscriptions with Module.initialize in constructor body
SimpleCounter(...) {
// ref is protected ModuleRef that used to create Module subscriptions
// and initialize declared `Unit`s
Module.initialize(
this,
(ref) => ref
..asyncPipelineGroup(
// `eventTransformers.sequental` is used by default
transformer: eventTransformers.sequental,
($) => $
..on(increment, _increment)
..on(decrement, _decrement)
..on(reset, _reset),
)
..syncGroup(
($) => $
..on(state, print)
..on(ref.lifecycle.init, (_) => print('inited'))
..on(ref.lifecycle.dispose, (_) => print('disposed')),
),
);
}
4. Use newly created module in your project
final counter = SimpleCounter(...);
counter.increment().invoke(); // 1
counter.increment().invoke(); // 2
counter.decrement().invoke(); // 1
await Future.delayed(
Duration.zero,
counter.dispose,
);
PLEASE: Read this small brief about using Trigger<T>'s
Entities
Unit
An abstract, observable entity that encapsulates the relationship between modules and their ability to produce meaningful intention, denoted as [Unit]Intent, which describes the purpose or objective associated with the Unit
[Unit]Intent
A private entity that serves a description of an intentional action around Unit, holding a reference to the this unit and target payload.
It should be emitted exclusively within the zone of IntentStream.
Store<T>
A subtype of the Unit that represents a holder/storage/container for T value
When the .update method is invoked, it returns a private _StoreIntent<T> object that stores new value with type T
Trigger<T>
A subtype of the Unit that represents a unary (synchronous) call.
When the .call method is invoked (with the appropriate payload of type T), it returns a private _TriggerIntent<T> object that brings T payload to subscribers
This rule streamlines the process of describing and invoking triggers, eliminating the need to create custom Event classes, as is done in the bloc.
Small brief about using Trigger
Any Trigger produces relevant _TriggerIntent on .call, this means that it only creates an intention, but does not execute it
Any _TriggerIntent has an .invoke(), which allows us to notify the referenced Trigger about our purpose to invocation
You MUST NOT call .invoke() directly in the body of an IntentStream
The context of processing each IntentStream prohibits direct invocation of .invoke(), instead you need to yield your _TriggerIntent that you got after calling .call(...) onto your Trigger
IntentStream _test(() _) async* {
// myCoolTrigger is Trigger<int>
// BAD
myCoolTrigger(123).invoke(); // throws Exception
// GOOD
yield myCoolTrigger(123);
}
// Somewhere in your code
void main() {
...
final module = CoolModule();
// GOOD
module.myCoolTrigger(123).invoke();
}
Generally, .invoke() method exists only for "pull the Trigger" in "open-world" context (out of the module)
Grouping and subscribing (of the Unit's)
Modulisto uses two ways of "group and subscribe":
- Asynchronous pipeline group -
asyncPipelineGroup - Synchronous group -
syncGroup
Each one can be created using the appropriate methods provided by ModuleRef, which is accessible through the static method Module.initialize
ModuleRef
Protected reference to this module that allow us to create groups and subscriptions during the initialization phase
asyncPipelineGroup
Used to subscribe one or more Unit's to a function that returns an IntentStream based on the provided payload
Each such function passes through an internal StreamController (shared for all events in this group) and is processed according to the provided transformer
Any EventTransformer used in a bloc or (delivered via bloc_concurrency) can be used here in the same manner
TL;DR: asyncPipelineGroup - Grouped subscriptions to specific events that generate stream of asynchronous feedback intentions
syncGroup
Used to subscribe Units to synchronous callbacks that doesn't returns anything (void)
Under the hood, syncGroup does not utilize any form of pipelining, so each callback is executed synchronously as reaction to the Unit
Can be used for debugging purposes or cross-invokation other Trigger's