Thanos Snap Effect in Flutter

by Aug 6, 2019Flutter4 comments

Have you ever woken up and thought “How awesome would it be to create Thanos snap effect in Flutter”? Probably not. Well… following Google’s Easter Egg I did. 🙂 And I’ve found out that no one did it in Flutter yet so why not give it a try! In this post, I will explain how I created a Thanos snap effect coming from the Avengers: Infinity War movie. Enjoy 🙂

TLDR: Thanos effect in Flutter. Here’s the package >>Snappable<<.

. . .


I am going to take the same approach Red Stapler took in his awesome video, which explains how to get Thanos snap effect in JS. He explains the idea very well and my Flutter implementation is highly based on his work.

To put it simple, the goal is to:

1. Convert a Widget to Image
2. Split the image to multiple buckets
3. Display each layer separately
4. Animate every layer of the image

. . .

1. Convert a Widget to image

Since I want this package to work on any widget, not only image, we need to convert that widget to an image (to get some pixels data etc.). This is fairly easy. We just need to use RepaintBoundary widget and use its RenderObject to get image data.

We will also use image package by Brendan Duncan to make it easy to work on actual image.

. . .

2. Split the images

To create Thanos snap effect, we need to split pixels of the image to multiple buckets. Each bucket will have some part of the pixels and will be animated in its own pace and direction. Technically, we could just go through every pixel and randomly find a bucket for that pixel. However, as Red Stapler has noticed, we want to give some sort of directionality, so that the animation doesn’t fade away in place but goes smoothly from top to bottom. To achieve it, we are introducing weights, which will cause higher pixels to be more likely to end up in the first buckets and lower pixels in the lasts.

You can see how first images are empty (black) on the bottom, and the last images are empty at the top. This will cause top part of an image to disappear faster than the bottom part.

Here is the code:

. . .

3. Display each layer separately

Now we need to decide on how we want to display the layers. I came up with 3 solutions:

1. Create a swarm of Containers with size of 1×1 pixel and display them using Positioned
2. Use CustomPainter and for each layer do drawPoints on Canvas
3. Create new PNG images, each having only pixels from its layer

The first approach just didn’t work for me. Hundreds of thousands of Containers didn’t render even once.

The second approach worked pretty nice, even in Debug mode, it loaded quickly, it animated smoothly and it looked promising. What’s more, the first draft of the post you’re reading right now even used the second option. It was until the time I launched the app in Release mode on my phone. It turned out that this solution is really CPU consuming and I wasn’t able to get smooth animation on my LG G6 which was a dealbreaker for me.

This leaves us with option three which is creating a separate image for every layer. This solution is really smooth, it animates nicely, but has one big drawback. To encode the image, we are using image library mentioned earlier and this library’s implementation of encoding the image is slow. Really slow. It’s as slow as mobile development without Flutter. However, it’s the best we got, so we have to accept that after doing the snap, we have to wait a bit before it actually happens.

If you now how to make it fast, let me know in the comment!

Having the Uint8Lists, we can use them in Image.memory to draw images.

. . .

4. Animate images

Now it’s time to finish Thanos’ snap with the animation. To create the animation we are going to use AnimationController. Once the snap happens, we will move each layer separately using Intervals. Interval is a type of Curve that can specify in which fraction of the main animation should the (small) child animation happen. So in our case, we want each layer animation to take about half of the whole snap and we want each layer to start a short time after the previous one.

Each animation is used to calculate Offset for the layer. Each layer is being moved by Transform widget. In addition, each layer’s opacity is slowly decreasing using sinus function with Opacity widget.

. . .

5. Snap

Now lets call animationController.forward() and say bye to our widgets…

And that’s it!

I hope you enjoyed this post, if so please leave the star in the GitHub repo 🙂

You can use the Snappable plugin and play with Thanos’ power by yourself. Share your snaps in the comments below 🙂

If you have any questions or maybe you know how to optimize the process feel free to leave a comment.

Cheers 🙂

Join the newsletter!

Join the newsletter to keep track with latest posts and get my special Widget to animate views' entrances without any hassle for FREE!

Marcin Szałek

Founder of Fidev

Flutter enthusiast since Alpha release in 2017. Believes that sharing is caring, which lead him to start a technical blog dedicated fo Flutter in its early days. Loves to see beautiful designs become real apps and is willing to help make it happen. Enjoys sunny beaches far from home.

Check out other posts!

Stripe Checkout in mobile Flutter app

Stripe Checkout in mobile Flutter app

Have you ever struggled to integrate card payments into your mobile Flutter app? If so, today is your lucky day! In this post, I present how to use Stripe Checkout in the Flutter app without any hassle!

Interacting with Widgets using Framy

Interacting with Widgets using Framy

Have you ever developed a widget or a page and you wanted to make sure it works correctly in different scenarios but then it turned out that you can’t just reproduce all the cases you want to cover? Framy may solve such problems!

3D Kinetic Type Poster in Flutter

3D Kinetic Type Poster in Flutter

In this post, we’re going to learn how to create probably the most useless effect on this blog. 😀 The design is named 3D CSS Kinetic Type Poster. It was created by Pete Barr, who shared the design on dribbble and the front-end implementation on codepen. Let’s see if we can do it in Flutter as well!

Share This

Share This

Share this post with your friends!