flexbox_layout 3.0.0
flexbox_layout: ^3.0.0 copied to clipboard
CSS Flexbox layouts for Flutter: justified photo galleries, Pinterest-style masonry (waterfall) grids, and pinch-to-zoom scalable lists with high-performance sliver rendering.
Changelog #
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
3.0.0 - 2026-06-12 #
Added #
Flexbox.clipBehavioris now honored: overflowing children are clipped byRenderFlexboxwhen a clip is requested (previously the parameter had no effect).- Horizontal
scrollDirectionsupport forFlexboxList,SliverFlexbox,DynamicFlexboxListandSliverDynamicFlexbox: child constraints and aspect-ratio orientation now follow the scroll axis. lastChildLayoutTypeBuilderis now honored bySliverDynamicFlexbox: indices with a non-noneLastChildLayoutTypeget a dedicated full cross-axis row sized to their natural extent (load-more footers).RenderFlexboxreports a baseline, so aFlexboxcan participate in baseline-aligned rows, and intrinsic main-axis sizes now includemainAxisSpacing.FlexboxScaleController.gridModeTransitionBandandFlexboxScaleController.gridBlend: continuous morphing between the aspect-ratio layout and the uniform 1:1 grid while pinching, instead of a hard switch atgridModeThreshold.FlexboxScaleController.focalPoint: the pinch gesture's focal point, meant to be read on demand during gestures (e.g. to anchor the zoom around the user's fingers).SliverFlexboxDelegateWithDirectExtent.gridBlend: 1:1↔aspect-ratio morph control for the smooth zoom pipeline, typically driven byFlexboxScaleController.gridBlend.SliverFlexbox.anchorOffsetFraction: when the flexbox delegate relayouts (e.g. during pinch zoom), the row at this viewport fraction stays visually stable viaSliverGeometry.scrollOffsetCorrection; the anchor is re-based by same-frame scroll deltas so flings and zooms compose.SliverScalableFlexbox.aspectRatios: per-child aspect ratios for non-uniform photo layouts in the packaged pinch-to-zoom widget.SliverDynamicFlexboxnow emits aSliverGeometry.scrollOffsetCorrectionwhen rows above the viewport resize, so visible content no longer jumps.MasonryFlexboxListandSliverMasonryFlexbox: a Pinterest-style masonry layout that packs each item into the currently shortest column (ties broken leftmost), withSliverMasonryFlexboxDelegateWithFixedCrossAxisCountandSliverMasonryFlexboxDelegateWithMaxCrossAxisExtentcolumn strategies.- Three per-item sizing modes for masonry, mixable within one list:
childAspectRatioBuilder(extent from a known ratio),childMainAxisExtentBuilder(explicit pixels), and a measured fallback for items whose builders return null (tight-cross / loose-main layout with debounced, threshold-gated updates). SliverMasonryFlexboxDelegate.fullSpanBuilder: masonry items that span all columns (section headers, banners, load-more footers); columns equalize below a full-span item.- Masonry performance: positions come from a checkpointed placement cache, so steady scroll frames only touch attached children (builder-sized children are laid out exactly once), deep jumps in the builder-sized modes do not build the children in between, and placement shifts above the viewport are absorbed by a scroll-correction anchor.
SliverMasonryFlexbox.anchorOffsetFraction: the viewport fraction at which the masonry scroll-correction anchor is sampled (0.0 leading edge, 0.5 center), so placement shifts keep the item at that position stable — the masonry counterpart ofSliverFlexbox.anchorOffsetFraction.SliverScalableMasonryFlexbox: pinch-to-zoom masonry driven by aFlexboxScaleControllerwhosecurrentExtentis the desired max column width. The continuous pinch extent is quantized at the widget layer into a discrete column count — extent changes within one band reuse the same delegate instance (zero relayout) and each count flip re-anchors around the gesture's focal point.
Changed #
- Raised the minimum Flutter SDK to 3.32.0 and Dart SDK to 3.8.0.
FlexboxScaleController.modedirection is fixed:FlexboxScaleMode.grid1x1now applies at small extents (currentExtent <= gridModeThreshold), so zooming items down past the threshold lands in the dense square grid like Google Photos. The previous behavior was inverted.SliverScalableFlexboxwas rebuilt on the smoothSliverFlexboxDelegateWithDirectExtentpipeline (it previously usedSliverFlexboxDelegateWithMaxCrossAxisExtent, which jumped between column counts while zooming). It is now aStatefulWidgetand listens to the full controller so fill-factor and grid-morph changes propagate every frame.SliverFlexboxDelegateWithDirectExtentnow lays out lazily — O(visible rows · log n) per frame instead of materializing every row — so pinch zoom stays smooth with thousands of photos. Aspect-ratio prefix sums are cached peraspectRatioslist instance: reuse the same list instance across frames and pass a freshly built list only when the underlying data actually changes (documented on the delegate).- Flex resolution in
RenderFlexboxis now purely arithmetic: static children are laid out exactly once per pass, and flexed/stretched children at most two to three times (previously up to four or more passes with O(n²) freeze loops). - Changing
Flexbox.clipBehaviornow only repaints instead of forcing a relayout. - The minimum intrinsic main size of a wrapping
Flexboxnow reports the largest single child (a wrapping container can always break lines) instead of the single-line sum;maxLines-capped containers keep the sum. - Optimized
RenderFlexboxdry layout and layout scratch storage to avoid real child layout during dry measurement and reduce per-layout allocations. - Reworked
RenderSliverDynamicFlexboxrow caching: newly measured trailing children no longer invalidate already-built rows, cached rows are reused as a superset while scrolling backward, and getter-provided aspect ratios are re-validated only for attached children. This removes O(n) row rebuilds per scroll frame on long lists in both scroll directions. - Further
SliverDynamicFlexboxscrolling work: deep scroll jumps re-anchor from the warm row cache instead of rebuilding rows from index 0, insert batches are sized from measured row extents (no same-frame child create/destroy churn), the row cache extends in place while scrolling (previously quadratic copying), configuration-equal delegate rebuilds keep the row cache, and children whose aspect ratios come from a getter are laid out once instead of twice. - A mid-list aspect-ratio change now repairs the row cache locally: only the affected rows are re-packed and the remaining rows are reused with shifted offsets, instead of rebuilding every row after the change. Debounced measure-mode changes take the same partial path by updating the cached ratio in place instead of fully invalidating the row cache.
SliverDynamicFlexboxsteady-scroll hot path: the positioning loop now binary-searches the first attached row instead of scanning from row 0 (O(log n) instead of O(scroll depth) per frame), and cold deep jumps with a getter pack rows index-only — without building a single child — before anchoring (a 30k-pixel cold jump went from ~423 child builds to under 60).SliverDynamicFlexbox.scrollExtentis now estimated from the exact cached row offsets plus a basis-locked per-item average, so the scrollbar no longer jitters while scrolling long unmeasured lists (adjacent-frame estimate drift dropped from tens of thousands of pixels to under one row).- Getter-provided aspect ratios are re-verified with the same
maxAspectRatioChecksPerLayout/aspectRatioCheckIntervalthrottles as measure-mode probing (rotating through attached children across frames), with one unthrottled verification pass after every delegate swap. - Scroll-offset corrections below one millipixel are suppressed in both sliver render objects, avoiding floating-point-noise double layouts at extreme scroll offsets.
SliverFlexboxDelegateWithFixedCrossAxisCountandSliverFlexboxDelegateWithMaxCrossAxisExtentnow produce aRegularSliverFlexboxLayoutthat answers all layout queries in O(1) arithmetic, so pinch-zoom extent changes and pagination growth no longer materialize every line on each pass; the previously unbounded per-child geometry cache was removed in its favor.DimensionResolverMixinnow coalesces bursts of resolved dimensions into a single rebuild and defers notification so synchronous resolutions are safe during build.FlexboxScaleControllersorts and validatessnapPointsso zoom stepping behaves predictably with unsorted input.- Kept scroll cache configuration compatible with Flutter 3.32 while silencing newer-SDK deprecation noise.
- Documented the item entrance animation system (README section, stagger
batch model, and the stable animation id contract): lists that mutate
anywhere but the tail must supply data-derived ids via
animationIdBuilder/FlexboxItemTransition.animationId.
Removed #
- Dead
FlexLinemembers:left,top,right,bottom,dividerLengthInMainSize,goneItemCount,sumCrossSizeBefore,itemCountNotGoneandupdatePositionFromView(). They were never wired to the layout pipeline.
Fixed #
FlexboxScaleController.animateToExtentnow settles exactly on its target extent when the spring completes (previously it rested within the solver tolerance of the target, which could leavezoomIn/zoomOutre-targeting the snap point they had just settled on).- Entrance animations (
FlexboxItemTransition) no longer snap to completion when an ancestor rebuilds mid-animation, and the child element subtree is preserved after the animation completes (no state loss or flash). - Auto-mode entrance stagger re-anchors per attach batch: items attached by later frames (steady scroll-in) animate immediately instead of inheriting the initial batch's maximum clamped delay, and deep jumps cascade 0, 1, 2, ... instead of bursting simultaneously after a shared delay.
- Entrance animation history is retained LRU-style: scrolling back over an
item refreshes its id, so bounded histories (
maxTrackedAnimationIds) on very long feeds no longer replay recently revisited items. - Enabling an entrance animation on an already-built item (manual-mode enqueue) no longer re-inflates the child element subtree, so child state survives the wrapper transition.
FlexboxItemTransitioncreates itsAnimationControllerlazily — items that attach without animating (the dominant recycled scroll path) allocate no controller and no ticker — and pending stagger delays are cancellable timers, so disposed items are released immediately.DimensionResolver.resolveCustomcancel/re-resolve cycles can no longer deliver stale results over newer ones (per-key generation tokens).RenderSliverDynamicFlexboxinvalidates index-keyed caches when the child list shrinks, so removed trailing items can no longer leave stale row geometry behind, and a partially attached boundary row is always filled to the end of the row before layout settles (no horizontal holes).RenderSliverDynamicFlexboxalso detects child-count growth: head or mid-list insertions invalidate stale index-keyed rows (sampled getter verification keeps the common append/load-more path cache-friendly), so a prepend no longer leaves misanchored leading rows behind.- Builder delegates without
childCountnow also recover from data-source shrinkage: reaching the end below the cached row coverage drops the stale trailing caches. - Jump-scrolling in measure mode backfills leading children only down to the first index whose ratios are already cached, instead of always materializing every child to index 0.
SliverDynamicFlexbox: debounced aspect-ratio updates applied while the app is idle now schedule their own relayout frame (previously the caches were updated but layout waited for the next unrelated frame).alignSelf: AlignSelf.baselinenow works without container-level baseline alignment (previously it produced negative offsets).- Baselines are collected after the final layout pass, so baseline alignment is no longer stale after a flex resize.
- A flex item declaring inverted min/max constraints no longer triggers a clamp crash.
SliverFlexboxDelegateWithFlexValuesclamps the available width when spacing exceeds a narrow viewport, so degenerate configurations produce zero-width items instead of negative geometry. Grid delegates andFlexChildInfonow assert positive aspect ratios and non-negative spacing in debug builds.- Flex items keep their cross-axis min/max constraints while growing or shrinking.
getDryLayoutno longer corrupts live layout state.- Children that cannot compute a dry layout (e.g.
LayoutBuilder) are fully supported in live layout passes.
2.0.0 - 2026-02-18 #
Added #
FlexboxItemAnimationControllerwith auto/manual animation modesFlexboxItemAnimationConfigandwithFlexboxItemAnimationfor staggered entrance transitions in sliver/list buildersFlexboxScaleController.extentListenablefor extent-only UI subscriptionsmaxAspectRatioChecksPerLayoutandaspectRatioCheckIntervaloptions in dynamic flexbox delegates
Changed #
- Optimized sliver and render flexbox layout paths with delegate/layout caching
- Improved dynamic aspect-ratio probing and relayout behavior for scrolling feeds
- Updated scalable flexbox rebuilding from full controller listening to extent-only listening
- Fixed max-lines behavior and defensive layout math in flexbox delegates
- Updated and expanded test coverage for dynamic, sliver, scale-controller, and item-animation behavior
1.1.1 - 2026-01-17 #
Changed #
- Enhanced documentation across all public APIs
- Improved code comments and examples for better clarity
- Comprehensive dart documentation coverage
1.1.0 - 2026-01-15 #
Added #
FlexboxScaleController- Controller for scalable flexbox with pinch-to-zoom support- Responsive scaling that follows pinch gestures in real-time
- Smooth snap animation with spring physics
- Mode switching between 1:1 grid and aspect ratio modes
- Velocity-based momentum for natural gesture continuation
- Configurable snap points, min/max extent, and grid mode threshold
FlexboxScaleModeenum - Display mode for scalable flexbox (grid1x1,aspectRatio)SliverFlexboxDelegateWithDirectExtent- Delegate with direct extent control- Supports smooth continuous scaling without discrete column jumps
- Fill factor interpolation for smooth layout transitions
- Ideal for Google Photos-like pinch-to-zoom experience
SliverScalableFlexbox- Scalable sliver widget with pinch-to-zoom support- Automatically rebuilds when controller's extent changes
- Integrates with CustomScrollView for seamless scrolling
Features #
- Pinch-to-zoom gesture support for flexbox layouts
- Spring physics animations for smooth snap transitions
- Fill factor animation for smooth layout state transitions
- Automatic display mode switching based on zoom level
- Double-tap to zoom between predefined levels
- Programmatic zoom control with
zoomIn()andzoomOut()methods - Velocity-based gesture momentum for natural feel
Example #
- Added
ScalableFlexboxPagedemonstrating pinch-to-zoom gallery - Network image gallery example with multiple image sources (Nekosia, Yande, Zerochan)
1.0.0 - 2026-01-14 #
Added #
- Initial release of the Flutter Flexbox library
Flexboxwidget - full CSS Flexbox layout implementationFlexItemwidget - wrapper for flex item propertiesFlexboxList- scrollable list with flexbox layout capabilitiesSliverFlexbox- sliver version for CustomScrollViewDynamicFlexboxList- auto-sizing flexbox list for variable contentSliverDynamicFlexbox- dynamic sliver for CustomScrollViewDimensionResolver- utility for measuring child dimensions- Multiple delegate types for different layout scenarios:
SliverFlexboxDelegateWithFixedCrossAxisCountSliverFlexboxDelegateWithMaxCrossAxisExtentSliverFlexboxDelegateWithAspectRatiosSliverFlexboxDelegateWithDynamicAspectRatiosSliverFlexboxDelegateWithFlexValuesSliverFlexboxDelegateWithBuilder
Features #
- Full CSS Flexbox layout algorithm implementation
- Support for
flexDirection,flexWrap,justifyContent,alignItems,alignContent - Flex item properties:
order,flexGrow,flexShrink,alignSelf,flexBasisPercent - Size constraints:
minWidth,minHeight,maxWidth,maxHeight wrapBeforeproperty for explicit line breaks- Main axis and cross axis spacing
- Item recycling for efficient scrolling
- Dynamic sizing based on aspect ratios
- Text direction support (LTR/RTL)
- Max lines limitation for wrapped layouts
- Configurable thresholds for layout updates:
aspectRatioChangeThreshold- minimum aspect ratio change to trigger updatecrossAxisExtentChangeThreshold- viewport width change to clear cache
FlexboxListextendsBoxScrollViewfor better framework integration- Comprehensive Dart documentation with examples for all public APIs
Example #
- Complete example app with interactive playground
- Demonstrations of all major features
- Network image gallery example