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
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
LayoutComponentwith an ArtboardComponentList oftileartboards. 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,tileNumberset, 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:
- assign the (still empty) canvas bindable to the slot;
- 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-casedstateMachine.advanceAndApply(0)for down/up/cancel inRiveWidgetPainter.pointerEvent(rive 0.14.9widget_controller.dart) — while identical call bodies executed from timers never converge, even with the same explicit advance calls. - Likely related runtime paths:
StateMachineInstancehit-testing skips component-list items whoseworldToLocalis unavailable (state_machine_instance.cpp~line 1060continue), and an artboard-property assignment only marks binding dirt (viewmodel_instance_artboard.cpp:24) with the actual mount deferred toupdatePass. - Items added to the
ArtboardComponentListfrom 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
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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