Skip to content

Flutter: draw only when the artboard changed (didChange), like iOS and web #665

Open

Description

@Michael-Betclic

Submission checklist

  • I have confirmed the issue is present in the latest version of the rive Flutter package
  • I have searched the documentation and forums and could not find an answer
  • I have searched existing issues and this is not a duplicate

Description

With Factory.flutter, FlutterRiveRenderBox calls markNeedsPaint() after every ticker advance while advance returns true; whether the artboard changed is never checked. The C++ runtime already tracks it (Artboard::didChange()), and the other runtimes use it:

  • iOS: RiveView.drawOptimization: DrawOptimization { alwaysDraw, drawOnChanged } (default .drawOnChanged).
  • Web: drawingOptions: DrawOptimizationOptions { AlwaysDraw, DrawOnChanged } (default DrawOnChanged).

Flutter doesn't even expose the flag, so any animation that keeps advancing redraws every frame, including frames where nothing changed.

Patch (ready to re-land)

rive_native has no public repo for a PR, so the series is on a fork, based on rive_native 0.2.0-dev.3 verbatim from pub.dev (4 commits, offered for re-landing with co-authorship as for #631):

What it does:

  • Exports riveArtboardDidChange and adds Artboard.didChange (looked up optionally: binaries without it return true, i.e. always draw).
  • Adds DrawOptimization { alwaysDraw, drawOnChanged } on ProceduralPainter, same names as iOS. Default alwaysDraw = unchanged behaviour.
  • With drawOnChanged, FlutterRiveRenderBox still advances every tick but only repaints when didChange is true or a redraw is forced (first frame, layout, painter swap, scheduleRepaint/fit/alignment, pointer, image decode, last advance before the ticker stops). The box becomes a repaint boundary.
  • Opt-in because a gap in dirt tracking (cf. #14701, opacity 0) would show a stale frame; the default can flip later like iOS/web. Factory.rive isn't covered (texture cleared before advance).

Benchmark (Galaxy Z Flip6, profile, 60 Hz, at rest, 10 s windows)

fps build ms raster ms GPU process CPU
tab bar, alwaysDraw 59.3 3.30 6.92 55 % 80 %
tab bar, drawOnChanged 59.8 1.77 6.78 55 % 71 %
player card, alwaysDraw 59.5 1.63 3.57 17 % 47 %
player card, drawOnChanged 59.8 1.01 2.87 18 % 36 %

Build time roughly halved and ~10 CPU points saved. GPU is unchanged because frames keep coming while the ticker runs (see the related issue).

Tests

New draw_optimization_test.dart 13/13 (fails without the gate); artboard_did_change_test.dart 1 pass / 2 skipped with the prebuilt library (no export; verified with llvm-nm on Android builds and on device); nearby existing suites 63/63 unchanged.

Reproduction steps / code

Any Rive animation that keeps advancing while holding still (e.g. a looping timeline with constant keys), shown with RiveWidget on Factory.flutter: DevTools shows a Rive repaint on every frame although Artboard::didChange() is false.

Rive Flutter package version

rive 0.14.11 (rive_native 0.1.11); patch based on rive_native 0.2.0-dev.3.

Flutter version

Flutter 3.47.3 (stable), Dart 3.13.3

Device

Samsung Galaxy Z Flip6 (SM-F741B)

OS version

Android 16

Additional context

Related: #664 (state machine never settles after the max-iterations cap).

🤖 Generated with Claude Code

Activity

  1. HayesGordon commented on Oct 20, 2026

    @HayesGordon
    Contributor

    The didChange check is something we've been meaning to implement for the Flutter runtime. Looking into it.

  2. added
    knownTriaged: understood and tracked on our side
    on Oct 21, 2026
  3. added
    fixed-pending-releaseFixed and merged; will close when it ships
    and removed
    knownTriaged: understood and tracked on our side
    on Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    fixed-pending-releaseFixed and merged; will close when it ships

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions