Dev Genius

Coding, Tutorials, News, UX, UI and much more related to development

Follow publication

Flutter Tutorial

Parallax Gallery View Animation in Flutter

Saurabh Pant
Dev Genius
Published in
5 min readJun 15, 2024

--

Recently, while I was viewing google photos, the memory carousel at the of the page got my attention. It was moving horizontally with a nice parallax animation from both left and right end. I really liked it and decided to give it a try in Flutter and the final result looks like as shown below

In the beginning it looked slightly tough to build but I managed to get through and let’s see how it was built.

The Conceptual part

Let’s see what are the details in this animation so as to use them in our implementation.

  • there are 2 complete images along with either one image with 85% portion visible or two images with ~42% portion is visible at any given time
  • as soon as any image hits the left side of the screen or is out to the right side 15%, its parallax starts
  • if image is entering in the left side then the image parallax is towards right and vice versa
  • there is 8 px of left margin on each image which is required to be considered in the calculations
  • lastly, scroll offset is the key for all the calculations

The Implementation

Create GalleryCard

We first create our main GalleryCard as follows

class GalleryCard extends StatelessWidget {
final String image;
final int index;
final double screenWidth;
final double scrollOffset;

const GalleryCard(
{super.key,
required this.image,
required this.index,
required this.screenWidth,
required this.scrollOffset});

@override
Widget build(BuildContext context) {
// basic calculations
final itemWidth = screenWidth / 2.85;
final itemHeight = itemWidth * 1.5;
return Container(
width: itemWidth,
height: itemHeight,
clipBehavior: Clip.antiAlias,
decoration: const BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.all(Radius.circular(24))),
child: Image.asset(
image,
fit: BoxFit.cover,
alignment: Alignment(
0,
0
),
),
);
}
}

Now we add this card few times in a row and put that row in a horizontal scrollview as follows

@override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
return SingleChildScrollView( // Scrollview
scrollDirection: Axis.horizontal,
controller: controller,
child: Container(
color: Colors.white,
child: Row( // Row containing our Gallery card
children: [
...List.generate(images.length, (index) {
return Padding(
padding: const EdgeInsets.only(left: 8.0),
child: GalleryCard(
screenWidth: width,
scrollOffset: scrollOffset,
image: images[index],
index: index,
),
);
})
],
),
),
);
}

If run our code upto this step then we would get something as shown

No parallax

Card Dimensions Calculations

We’ll set up the left side parallax by doing the calculations as per the conceptual section as follows

final marginOffset = 8 * (index + 1);
final lowerRange = (index * itemWidth) + marginOffset;
final highRange = ((index + 1) * itemWidth) + marginOffset;
  • calculating the margin offset for each card. this is the padding that we provided to each card from left. This margin is to be considered otherwise the movement for each card will not be consistent.
  • then we calculate the lower (left) and high (right) position of each card

Left Side Parallax

Using the card dimensions, we can calculate few values for left side as follows

// left side calculations
final isInLeftSideRange = scrollOffset >= lowerRange && scrollOffset <= highRange;
final leftSideOffset = highRange - scrollOffset;
final leftSideDivisor = index > 0 ? (lowerRange - marginOffset) / index : highRange - marginOffset;
  • isInLeftSideRange : indicates if the scrolling offset lies within this card’s area. If yes then it means the card has started entering the left side of the screen.
  • leftSideOffset : value which indicates how much the card has been moved into the left side
  • leftSideDivisor : a denominator to calculate the percentage value for parallax

Now we calculate the fraction offset which indicates the percentage of the card into the left side as follows

final fractionOffset = (offset < 0 ? 1 : leftSideOffset) / leftSideDivisor;

Finally we can calculate the value for our parallax as follows

final leftParallaxPercent = isInLeftSideRange ? (1 - fractionOffset) : 0.0;

We can apply this percent into the x coordinate of the image Alignment as follows

child: Image.asset(
image,
fit: BoxFit.cover,
alignment: Alignment(
leftParallaxPercent,
0
),
),

If we run our code upto this step, we would get our gallery view with only left side parallax as follows

Right Side Parallax

Similarly as we found out the left side parallax percentage, we can find out the right side one but with slight updates.

// right side calculations
final rightSideScrollOffset = lowerRange - scrollOffset;
final rightSideOffsetThreshold = screenWidth - (itemWidth * 0.85);
final isInRightSideRange = rightSideScrollOffset > rightSideOffsetThreshold && rightSideScrollOffset < screenWidth;
final rightSideOffset = rightSideScrollOffset - rightSideOffsetThreshold;
final rightSideDivisor = screenWidth - rightSideOffsetThreshold + marginOffset;
  • rightSideScrollOffset: value indicate the amount of card scrolled into the right side of the screen
  • rightSideOffsetThreshold: maximum value upto which parallax to be shown when scrolling from right to left
  • isInRightSideRange: if the card has entered into the right side of the screen when scrolling from left to right
  • rightSideOffset: amount by which the card is visible when scrolling from right to left
  • rightSideDivisor: a denominator to calculate the percentage value for parallax

Final caluclations

As we already have left side values, we can adjust the final calculation with both left and right side values as follows

// final calculations
final offset = isInLeftSideRange ? leftSideOffset : rightSideOffset;
final divisor = isInLeftSideRange ? leftSideDivisor : rightSideDivisor;
final fractionOffset = (offset < 0 ? 1 : offset) / divisor;
final leftParallaxPercent = isInLeftSideRange ? (1 - fractionOffset) : 0.0;
final rightParallaxPercent = isInRightSideRange ? fractionOffset : 1.0;
final leftPercent = -leftParallaxPercent;
final rightPercent = rightParallaxPercent;

Replacing the above values in our image widget alignment as follows

child: Image.asset(
image,
fit: BoxFit.cover,
alignment: Alignment(
isInLeftSideRange ? leftPercent : (isInRightSideRange ? rightPercent : 0.0),
0
),
),

Running our code now would give us our final result.

Bamn! We’ve successfully created our Parallax Gallery animation from both end.

Hope you’ve enjoyed it! You can watch the speed code video on YouTube

Or can checkout the repo below

Comment down to let me know if you have any inputs on this.

That is all for now! Stay tuned!

Connect with me (if the content is helpful to you ) on

Until next time…

Cheers!

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Published in Dev Genius

Coding, Tutorials, News, UX, UI and much more related to development

Written by Saurabh Pant

App Architect (Native & Flutter) | TataDigital | Mentor | Instructor @Droidcon | Youtube @_zaqua | Writer @Flutter Community

No responses yet