Scanning animation in Flutter
Using AnimatedWidget & AnimationController.
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:
- Play an animation forward or in reverse, or stop an animation.
- Set the animation to a specific value.
- Define the upperBound and lowerBound values of an animation.
- Create a fling animation effect using a physics simulation.
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