Agent

Agent represents a reference to a specific unit that it is set up for.

Base

  • isInit: Indicates whether the unit is initialized and attached to the tree.

  • isInitializing: Indicates whether the unit is being initialized.

  • isAbsence: Indicates whether the unit is being disposed or has not been initialized.

  • Future<void> process(Paper paper): Triggers the unit to process the papers.

  • Future<R?> report<R extends P>(): Asks the unit to report the papers of type R.

We have 2 types of agents for each type of unit Unit and UnitWidget respectively, with more suitable APIs:

UnitAgent

  • UnitAgent<P> log(Unit<P> Function(UnitAgent<P> agent) builder): Registers a builder to build Unit attached to this agent. In the builder, it provides an input agent. It is the agent itself so we can conveniently inject it into the Unit.

Set<Agent>? register() => {
    agent.log((a) => SampleUnit(a)), // a is the agent itself.
}
  • Future<void> init(): Initializes the unit and attaches it to the tree. Usually, it is not explicitly called because when Agent.process is called, the init will be called to initialize before processing the papers.

  • void dispose(): Disposes the unit and detaches it from the tree. It is used to save memory if the unit is not needed.

WidgetAgent

  • WidgetAgent<P> log(UnitWidget<P> Function(WidgetAgent<P> agent) builder): Registers a builder to build UnitWidget attached to this agent. This method is needed when the unit widget is the root of the UI, or of the context tree, but under a root of a unit in the unit tree.
class SampleState extends UnitState<SamplePaper, SampleUnit> {
  final rootWidget = WidgetAgent<RootWidgetPaper>();

  
  Set<Agent>? register() => {
    rootWidget.log((a) => RootWidget(widgetState, agent: a)), // a is the agent itself.
  };
}

Then we can trigger the building of the root widget, usually in Script:

class SampleScript extends Script<SamplePaper, SampleState> {
  
  void map() => on<SampleInitializing>(onInitializing);

  Future<void> onInitializing(
    SampleInitializing p,
    SampleState s,
    SourceVerifier ifFrom,
  ) async {
    await s.rootWidget.constructAsRoot();
  }
}
  • Future<void> constructAsRoot(): Constructs the unit widget as a root widget in the context tree. Once a root widget is constructed, calling this method on other agents will have no effect.

AgentSet

AgentSet is generally a collection of agents, which are attached to multiple units of the same type.

AgentSet is meant for dynamically generating agents without knowing the boundary at runtime.

UnitAgentSet

UnitAgentSet is a subtype of AgentSet that is specialized for obtaining UnitAgent.

  • bool isInitAt(String key): Checks if the UnitAgent at key is initialized.

  • bool isInitializingAt(String key): Checks if the UnitAgent at key is initializing.

  • bool isAbsentAt(String key): Checks if the UnitAgent at key is absent.

  • Agent log(Unit Function(String key, UnitAgent<P> a) builder): Registers a method for building units attached to key.

  • Future<void> init(String key): Initializes a unit at key.

  • void dispose(String key): Disposes the unit at key.

  • Future<void> process(String key, {required P paper}): Commands the unit at key to process paper. If the unit is not initialized yet, it will be initialized and then process paper.

  • Future<void> report<R extends P>(String key): Asks the unit at key to report papers with type R.

WidgetAgentSet

WidgetAgentSet is a subtype of AgentSet that is specialized for obtaining WidgetAgent.

  • bool isInitAt(String key): Checks if the WidgetAgent at key is initialized.

  • bool isInitializingAt(String key): Checks if the WidgetAgent at key is initializing.

  • bool isAbsentAt(String key): Checks if the WidgetAgent at key is absent.

  • WidgetAgent<P> agentAt(String key): Retrieves the agent at key. If the agent does not exist, an agent attached to key will be created.

  • Future<void> process(String key, {required P paper}): Commands the unit widget at key to process paper. If the unit is not initialized yet, it will be initialized and then process paper.

  • Future<void> report<R extends P>(String key): Asks the unit widget at key to report papers with type R.

// branch.dart

class Branch extends UnitWidget<BranchPaper> {
  // ignore: use_key_in_widget_constructors
  Branch(
    super.parent, {
    required super.agent,
    super.listener,
  });

  
  UnitWidgetState<BranchPaper, Branch> createState() => BranchState();

  
  Script<BranchPaper, BranchState> createScript() => BranchScript();
}

class BranchState extends UnitWidgetState<BranchPaper, Branch> {
  final leafs = WidgetAgentSet<LeafPaper>();

  final flowers = UnitAgentSet<FlowerPaper>();

  
  Set<Agent>? register() => {
        flowers.log((key, a) => Flower(a)),
      };

  bool show = true;
  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          body: SafeArea(
        child: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              if (show) Leaf(this, agent: leafs.agentAt('top')),
              const SizedBox(height: 10),
              if (show) Leaf(this, agent: leafs.agentAt('middle')),
              const SizedBox(height: 10),
              Leaf(this, agent: leafs.agentAt('bottom')),
              const SizedBox(height: 10),
              ElevatedButton(
                onPressed: () {
                  report(BranchLeafsColor());
                },
                child: const Text('Change leaves color'),
              ),
              ElevatedButton(
                onPressed: () {
                  report(BranchFlowersBloom());
                },
                child: const Text('Bloom flowers'),
              ),
            ],
          ),
        ),
      )),
    );
  }
}

class BranchScript extends Script<BranchPaper, BranchState> {
  
  void map() =>
      on<BranchLeafsColor>(onLeafColor)
      ?.on<BranchFlowersBloom>(onFlowersBloom);

  Future<void> onLeafColor(
    BranchLeafsColor p,
    BranchState s,
    SourceVerifier ifFrom,
  ) async {
    await s.leafs.process('top', paper: LeafColor(Colors.red));
    await s.leafs.process('middle', paper: LeafColor(Colors.blue));
    await s.leafs.process('bottom', paper: LeafColor(Colors.green));

    final topLeafColorPaper = await s.leafs.report<LeafColor>('top'); // Colors.red
    final middleLeafColorPaper = await s.leafs.report<LeafColor>('middle'); // Colors.blue
    final bottomLeafColorPaper = await s.leafs.report<LeafColor>('bottom'); // Colors.green
  }

  Future<void> onFlowersBloom(
    BranchFlowersBloom p,
    BranchState s,
    SourceVerifier ifFrom,
  ) async {
    await s.flowers.process('top', paper: FlowerBloom(true));
    await s.flowers.process('middle', paper: FlowerBloom(true));
    await s.flowers.process('bottom', paper: FlowerBloom(false));

    final topFlowerBloomPaper = await s.flowers.report<FlowerBloom>('top'); // true
    final middleFlowerBloomPaper = await s.flowers.report<FlowerBloom>('middle'); // true
    final bottomFlowerBloomPaper = await s.flowers.report<FlowerBloom>('bottom'); // true
  }
}

class BranchPaper extends Paper {}

class BranchLeafsColor extends BranchPaper {}

class BranchFlowersBloom extends BranchPaper {}
// leaf.dart
class Leaf extends UnitWidget<LeafPaper> {
  // ignore: use_key_in_widget_constructors
  Leaf(super.parent, {required super.agent, super.listener});

  
  UnitWidgetState<LeafPaper, Leaf> createState() => LeafState();

  
  Script<LeafPaper, LeafState> createScript() => LeafScript();
}

class LeafState extends UnitWidgetState<LeafPaper, Leaf> {
  Color color = Colors.amber;

  
  Widget build(BuildContext context) {
    return Container(width: 60, height: 60, color: color);
  }
}

class LeafScript extends Script<LeafPaper, LeafState> {
  
  void map() => on<LeafColor>(onColor, onReportColor);

  void onColor(
    LeafColor p,
    LeafState s,
    SourceVerifier ifFrom,
  ) {
    s.color = p.color;
    s.render();
  }

  LeafColor onReportColor(
    LeafState s,
  ) {
    return LeafColor(s.color);
  }
}

class LeafPaper extends Paper {}

class LeafColor extends LeafPaper {
  LeafColor(this.color);

  final Color color;
}
// flower.dart
class Flower extends Unit<FlowerPaper> {
  Flower(
    super.agent, {
    super.listener,
    this.bloom = false,
  });

  final bool bloom;

  
  Script<FlowerPaper, FlowerState> createScript() => FlowerScript();

  
  UnitState<FlowerPaper, Flower> createState() => FlowerState();
}

class FlowerState extends UnitState<FlowerPaper, Flower> {
  late bool bloom;

  
  Future<void> initState(Flower unit) async {
    super.initState(unit);

    bloom = unit.bloom;
  }
}

class FlowerScript extends Script<FlowerPaper, FlowerState> {
  
  void map() => on<FlowerBloom>(onBloom, onReportBloom);

  void onBloom(
    FlowerBloom p,
    FlowerState s,
    SourceVerifier ifFrom,
  ) {
    s.bloom = p.bloom;
  }

  FlowerBloom onReportBloom(
    FlowerState s,
  ) {
    return FlowerBloom(s.bloom);
  }
}

class FlowerPaper extends Paper {}

class FlowerBloom extends FlowerPaper {
  FlowerBloom(this.bloom);

  final bool bloom;
}