staggered_flow_grid 1.0.1 copy "staggered_flow_grid: ^1.0.1" to clipboard
staggered_flow_grid: ^1.0.1 copied to clipboard

A high-performance, zero-dependency Flutter widget that renders children in a staggered waterfall grid layout using a custom shortest-column RenderObject.

staggered_flow_grid #

pub.flutter-io.cn License: MIT Platform

A high-performance, zero-dependency Flutter widget that renders children in a staggered (Pinterest / waterfall) grid layout.

Built with a pure custom RenderBox — strictly no external packages required.


Features #

  • Shortest-column algorithm — each child is placed in the column with the smallest current height, producing a balanced waterfall effect.
  • Zero dependencies — only flutter/rendering.dart.
  • Varying aspect ratios — children self-size vertically; no height hints needed.
  • RTL support — column ordering automatically mirrors in right-to-left locales via the ambient Directionality widget.
  • SingleChildScrollView compatible — computes its own intrinsic height.
  • computeDryLayout support — correct intrinsic sizing inside IntrinsicHeight / Column parents.
  • All platforms — Android, iOS, Web, macOS, Windows, Linux.

Installation #

# pubspec.yaml
dependencies:
  staggered_flow_grid: ^1.0.0
flutter pub get

Quick start #

import 'package:staggered_flow_grid/staggered_flow_grid.dart';

StaggeredFlowGrid(
  crossAxisCount: 2,
  mainAxisSpacing: 8,
  crossAxisSpacing: 8,
  children: [
    Container(color: Colors.red,    height: 120),
    Container(color: Colors.green,  height: 200),
    Container(color: Colors.blue,   height: 160),
    Container(color: Colors.yellow, height: 100),
  ],
)

API #

StaggeredFlowGrid #

Parameter Type Default Description
crossAxisCount int Required. Number of columns (≥ 1).
mainAxisSpacing double 0.0 Vertical gap between items in the same column.
crossAxisSpacing double 0.0 Horizontal gap between adjacent columns.
textDirection TextDirection? null Column order; defaults to ambient direction.
children List<Widget> [] Child widgets of any height.

Column width is calculated as:

columnWidth = (totalWidth − (crossAxisCount − 1) × crossAxisSpacing) / crossAxisCount

Usage examples #

Inside SingleChildScrollView #

SingleChildScrollView(
  child: Padding(
    padding: const EdgeInsets.all(12),
    child: StaggeredFlowGrid(
      crossAxisCount: 3,
      mainAxisSpacing: 6,
      crossAxisSpacing: 6,
      children: myWidgets,
    ),
  ),
)

Inside a Sliver scroll view #

CustomScrollView(
  slivers: [
    SliverAppBar(title: const Text('Gallery')),
    SliverToBoxAdapter(
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: StaggeredFlowGrid(
          crossAxisCount: 2,
          mainAxisSpacing: 8,
          crossAxisSpacing: 8,
          children: myWidgets,
        ),
      ),
    ),
  ],
)

Network images with varying aspect ratios #

StaggeredFlowGrid(
  crossAxisCount: 2,
  mainAxisSpacing: 8,
  crossAxisSpacing: 8,
  children: imageUrls.map((url) =>
    ClipRRect(
      borderRadius: BorderRadius.circular(8),
      child: Image.network(url, fit: BoxFit.cover),
    ),
  ).toList(),
)

RTL layout #

Column ordering automatically mirrors when your app runs in a right-to-left locale (e.g., Arabic, Hebrew) via the ambient Directionality. You can also override it explicitly:

StaggeredFlowGrid(
  crossAxisCount: 3,
  textDirection: TextDirection.rtl, // explicit override
  children: myWidgets,
)

Performance tip — RepaintBoundary #

Wrap expensive children in RepaintBoundary to isolate their repaint regions:

StaggeredFlowGrid(
  crossAxisCount: 3,
  children: myHeavyWidgets
      .map((w) => RepaintBoundary(child: w))
      .toList(),
)

Architecture #

lib/
├── staggered_flow_grid.dart          ← public barrel export
└── src/
    ├── render_staggered_flow_grid.dart   ← RenderBox + algorithm
    └── staggered_flow_grid.dart          ← MultiChildRenderObjectWidget

Layout algorithm (Shortest-Column / Waterfall) #

1. columnWidth = (availableWidth - (C-1) × crossAxisSpacing) / C
2. columnHeights = [0.0, 0.0, ... × C]
3. For each child (in insertion order):
   a. shortestCol = argmin(columnHeights)
   b. x = _xOffset(shortestCol, columnWidth, totalWidth, textDirection)
   c. y = columnHeights[shortestCol] == 0
              ? 0
              : columnHeights[shortestCol] + mainAxisSpacing
   d. Lay out child with BoxConstraints.tightFor(width: columnWidth)
   e. childParentData.offset = Offset(x, y)
   f. columnHeights[shortestCol] = y + child.size.height
4. totalHeight = reduce(max, columnHeights)

Time complexity: O(n × C) per layout pass, where n = child count and C = column count. For typical column counts (2–5), the linear column scan is cache-friendly and faster in practice than a heap-based approach.


Limitations #

Limitation Workaround
Not lazy — all children are laid out even if off-screen For very large lists (1000+ items), use a SliverList with a custom delegate, or virtualise with ListView.builder + StaggeredFlowGrid in chunks.
Requires a finite cross-axis constraint Wrap in SizedBox, Expanded, or any width-constrained parent.

Platform support #

Android iOS Web macOS Windows Linux

Contributing #

  1. Fork the repo and create a feature branch.
  2. Run flutter analyze and flutter test — both must pass with no issues.
  3. Open a pull request with a clear description and test coverage.

License #

MIT — see LICENSE.

0
likes
160
points
12
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A high-performance, zero-dependency Flutter widget that renders children in a staggered waterfall grid layout using a custom shortest-column RenderObject.

Repository (GitHub)
View/report issues

Topics

#grid #layout #staggered-grid #waterfall #masonry

License

MIT (license)

Dependencies

flutter

More

Packages that depend on staggered_flow_grid