simprokmachine 1.1.6 copy "simprokmachine: ^1.1.6" to clipboard
simprokmachine: ^1.1.6 copied to clipboard

A framework for automation of inter-class communication in a declarative style.

simprokmachine #

Introduction #

Every application consists of the classes that refer to the other classes, which refer to the other classes etc. It is a tree of classes where each one knows about its children and doesn't know about its parent.

In general, classes communicate with each other by calling methods and properties of their children to pass data "down" or by triggering a callback passing data "up" to the parent.

Moreover, we have many patterns to make this communication easier such as delegate, facade, observable, command, etc. And we haven't even started discussing how to pass data from the logic's layer to the UI.

Problem #

Every time the communication must be organized it is up to us to decide which pattern to use, and how to handle it. This requires attention and can easily result in unexpected bugs.

Solution #

simprokmachine is a framework that automates the communication between the application's components called "machines".

How to use #

Machine - is an instance in your application that receives and processes input data and may emit output.

concept

To create it use ChildMachine class.

class PrinterMachine extends ChildMachine<String, bool> {
    
    @override
    void process(String? input, Handler<bool> callback) {
        log(input);
    }
}

If your machine must update flutter widgets with its input - use ChildWidgetMachine instead. Combine it with MachineConsumer to update UI where needed.

class PrinterWidgetMachine extends ChildWidgetMachine<String, bool> {

    @override
    Widget child() {
      return MaterialApp( 
          home: Scaffold(
            body: Center(
              child: MachineConsumer<String, bool>(
                initial: (BuildContext context) => Text("UI before first input was received"),
                builder: (BuildContext context, String? input, Handler<bool> callback) => Text("UI when input received: $input")
              ),
            ),
          ),
      ),
    }
}

To start the flow use runRootMachine() method in your main() function.

void main() {
    runRootMachine<String, bool>(
        root: PrinterWidgetMachine(),
    );
}

This does not print anything but null because after startRootMachine() is called the root is subscribed to the child machine triggering process() method with null value.

Use Handler<Output> callback to emit output.

class EmittingMachine extends ChildMachine<String, bool> {
    
    void process(String? input, Handler<bool> callback) {
        if (input != null) { 
            log("input: \(input)");
        } else {
            callback(false); // Emits output
        }
    }
}

To unite two or more machines where one machine is WidgetMachine and another one is plain Machine use mergeWidgetMachine().

void main() {
    runRootMachine<String, bool>(
        root: mergeWidgetMachine(
          main: PrinterWidgetMachine(),
          secondary: { PrinterMachine(), },
        ), 
    );
}

To merge more than one machine together - use merge().

final Machine<Input, Output> machine1 = ...
final Machine<Input, Output> machine2 = ...

... = merge({
    machine1,
    machine2,
});

To separate machines into classes instead of cluttering them up in the runRootMachine(), use ParentMachine or ParentWidgetMachine classes.

class IntermediateLayer extends ParentMachine<String, bool> {

    @override
    Machine<String, bool> child() {
        return PrinterMachine(); // or PrinterWidgetMachine() if extends ParentWidgetMachine
    }
}

To map or ignore input - use inward().

... = machine.inward((ParentInput parentInput) {
    return Ward.values([ChildInput(), ChildInput(), ChildInput()]); // pass zero, one or more outputs.
})

To map or ignore output - use outward().

... = machine.outward((ChildOutput childOutput) {
    return Ward.values([ParentOutput(), ParentOutput(), ParentOutput()]); // pass zero, one or more outputs.
});

To send input back to the child when output received - use redirect().

... = machine.redirect((ChildOutput childOutput) { 
    // Return 
    // Direction.prop() - for pushing ChildOutput further to the root.
    // Direction.back(Ward.values([ChildInput()])) - for sending child inputs back to the child.
    ...
});

Check out the sample and the wiki for more information about API and how to use it.

Killer-features #

  • Declarative way of describing your application's behavior.
  • Automated concurrency management saves from race conditions, deadlocks, and headache.
  • Flexible. Every existing component can become a machine.
  • Modular. Every machine can be described once and reused easily.
  • Cross-platform. Kotlin and Native iOS supported.

Installation #

Add the line into pubspec.yaml:

dependencies:
    simprokmachine: ^1.1.3

Now in your Dart code, you can use:

import 'package:simprokmachine/simprokmachine.dart';

What to check next #

Check out these tools to see an existing library of useful machines and the architectural approach we suggest using.

0
likes
120
pub points
0%
popularity

Publisher

unverified uploader

A framework for automation of inter-class communication in a declarative style.

Repository (GitHub)
View/report issues

Documentation

Documentation
API reference

License

MIT (LICENSE)

Dependencies

flutter, provider, rxdart

More

Packages that depend on simprokmachine