Spawned content

Triggers on runtime-spawned prefabs and nested components.

A state's triggers don't have to be declared on its root. Components nested in the prefab, and prefabs the state spawns at runtime, can all drive the flow, and their triggers appear as ports on the state's node.

StateFlow Editor — Spawned Content
The Inventory node lists OpenGemStore under CurrencyBar, and Selected<String> under an accented InventorySlot section marked 'spawned'. They lead to Gem Store and Item popups.
OpenGemStore comes from a nested CurrencyBar. Selected comes from the InventorySlot prefab the screen spawns, shown in the accent colour.

Nested components

Every StateFlowComponent in the prefab's hierarchy contributes its triggers, grouped on the node under its class name. A reusable piece of UI can carry its own exits:

CurrencyBar.cs
public class CurrencyBar : StateFlowComponent
{
    [SerializeField] private Button getGemsButton;

    public event Trigger OpenGemStore;

    public override void Initialize()
    {
        getGemsButton.onClick.AddListener(() => OpenGemStore?.Invoke());
    }
}

Put the CurrencyBar inside any screen prefab, and its OpenGemStore port shows on that screen's node. Nested components receive the same lifecycle calls as the root.

Spawned prefabs

Lists, grids and other content you instantiate at runtime don't exist when the editor builds the node. Mark the prefab field with [SpawnedState] and the editor reads the prefab's triggers from it:

InventoryScreen.cs
using System.Collections.Generic;
using StateFlow.Runtime.Attributes;
using StateFlow.Runtime.Components.State;
using StateFlow.Runtime.Utilities;
using UnityEngine;

public class InventoryScreen : StateFlowComponent
{
    [SpawnedState, SerializeField] private InventorySlot slotPrefab;
    [SerializeField] private RectTransform grid;

    private readonly List<InventorySlot> slots = new();

    public override void Initialize()
    {
        foreach (var item in Inventory.Items)
        {
            slots.Add(StateFlowSpawner.Spawn(slotPrefab, grid, item));
        }
    }

    // Removing content while the state is open. Slots still alive when the state itself
    // is destroyed get Exit and Dispose automatically.
    private void ClearSlots()
    {
        foreach (var slot in slots)
        {
            StateFlowSpawner.Despawn(slot);
        }

        slots.Clear();
    }
}
InventorySlot.cs
public class InventorySlot : StateFlowComponent, IStateFlowComponentWithPayload<string>
{
    [SerializeField] private Button button;
    [SerializeField] private Text label;

    private string item;

    public event Trigger<string> Selected;

    public override void Initialize()
    {
        button.onClick.AddListener(() => Selected?.Invoke(item));
    }

    public void SetPayload(string payload)
    {
        item = payload;
        label.text = payload;
    }
}

[SpawnedState]

StateFlowSpawner

MethodDoes
Spawn(prefab, parent)Spawns the prefab, binds its triggers to the active state's transitions and runs Initialize, LateInitialize and Enter. TransitionFinished follows once the owning state's transition has ended.
Spawn(prefab, parent, payload)The same, delivering payload through SetPayload as a transition would.
Spawn(prefab, parent, first, second)The same with two payloads.
Despawn(instance)Unbinds it, runs Exit and Dispose, and destroys it.

In 1.2.0 this class was called ManagedLifecycle, with Instantiate and Dispose. The old name still compiles, with an obsolete warning, until 2.0.

Spawn under the state's own hierarchy, so the content belongs to that state's machine. Spawned content keeps its own lifecycle: it isn't exited when a popup opens over its owner, and it stays bound when the owner becomes active again. When the owning state is destroyed, any spawned content still alive gets Exit and Dispose automatically. Use StateFlowSpawner.Despawn to remove content earlier, while the state is open. Object.Destroy also works, but skips its Exit and Dispose.

Play mode
An Inventory screen with a grid of nine spawned item slots, a Shuffle button and a Gems currency bar at the top right.
Each slot is spawned with StateFlowSpawner, and tapping one fires its Selected trigger.
Try it

Import Feature Samples from the Package Manager and open 09 Spawned Content/09 Spawned Content.unity. About the samples