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: