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 🙂
TLDR: Thanos effect in Flutter. Here’s the package >>Snappable<<.
. . .
Strategy
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 Flutter Web
Flutter Web is getting more mature every day. If you want to accept payments using Stripe Checkout in your Flutter web application, this article is just for you!
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!
Water Drop effect in Flutter
In this post, I’d like to share with you how to achieve a water drop effect in Flutter. Why would you spend your time learning such skill? Well, I have no idea but it looks cool so let’s get started! ?
How to Snap with a Key with multiple items by listview?
Hey, sorry for the late reply.
I guess the easiest way is to just have a list of Keys and iterate by them.
thanks you.
@@marcin_szalek:disqus You have an example of this?, because I can’t do it. Tks