Flutter — PageView & Zoom transition

Tony Owen
3 min readDec 9, 2018

--

I was given a design to prototype in Android at work. Circle images that snap to the centre, and zoom into place.

In Android a ViewPager with a whole host of libraries, or a RecyclerView will do the trick, but I was curious. How can Flutter handle it?

As you can see from the GIF above, it handles it just fine. But I did have to play for quite a while to get it there.

Lets start with the circle cards:

Card(
elevation: 4,
clipBehavior: Clip.antiAlias,
shape: CircleBorder(side: BorderSide(color: Colors.grey.shade200, width: 5)),
child: Image.asset(
"assets/your_image.png",
fit: BoxFit.cover,
),
)

It’s really easy. Add a card, set its shape to a circle and add a border color / width. Inside the card add the image.

Next the pager:

Initially I just wanted to get the circles snapping, and showing half when next on screen. Simply adding a PageView will not achieve this. We also need to provide a PageController and set the viewportFraction setting this to 0.5 achieved what I was looking for.

Initialise the PageController and then add it to the PageView :

PageView.build(
controller: pageController
itemBuilder: (context, index){
...
}
)

We’re getting close, but what about the zoom effect?

For this, first I searched Google, found a library, didn’t work as I needed. Then I found a few blogs, they were doing more complex animations. But they gave me the starting point NotificationListener

Adding a NotificationListener<ScrollNotification> with the PageView as its child, will deliver fine detailed scrolling information.

NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification){
debugPrint(scrollNotification.toString());
},
child: PageView.build(
controller: pageController
itemBuilder: (context, index){
...
}
)
)

You’ll get a stream of information like this:

ScrollUpdateNotification(depth: 0 (local), PageMetrics(615.7..[414.0]..212.3), scrollDelta: 0.8587489211474804)

After trying to get my head around it for a bit, I realised … I don’t need that information. I just need to know about the scroll event. Every time the scroll notification fired, the thing I needed was pageController.page which returns a double 0.2 we’re near position 0, 0.9 we’re near position 1… etc.

So I created a variable called page and used that in state.

onNotification: (ScrollNotification notification) {
if (notification is ScrollUpdateNotification) {
setState(() {
page = pageController.page;
});
}
},

Now we know where we are in the scroll, time to zoom:

I had created a method which would return the circle card, this method took the image assets string, and a scale double.

Widget circleOffer(String image, double scale) {

return Align(
alignment: Alignment.bottomCenter,
child: Container(
margin: EdgeInsets.only(bottom: 10),
height: PAGER_HEIGHT * scale,
width: PAGER_HEIGHT * scale,
child: Card(
elevation: 4,
clipBehavior: Clip.antiAlias,
shape: CircleBorder(side: BorderSide(color: Colors.grey.shade200, width: 5)),
child: Image.asset(
image,
fit: BoxFit.cover,
),
),
),
);
}

It adjusts the height and width, based on scale.

To calculate the scale, I used this line. It works well enough for me, I’m sure a good mathematician could do much better:

final scale =
max(SCALE_FRACTION, (FULL_SCALE - (index - page).abs()) + viewPortFraction);

So now in the builder for our PageView we call to get circleOffer with the calculated scale, and the animation will work.

Here’s a gist of the full class:

--

--

Tony Owen
Tony Owen

Written by Tony Owen

Flutter Fan Boy & Android Developer

Responses (3)