rive-app / rive-app/rive-flutter

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

Open
#644 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Dart
Stars
1.5k
Forks
240
PR merge metrics
No merged PRs in 30d

Description

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.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the same-batch slot assignment and list population with the minimal project and .riv if provided. Start by reading state_machine_instance.cpp around the hit-testing skip and viewmodel_instance_artboard.cpp around deferred binding, then compare the pointer-event path in widget_controller.dart. Done means mounted nested-list items receive pointer events after deferred structural mutations, with regression coverage for the reported sequence.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, dart, flutter
Domain
frontend, web-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.