Mobile Development 9 min read

Implementing a High‑Performance Waterfall Flow ListView in Flutter

The article describes how to build a high‑performance waterfall‑flow ListView in Flutter by extending SliverMultiBoxAdaptor, managing child reuse, lazy loading, edge‑based insertion, garbage collection, and paint‑only updates, achieving a modest FPS boost on Xianyu’s search page while outlining remaining challenges such as scroll‑to, memory use, and lifecycle callbacks.

Xianyu Technology
Xianyu Technology
Xianyu Technology
Implementing a High‑Performance Waterfall Flow ListView in Flutter

Background

In the Xianyu app many pages require a waterfall‑flow layout, but Flutter only provides ListView and GridView, which cannot express arbitrary custom layouts. Existing community solutions based on SliverMultiBoxAdaptor lack reuse mechanisms and often cause repeated layout, low frame rates, and flashing.

Flutter List View Overview

Flutter’s scrolling system consists of Scrollable (a StatefulWidget that listens to gestures), Viewport (holds one or more Sliver s), and Sliver objects that translate SliverConstraints into SliverGeometry . The Viewport decides which slivers are visible based on the offset passed from Scrollable .

Core Waterfall‑Flow Logic

The layout process must assign an offset to each child and lay it out. The implementation inherits from SliverMultiBoxAdaptor to reuse its constraint conversion and uses a SliverBoxChildManager for lazy loading.

The three main steps are:

During scrolling, find the nearest edge child and add new children before or after it, then layout those children.

Garbage‑collect children that have moved far outside the viewport.

Minimize calls to performLayout so that layout is not directly followed by paint.

Key Code Snippets

Layout loop (simplified):

for (int index = firstIndex; index <= targetLastIndex; ++index) {
  final SliverGeometry gridGeometry = layout.getGeometryForChildIndex(index);
  final BoxConstraints childConstraints = gridGeometry.getBoxConstraints(constraints);
  RenderBox child = childAfter(trailingChildWithLayout);
  if (child == null || indexOf(child) != index) {
    child = _createAndLayoutChildIfNeeded(childConstraints, after: trailingChildWithLayout);
  }
  if (child != null && indexOf(child) == index) {
    _layoutedChilds.add(index);
    _layoutChildIfNeeded(child, parentUsesSize: true);
    trailingChildWithLayout = child;
  } else if (child == null) {
    break;
  }
}

Garbage collection logic:

if (firstChild != null) {
  final int oldFirstIndex = indexOf(firstChild);
  final int oldLastIndex = indexOf(lastChild);
  final int trailingGarbage = targetLastIndex == null
      ? 0
      : (oldLastIndex - targetLastIndex).clamp(0, childCount);
  final int leadingGarbage = (firstIndex - oldFirstIndex).clamp(0, childCount);
  collectGarbage(leadingGarbage, trailingGarbage);
  _layoutedChilds.sort();
  _layoutedChilds.removeRange(0, leadingGarbage);
  _layoutedChilds.removeRange(_layoutedChilds.length - trailingGarbage, _layoutedChilds.length);
} else {
  collectGarbage(0, 0);
}

Painting optimization (avoiding unnecessary layout):

void _paintWithContext(PaintingContext context, Offset offset) {
  if (_needsLayout) return;
  _needsPaint = false;
  paint(context, offset);
}

Performance Results

After replacing the original list view with the custom waterfall flow on the main search page, the frame rate increased from ~54.7 fps to ~56.2 fps (≈1.5 fps gain). Memory usage showed a slight increase.

Future Work

Remaining challenges include implementing scrollTo(int index) , reducing memory consumption, and stabilizing reuse mechanisms. Further research will explore more robust child reuse and exposing lifecycle callbacks for exposure tracking.

FlutterPerformancelayoutlistviewsliverWaterfallFlow
Xianyu Technology
Written by

Xianyu Technology

Official account of the Xianyu technology team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.