Skip to content

Web: dynamically bound nested artboard (artboard property + ArtboardComponentList) never receives pointer events when slot assign and list population run in the same batch #644

Description

@NashIlli

Environment

  • Flutter web (both DDC dev builds and wasm), rive: 0.14.9, rive_native: 0.1.9 (wasm artifact @rive-app/flutter-native-wasm@42.0.0)
  • Reproduced on Chrome desktop with mouse and with touch input
  • Only verified on web so far — we have not ruled out other platforms; the suspect runtime paths are shared, so native targets may be affected too

Setup

  • A root artboard whose view model has an artboard property ("slot").
  • At runtime we bind a fresh canvas artboard into it:
    final vm = file.defaultArtboardViewModel(canvasArtboard)!.createInstance();
    final bindable = file.artboardToBind('canvas', viewModelInstance: vm);
    rootVm.artboard('slot')!.value = bindable;
  • The canvas artboard contains a LayoutComponent with an ArtboardComponentList of tile artboards. Each tile's state machine has native pointer listeners (Pointer Down / Pointer Enter) that fire view-model triggers.
  • The list is populated by a Luau script that reacts to a VM number (tileNumber).

Actual behavior

If the slot assignment and the list population run in the same synchronous batch (e.g. inside a setup call chain executed from a timer/Future), the mounted content renders perfectly and accepts VM writes (colors, etc.), but its tile pointer listeners never fire — permanently. Hit-testing silently skips the content forever, no matter how much time passes or how many pointer events arrive later. Listeners belonging to the root artboard's own state machine keep working the whole time.

Expected behavior

Programmatic/deferred structural mutations (artboard-property rebinds + component-list population) should converge for hit-testing the same way they do for rendering.

What does NOT fix it (all tried)

  • Explicit stateMachine.advanceAndApply(0) after every structural boundary (slot assign, tileNumber set, per-item color writes) — even though it internally runs up to 5 update passes.
  • Synthesizing pointer events through RiveWidgetController.pointerEvent (hover/down/up at a neutral position) followed by zero-dt advances.
  • Waiting arbitrarily long (seconds) between the mutations and the first user interaction.

Workaround that DOES fix it

Splitting the mutation into two separately scheduled steps outside the setup batch:

  1. assign the (still empty) canvas bindable to the slot;
  2. in a later scheduled task, set tileNumber (script populates the list) and write the colors;

with advanceAndApply(0) after each boundary. With that separation the same native listeners fire reliably — mouse, drag and touch, across many rounds, including re-populating a live canvas (changing tileNumber on an already-mounted canvas).

Additional data points

  • Mutations executed inside a real Flutter pointer callback (Listener.onPointerDown) always converge — consistent with the special-cased stateMachine.advanceAndApply(0) for down/up/cancel in RiveWidgetPainter.pointerEvent (rive 0.14.9 widget_controller.dart) — while identical call bodies executed from timers never converge, even with the same explicit advance calls.
  • Likely related runtime paths: StateMachineInstance hit-testing skips component-list items whose worldToLocal is unavailable (state_machine_instance.cpp ~line 1060 continue), and an artboard-property assignment only marks binding dirt (viewmodel_instance_artboard.cpp:24) with the actual mount deferred to updatePass.
  • Items added to the ArtboardComponentList from Dart (list.add(...) instead of script population) show a related symptom: item 0 receives the pointer position untransformed, so its hit area behaves as if it covered the whole canvas and co-fires alongside every other tile.
  • Assigning an empty artboard over a populated slot (teardown) also breaks hit-testing of a sibling slot's live content until that sibling is re-mounted with the two-step pattern.

Happy to provide a minimal repro project + .riv on request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions