Skip to content

FlowAsset

A Flow is a named collection of screens. Each screen has its own widget layout. The host transitions players between screens at runtime — show one player a pick_word screen while the others wait on lobby, advance everyone to a round screen when the game starts, and so on.

Flows are the right abstraction whenever the phone-side UI changes during a session. For single-screen sessions, Templates are simpler.

Authoring

  1. Create the asset. Assets → Create → PairKit → Flow Asset.
  2. Author screens in the inline editor — give each a name, drop in widgets via the WidgetDefinitions factories, set one screen as the initialScreen.
  3. Drag the FlowAsset into the Flow Asset slot on PhoneControllerManager. If both Template and Flow Asset are set, the flow wins.

The custom inspector runs FlowValidator on save — missing initialScreen, duplicate widget ids, empty screens, and similar authoring mistakes surface inline before you press play.

Runtime API

The host moves players between screens with these methods on PhoneControllerManager:

// Move every player to the same screen, with optional initial data.
manager.ShowScreenToAll("lobby", new { count = 0 });
// Move a specific player to a screen.
manager.ShowScreen(player.Id, "spectate");
// Move several players in one call.
manager.ShowScreen(new[] { p1.Id, p2.Id }, "round");
// Shallow-merge new data into the player's current screen state.
manager.UpdateScreenDataForAll("lobby", new { count = newCount });
manager.UpdateScreenData(player.Id, "round", new { hp = 80 });

Scoped input

OnInput fires for every event from every screen — useful, but verbose. With a flow you can subscribe per (screen, widget):

var unsub = manager.OnWidgetInput("lobby", "name_input", (player, evt) =>
{
var name = evt.GetString("text");
manager.UpdateScreenDataForAll("lobby", new { count = ++_ready });
if (_ready == manager.Players.Count)
manager.ShowScreenToAll("round");
});
// Later, when leaving the lobby:
unsub();

The returned delegate is your unsubscribe — call it when leaving the screen so handlers don’t leak across rounds.

Stale input

Input from a screen the player is no longer on is dropped automatically by the relay (counted as pairkit_stale_input_total — see protocol/metrics). You don’t need to defensively check the screen name in your handler.

Pitfalls

  • Forgetting to call the unsubscribe delegate from OnWidgetInput. Subscriptions accumulate. Symptom: input fires the same handler N times after N rounds.
  • Using ShowScreen while players are mid-input. The screen change cancels any PromptAsync for that player with OperationCanceledException. Either await the prompt first, or design the flow so transitions happen between prompts.
  • Author-time Template slot still set. The flow takes precedence at runtime, but a leftover Template in the inspector confuses readers of the scene. Clear it.

Next