Concepts

How the framework thinks

Five ideas explain everything: element scenes, states, the stack, context and shared elements.

Vocabulary

TermMeaning
Element sceneA Unity scene with one root object <Name>Root holding a UiElement. The unit of loading.
UiElementA MonoBehaviour that receives data in Populate(object context) and owns its show / hide animation presets.
StateA named set of element scenes, identified by a state key, e.g. StoreUiState.
ContextThe object passed to ShowState, forwarded unchanged to every element of the state. Usually a UiPopulationParams subclass.
Shared elementA scene used by consecutive states. It stays loaded, is re-populated, and is not animated.

An element's lifecycle

Scene loadsAwake picks the target Populate(ctx)may run again: idempotent PrepareForShowstart pose, no flash ShowAsyncplays the show preset HideAsyncthen unloaded at ref 0 Elements already on screen (shared) skip PrepareForShow, ShowAsync and HideAsync, and only receive Populate again.

The state stack

Non-additive (the default) replaces the whole stack: the new state animates in while every state on the stack animates out, then the old states are released.

Additive pushes on top, for popups and overlays. The states below stay loaded and visible.

HideUI() pops the top state, plays its hide animations and releases its scenes.

ResetUIAsync() hides and unloads everything.

the stack after ShowState(Reward, additive: true) RewardUiState RewardPopupUiElement · top, HideUI pops it MainMenuUiState MainMenuUiElement TopBarUiElement NavBarUiElement · still loaded and visible ShowState(Store) without additive replaces both

Shared elements and reference counting

Scenes are identified by their Addressables GUID and counted per state on the stack. The new state's scenes are counted before the old ones are released, so a scene used by both never unloads and reloads in between. A scene unloads only when its count reaches zero.

MainMenuUiState → StoreUiState (tab switch with a SlideLeft transition) TopBarUiElementshared · re-populated · not animated MainMenuUiElementhide: leaves moving left, then unloaded StoreUiElementshow: enters from the right moving left NavBarUiElementshared · re-populated · not animated

State classes are type markers

ShowState<StoreUiState>() finds the state whose key equals the class name. The runtime builds the state from the config, so code inside a UiState subclass never runs: keep behaviour in your elements. For keys without a class, use ShowStateByKey("StoreUiState").

The element scene contract

Animation targets the one object under the Canvas, so every element scene has the same shape. The Elements tab checks it and fixes it in one click.

<Name>Root plain Transform with the UiElement subclass; the ONLY root object in the scene └─ Canvas child 0: root Canvas + CanvasScaler + GraphicRaycaster └─ Root the ONLY Canvas child: stretched RectTransform, the animation target └─ … all content (nested canvases are fine here)

Good to know

One call at a time

Overlapping ShowState / HideUI calls are not queued for you. Await each call, or route navigation through one place that queues them (the showcase's ShowcaseNavigator does).

HideUI and shared elements

HideUI doesn't skip elements shared with the state underneath, so keep popups to their own scenes.

Initialization

IsInitialized is true as soon as the manager exists, before the config has loaded. Await InitializeAsync() before showing states.

No data binding

The framework hands each element its context. Updating what is already on screen (a coin counter) is your code, for example events on your data.