How Slivers are made — Part 1

Tomek Polański
Nov 24 · 4 min read

The best way to learn how to write your own Sliver is to know how existing Slivers are made.

I would split Slivers into groups by their complexity:

Easy

Single child Slivers

Medium

Slivers with one child that provide a special effect

Hard

Slivers containing multiple children

To understand Slivers, you need to understand Sliver constraints and geometry.

SliverConstraints & SliverGeometry

Usually, you are using Widgets that are based on RenderBox — to layout them on-screen, BoxConstraints are passed to RenderBox and its size is calculated.

Slivers are more complicated — RenderBox widgets do not care where there are on the screen, but slivers do. They need to know where they are in the scrollable space.

Slivers use SliverConstraints (RenderBox uses BoxConstraints) as inputs and calculate SliverGeometry (RenderBox calculates size).


Writing your first sliver: SliverToBoxAdapter

SliverToBoxAdapter simply adapts Widgets that are based on RenderBox, to a Sliver that you can use in a scroll view.

Every Sliver consists of aWidget and aRenderSliver.

For SliverToBoxAdapter, the widget part is pretty simple — the crucial part is creating RenderSliver, in this case, RenderSliverToBoxAdapter.

class SliverToBoxAdapter extends SingleChildRenderObjectWidget {  const SliverToBoxAdapter({
Key key,
Widget child,
}) : super(key: key, child: child);
@override
RenderSliverToBoxAdapter createRenderObject(BuildContext context) => RenderSliverToBoxAdapter();
}

The interesting part is RenderSliverToBoxAdapter which has only one method performLayout. Let’s diagnose it line by line:

@override
void performLayout() {
if (child == null) {
geometry = SliverGeometry.zero;
return;
}

In case there is no child, we skip all the calculations.


child.layout(constraints.asBoxConstraints(), parentUsesSize: true);

We perform layout on the child’s using SliverConstraints, we are converted into BoxConstraints.

We specify that the parent uses a child’s size — if the child resizes, then the sliver itself will redo layout calculation.


double childExtent;
switch (constraints.axis) {
case Axis.horizontal:
childExtent = child.size.width;
break;
case Axis.vertical:
childExtent = child.size.height;
break;
}

Depending on the orientation of the sliver, we calculate childExtent which is the space that the child should take — if the list is vertical, then it’s child’s height.


final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: childExtent);

Here we are calculating the visible child’s size. In case the child is only 50% visible, this value will be 50% of childExtend’s.

This value is assigned to Geometry.paintExtent (interactive example)


final double cacheExtent = calculateCacheOffset(constraints, from: 0.0, to: childExtent);

To understand cacheExtent we need to talk about viewport caching.

Slivers (yellow), cache area (green), visible area (red)

In the image

  • The yellow space within the black frame is area populated by all the Slivers.

Slivers in the yellow area are not rendered as that would be wasteful.

When scrolling the visible area (red) we need to layout and paint not visible Slivers — we need to give them enough time for the necessary calculation — therefore we start to load them a bit earlier. Whenever a Slivers enters the cache area (green), it will be laid out even thou they are not yet visible.

The Geometry.cacheExtent (interactive example) property states how much of the sliver is in the cache (green) area.


geometry = SliverGeometry(
scrollExtent: childExtent,
paintExtent: paintedChildSize,
cacheExtent: cacheExtent,
maxPaintExtent: childExtent,
hitTestExtent: paintedChildSize,
hasVisualOverflow: childExtent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0,
);

setChildParentData(child, constraints, geometry);

Applies geometry to the underlying render object.


Coming up

In the next article, we will be implementing more complex SliverFillRemaining.


* In RenderBox you need to return intrinsic min/max dimensions but that we can simplify the concept for the purpose of this article

Flutter Community

Articles and Stories from the Flutter Community

Tomek Polański

Written by

Passionate mobile developer. One thing I like more than learning new things: sharing them

Flutter Community

Articles and Stories from the Flutter Community

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade