Scanning animation in Flutter

Ramankit Singh
3 min readAug 25, 2019

--

Using AnimatedWidget & AnimationController.

The animation here is a bit choppy as its GIF.

For creating a scanning animation we have to first create the AnimatedWidget which will rebuild frequently and update the different position of the scanning gradient whenever the value of the animation is changed. We can stack this animated widget on top of any other widget to provide the scanning effect. Here I am using Image Scanning as an example.

Let's break down this into by knowing various widgets involved in achieving this.

  • AnimatedWidget
  • AnimationController
  • SingleTickerProviderStateMixin

Animated Widget

A widget that rebuilds when the given Listenable changes value.

Here the AnimatedWidget will be the ImageScannerWidget which will return the Container with LinearGradient decoration and it will be inside Positioned widget as we want to frequently change its current position based on the value of the animation to produce scanning effect.

AnimationController

This class lets you perform tasks such as:

This AnimationController will provide different values from 0.0 to 1.0 and in reverse 1.0 to 0.0, and we will use this to calculate different position values. These values will be provided under 1 sec.

We can also add a listener to AnimationController to know about the status of the animation to know when it's completed, dismissed, forwarded or reversed.

SingleTickerProviderStateMixin

It provides a class which calls its callback once per animation frame.

1. Creating ImageScannerWidget

Widget build(BuildContext context) {final Animation<double> animation = listenable;final scorePosition = (animation.value * 440) + 16; // Here this value can be dynamicallly provided by the widget on top of which its stacked.Color color1 = Color(0x5532CD32);
Color color2 = Color(0x0032CD32);
if (animation.status == AnimationStatus.reverse) {
color1 = Color(0x0032CD32);
color2 = Color(0x5532CD32);
}
return new Positioned(
bottom: scorePosition,
left: 16.0,
child: new Opacity(
opacity: (stopped) ? 0.0 : 1.0,
child: Container(
height: 60.0, //Gradient Height
width: width,
decoration: new BoxDecoration(
gradient: new LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: [0.1, 0.9],
colors: [color1, color2],)),
)));
}

2. Creating AnimationController

//inside initState() _animationController = new AnimationController(
duration: new Duration(seconds: 1), vsync: this);
_animationController.addStatusListener((status) {
if (status == AnimationStatus.completed) {
animateScanAnimation(true);
} else if (status == AnimationStatus.dismissed) {
animateScanAnimation(false);
}
});

In your build method stack the widgets.

Stack(children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: CupertinoColors.white),
borderRadius:BorderRadius.all(Radius.circular(12))),
child: ClipRRect(
borderRadius:
BorderRadius.all(Radius.circular(12)),
child: Image(width: 334,image: NetworkImage("https://images.pexels.com/photos/1841819/pexels-photo-1841819.jpeg?auto=compress&cs=tinysrgb&dpr=3&h=750&w=1260")),),),
),
ImageScannerAnimation(
_animationStopped,334,animation: _animationController,)
//Also this 334 value can be dynamically provided. Here its similar to width of the image container.
]),void animateScanAnimation(bool reverse) {
if (reverse) {
_animationController.reverse(from: 1.0);
} else {
_animationController.forward(from: 0.0);
}
}

3. Start/End Animation

CupertinoButton(
color: CupertinoColors.activeGreen,
onPressed: () {
if (!scanning) {

animateScanAnimation(false); // Starts the animation.
setState(() {
_animationStopped = false;
scanning = true;
scanText = "Stop";
});
} else {
setState(() {
_animationStopped = true;
scanning = false;
scanText = "Scan";
});
}
},

5. Clean Up

@override
void dispose() {
_animationController.dispose();
super.dispose();
}

Full Code:

https://gist.github.com/webianks/e0655fb723cd16d433a397933f8771a2

Clap 👏 it, if you liked❤ it .

Thanks, I am also active on Github, Twitter.

--

--

Ramankit Singh

Principal Engineer @tiket | Ex- Paytm | Android📱UI/UX 🪐 & Open Source Lover 🌐 | 2 X Google Android Developer Certification Holder