Thanos Snap Effect in Flutter
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 🙂
. . .
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
CustomPainter and for each layer do
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!
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
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
. . .
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.
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!
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!
Check out the videos explaining how I implemented awesome SY Expedition Travel animation by Anton Skvortsov! In the following videos I will show you the whole process of creating a UI challenge in Flutter.
Parallax effects are awesome. Having elements move in the different speed during scrolling can easily provide the unique feeling for the application and they can make the user think that your app is well-polished. In this post, I will try to achieve parallax effect using PageView, Transforms, Alignments and some basic math.
Flutter makes beautiful animations easy. That’s the fact. What is also the fact is that we as developers are scared to push our apps to the next level by adding those minor animations that make the app beautiful instead of just pretty. Let’s change it together! In this post, I’d like to show you how we can add a ripple effect that may make your client say “I like that!”.