Technical comparison

Three visibility jobs. One feed decision.

scroll_spy, visibility_detector, and inview_notifier_list solve related problems, but they do not promise the same result. The useful comparison starts with the question your Flutter screen asks.

Short answer

01

How much of this widget is painted?

Start with visibility_detector. It is a general, paint-aware visibility primitive.

02

Is this indexed list child inside my range?

Consider inview_notifier_list. Its list wrapper and item builder target that job directly.

03

Which visible item should own attention?

Use scroll_spy when the screen needs one policy-selected, stable primary item for autoplay, navigation, analytics, or prefetching.

Responsibility matrix

Responsibilityscroll_spyvisibility_detectorinview_notifier_list
Visible stateYesYes, paint-aware detailYes, configured list range
Focused regionLine, zone, or customApplication codeRange condition
Single winnerFive policies or customApplication codeApplication code
Winner stabilityHysteresis + hold timeApplication codeApplication code
Global resultPrimary, focused, visible IDsPer-widget callbacksItem builder state
Debug UIViewport overlayApplication codeApplication code

A narrow reproducible benchmark

The repository includes a widget-test harness for the common denominator: whether fixed-extent ListView items intersect the viewport. It uses 3,000 items, a 400 × 600 viewport, forty 25-pixel jumps, aligned zero delivery intervals, and three actual mounted-item counts. scroll_spy's focus and primary selection are disabled in this scenario.

The Flutter 3.44.4 reference run produced the following deterministic API-delivery counts:

ImplementationMountedReactive buildsCallbacksRepeated deliveries
scroll_spy11 / 51 / 20140 / 40 / 402010
inview_notifier_list11 / 51 / 201220 / 1,020 / 4,020not exposed190 / 990 / 3,990
visibility_detector11 / 51 / 20130 / 30 / 308060

All three integrations reported the same 20 normalized visibility transitions and the same final visible sets. The counts show delivery behavior in this one harness. A callback and a builder invocation are not equivalent CPU units, so these numbers are not relative speedups.

Debug wall time around `jumpTo` and two test pumps is printed only to catch major harness regressions. It is not device frame time, raster time, GPU time, release throughput, or FPS.

Read the full methodology Inspect the harness

What to benchmark in your app

  • Use the same device, Flutter version, build mode, list, cache extent, and scroll gesture.
  • Separate detection work from video decoding, network fetches, image decoding, and application rebuilds.
  • Measure profile-mode frame timing with DevTools or a FrameTiming harness.
  • Test the semantics you need. Do not disable primary selection if your product depends on it.
  • Record callback, rebuild, and allocation behavior separately.

Need one calm playback owner?

Run the real-video example and inspect every transition before choosing.

Open the demo Read the autoplay guide