Parallax Scrolling Effect with Flutter
A parallax scrolling effect is attained by pushing the background image very slightly toward the opposite direction of the whole list. Let’s know more about it.
Table of Contents :
Parallax Effect — Introduction
What is a Stack widget? — Explained
Parallax effect
Parallax scrolling is a website technique where we make our background move at a little slower pace than the foreground we make. This is a 3D effect as the user scrolls down the site, putting in a sense of depth and forming a more mesmeric experience while browsing.
Our eyes recognize objects close to us comparatively larger than those far away from our eyes. We perceive distant objects as if they are moving very slowly. Parallax bounds the same idea as human eyes which is a so-called optical illusion.
Flutter makes it very easy to implement the parallax effect in our apps by using a few widgets, such as Stack, Positioned, etc. We’re going to use Stack and Positioned widget in the demo project below. With the help of flutter widgets here we quickly can implement parallax scrolling.
What is a Stack widget?
It is a widget in Flutter SDK which allows us to make layers of widgets by putting one over the other.
This class is significant if you want to overlap several children simply. It is like putting objects in a single bucket one after the other(First goes in, last comes out). Similarly, if we assemble the same example pattern in a flutter, we could imagine having a few texts and an image, overlaid with a gradient and an elevated button attached at the bottom.
It gets mixed very smoothly and makes the UI much more interactive than before.
Constructor of Stack Class:
Stack(
{Key key,
AlignmentGeometry alignment: AlignmentDirectional.topStart,
TextDirection textDirection,
StackFit fit: StackFit.loose,
Overflow overflow: Overflow.clip,
Clip clipBehavior: Clip.hardEdge,
List<Widget> children: const <Widget>[]}
)
Implementation in code:
Stack(
children: [
Image.asset(
ImageUtils.roundShape,
width: double.infinity,
),
Container(
height: 51.h,
// width: 270.w,
decoration: BoxDecoration(
color: ColorUtils.color373856,
borderRadius: BorderRadius.circular(
45.r,
),
),
);
SvgPicture.asset(
ImageUtils.scanIcon,
height: 51.h,
width: 51.w,
),
],
);
The children i.e., Image, Container, and SvgPicture widgets will be overlaid one over the other. Top — > SvgPicture widget, Bottom — > Image widget.
We’re also using the Positioned widget in this. Positioned widget is a widget that controls where a child of a Stack widget is going to be positioned. For example :
Stack(
children: [
Container(
height: 250,
child: Image.network(
"https://i.pinimg.com/originals/4f/39/10/4f39103da156fb7e479abd6355932e88.jpg",
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) {
return child;
} else {
return const Center(
child: CircularProgressIndicator());
}
}, fit: BoxFit.fill),
),
Positioned(
bottom: 20,
left: 20,
child: ClipRRect(
borderRadius: BorderRadius.circular(60),
child: Image.network(
"https://scontent.fdel3-2.fna.fbcdn.net/v/t39.30808-6/301901141_372422665106709_6958811342740638978_n.jpg?_nc_cat=107&ccb=1-7&_nc_sid=09cbfe&_nc_ohc=LgtpoTtlA7QAX--bEd1&_nc_ht=scontent.fdel3-2.fna&oh=00_AT9ZAOvxE80tiN7MQ5xUQ5SVLRiLhUpQwP5egfuSNkmrAA&oe=6351DA3E",
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) {
return child;
} else {
return const Center(
child: CircularProgressIndicator());
}
},
fit: BoxFit.fill,
height: 75,
width: 80,
),
))
]
);
Here Positioned widget is for setting the fixed position of the child, i.e., the ClipRRect widget. For this, we’d be using named parameters such as top, bottom, left, right, etc. In the above example, bottom: 20 and left: 20 parameters are being used which means the child widget will be positioned this much distance from the left and bottom of the stack. Then the other parameters will occupy the rest of the distance (from top and right).
In the further demo project, we’ll be dealing with ListView widget as our Scrollable. ListView widget is the most commonly used ScrollView widget where its children get displayed one after another in the scroll direction.
Role of ListView in our demo:
In our project, the ListView widget that has a child of a Container widget is going to be responsible to form the background for all the further children of Stack
.ListView(
children: <Widget>[
Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment(0.5, 0.0),
colors: [
Color.fromRGBO(130, 0, 94, 1),
Colors.lightBlue
],
tileMode: TileMode.mirror)),
height: 1200)
],
),
When we run the application, we ought to get the screen’s output like the underneath screen capture.
Over this, we’ll add some images & texts, and then form a parallax effect using them.
Code Implementation:-
main.dart
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const ParallaxEffectScreen(),
);
}
}
ParallaxEffectScreen.dart
class ParallaxEffectScreen extends StatefulWidget {
const ParallaxEffectScreen({Key? key}) : super(key: key);
@override
State<ParallaxEffectScreen> createState() => _ParallaxEffectScreenState();
}
class _ParallaxEffectScreenState extends State<ParallaxEffectScreen> {
String? asset;
double divOne = 0;
double divFive = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: NotificationListener(
onNotification: (notify) {
if (notify is ScrollUpdateNotification) {
setState(() {
divOne += notify.scrollDelta! / 1;
divFive += notify.scrollDelta! / 5;
});
}
return true;
},
child: Stack(
children: <Widget>[
ListView(
children: <Widget>[
Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment(0.5, 0.0),
colors: [
Color.fromRGBO(130, 0, 94, 1),
Colors.lightBlue
],
tileMode: TileMode.mirror)),
height: 1200)
],
),
ParallaxText(
colour: Colors.white,
left: 170 - divOne * 3,
top: 120 + divFive,
text: "Demo of"),
ParallaxText(
colour: Colors.white,
left: 20 + divOne * 2,
top: 400 + divFive / 2,
text: "Parallex\n Scrolling"),
ParallaxImage(
left: 20,
top: 100 - divOne,
height: 200,
width: 200,
asset: "photo6",
widget: ParallaxText(
colour: const Color.fromRGBO(130, 0, 94, 1),
left: 150 - divOne * 3,
top: 20 + divOne + divFive,
text: "Demo of",
)),
ParallaxImage(
left: 200 - divOne,
top: 350 - divOne,
height: 300,
width: 300,
asset: "photo7",
widget: ParallaxText(
colour: const Color.fromRGBO(130, 0, 94, 1),
left: -180 + divOne * 3,
top: 50 + divOne + divFive / 2,
text: "Parallax\n Scrolling",
)),
ParallaxText(
colour: Colors.white,
left: divFive,
top: 720 - divOne,
text: "Be creative",
),
ParallaxImage(
left: 95,
top: 700 - divOne,
height: 400,
width: 230,
asset: "photo3",
widget: ParallaxText(
colour: const Color.fromRGBO(130, 0, 94, 1),
left: -95 + divFive,
top: 20,
text: "Be creative",
))
],
),
),
);
}
}
Here ParallaxText() is a constructor of a Custom widget made for the arrangement of texts over the screen(as they internally contain Positioned widgets). Similarly, the ParallaxImage() is a constructor of a Custom widget that will be responsible for the arrangement of Images over the screen.
ParallaxText.dart
class ParallaxText extends StatelessWidget{
const ParallaxText({
Key? key,
required this.left,
required this.top,
required this.text,
required this.colour,
}) : super(key: key);
final double left;
final double top;
final String text;
final Color colour;
@override
Widget build(BuildContext context) {
return Positioned(
left: left,
top: top,
child: Text(text, style: TextStyle(color: colour, fontWeight: FontWeight.w900, fontSize: 60.0, fontFamily:"Helvetica")
),
);
}
}
ParallaxImage.dart
class ParallaxImage extends StatelessWidget{
const ParallaxImage({
Key? key,
required this.left,
required this.top,
required this.asset,
required this.height,
required this.width,
required this.widget,
}) : super(key: key);
final double left;
final double top;
final String asset;
final double height;
final double width;
final Widget widget;
@override
Widget build(BuildContext context) {
return Positioned(
left: left,
top: top,
child: Container(decoration: BoxDecoration(image: DecorationImage(
fit: BoxFit.cover,
image: ExactAssetImage("assets/images/$asset.png")
),
borderRadius: BorderRadius.circular(12.0)),
height: height,
width: width,
child: Stack(children: <Widget>[
widget
],),)
);
}
}
Here, the Positioned widget helps its child to get positioned over the stack according to the parameters it’s receiving.
When we run the application, we ought to get the screen’s output like the underneath screen video.
Conclusion:
In this article, we’ve learned how to display a list of parallax scrolling images/texts. Now you can try it in your project and make it more fun to work with.
❤ ❤ Thanks for reading this article ❤❤
If I got something wrong? Let me know in the comments. I would love to improve.
Clap 👏 If this article helps you.
Feel free to connect with us:
And read more articles from FlutterDevs.com.
FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire a Flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! You can connect with us on Facebook, GitHub, Twitter, and LinkedIn for any flutter-related queries.
We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.