Simple Parallax
Parallax widgets for Flutter, in pure Dart. Two modes, either axis, any ImageProvider, and no
dependencies beyond the Flutter SDK.
Container mode above, item mode below; scrolling down on the left, sideways on the right.
Install
flutter pub add simple_parallax
Requires Flutter 3.22 or later.
Container mode
One background drifting behind a scrolling area. autoSpeed derives the speed from the real scroll
extent, so the background uses exactly the travel overscan gives it and never runs out of image:
SimpleParallaxContainer(
image: const AssetImage('assets/images/background.webp'),
autoSpeed: true,
overscan: 1.5,
child: Column(children: items),
);
| Parameter | Default | Effect |
|---|---|---|
image |
required | Any ImageProvider: asset, network, file or memory. |
child |
required | The scrolling content, laid out as a single box sliver. |
slivers |
required | The scrolling content as slivers, on the .slivers constructor. |
scrollDirection |
Axis.vertical |
The axis the content scrolls and the background drifts along. |
speed |
0.3 |
Background travel per pixel scrolled. Ignored when autoSpeed is set. |
autoSpeed |
false |
Derives the speed from the scroll extent. |
overscan |
1.5 |
How much larger than the viewport the background is drawn along the scroll axis. |
height |
null |
Forces the viewport height instead of using the constraints. |
width |
null |
Forces the viewport width instead of using the constraints. |
fit |
BoxFit.cover |
How the background fills its layer. |
alignment |
Alignment.center |
How the background sits inside its layer. |
Slivers
The container is a CustomScrollView, and child is put in a single box sliver. Use the
.slivers constructor instead to hand it the slivers yourself, so the content builds as it scrolls
and other slivers can ride over the background:
SimpleParallaxContainer.slivers(
image: const AssetImage('assets/images/background.webp'),
autoSpeed: true,
slivers: <Widget>[
const SliverAppBar(title: Text('Chapters'), floating: true),
SliverList.builder(
itemCount: 500,
itemBuilder: (BuildContext context, int index) =>
ListTile(title: Text('Chapter $index')),
),
],
);
Everything else behaves the same: the background still drifts along scrollDirection, and
autoSpeed still reads the real scroll extent. On a long list, prefer a fixed speed: autoSpeed
spreads the travel overscan allows over the whole extent, so the drift becomes imperceptible.
Item mode
Each block slides its own background as it crosses the viewport. The item finds the enclosing
Scrollable by itself, so it works in a ListView, a CustomScrollView, or anything else that
scrolls:
ListView(
children: <Widget>[
SimpleParallaxItem(
image: const NetworkImage('https://example.com/cover.jpg'),
height: 300,
child: const Center(child: Text('Chapter one')),
),
],
);
| Parameter | Default | Effect |
|---|---|---|
image |
required | Any ImageProvider. |
child |
null |
Content drawn over the background. |
speed |
1.0 |
Fraction of the available travel used, from 0 to 1. |
overscan |
1.5 |
How much larger than the item the background is drawn along the scroll axis. |
height |
screen height, or constraints when horizontal | Item height. |
width |
constraints, or screen width when horizontal | Item width. |
fit |
BoxFit.cover |
How the background fills its layer. |
SimpleParallaxWidget is a convenience scroll view for a list of items. It is a CustomScrollView
over one SliverList, so the blocks build as they come into view and each one is laid out across
the full cross axis, the way a ListView lays its children out.
SimpleParallaxWidget(
children: <Widget>[
const SimpleParallaxItem(image: AssetImage('assets/a.webp'), height: 300),
Container(height: 400, color: Colors.blueGrey),
],
);
Scrolling sideways
Both modes work on either axis. The container takes a scrollDirection, exactly like a
ListView:
SimpleParallaxContainer(
image: const AssetImage('assets/images/background.webp'),
scrollDirection: Axis.horizontal,
autoSpeed: true,
child: Row(children: items),
);
An item has nothing to pass: it reads the axis from the scrollable it sits in, so dropping it into a
horizontal list is enough. Give it a width there, the way you give it a height in a vertical one:
ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
SimpleParallaxItem(
image: const AssetImage('assets/images/background.webp'),
width: 300,
child: const Center(child: Text('Chapter one')),
),
],
);
SimpleParallaxWidget takes the same scrollDirection and lays its blocks out along that axis.
How it performs
Scrolling repaints the background and nothing else. In container mode the moving layer sits behind a
RepaintBoundary and only its transform is rebuilt, so your content is built once. In item mode the
background is painted by a Flow bound directly to the scroll position, which repaints without
rebuilding a single widget.
Both scroll views are CustomScrollViews, so content handed over as slivers is built only as far as
the viewport reaches.
Migrating from 0.1.x
| Before | Now |
|---|---|
imagePath: 'assets/a.webp' |
image: AssetImage('assets/a.webp') |
decal: 1.5 |
overscan: 1.5 |
SimpleParallaxItem(speed: 0.3) |
speed is a 0..1 fraction now, default 1.0 |
SimpleParallaxItem only inside SimpleParallaxWidget |
works inside any scrollable |
autoSpeed needed a GlobalKey on your child |
nothing to pass |
The package is no longer a Flutter plugin: the native platform folders are gone, and so is the
provider dependency.
Dependencies
None beyond the Flutter SDK.
Example
example/ is one app with four screens, one per combination: container mode and item mode, each
vertical and horizontal.
cd example && flutter run
Tests
flutter test
License
MIT, see LICENSE.
Libraries
- simple_parallax
- Parallax widgets for Flutter, in pure Dart and with no dependencies.
