Pinkesh Darji
May 24 · 5 min read
Free vector art via https://www.vecteezy.com

I am pretty sure that you have attended Google IO19 either from there or virtually and you must have watched the efforts, Google is making in AI, explained by Jeff Dean where he shows how RNN(Recurrent Neural Network) process works.

If you were unlucky to watch, You can stop by at 1:29:08 from Official Google IO19 Keynote.

Source: https://www.youtube.com/user/GoogleDevelopers

And this is the exact animation that I have tried to develop in Flutter.

Source: https://www.youtube.com/user/GoogleDevelopers

And the final output looks like this.

Excited! Let’s have a look at how it is made.

Creating a UI structure

As you can see that animated cursor is moving on top of text, We will need a widget that places a widget on top of another widget and Stack Widget is the best one to use.

A Stack contains two widgets-

Widget 1: Container that holds text widget which is used to display text that looks like this.

Container(
padding: EdgeInsets.symmetric(horizontal: 3.0),
child: Text(
textString,
style: TextStyle(fontSize: 22),
))

Widget 2: AnimatedBuilder, AnimatedBuilder is a specialized widget that listens to the values generated on the Animation object that is given to its animation property.

AnimatedBuilder(
animation: animationController,
builder: (BuildContext context, Widget child) {
...
})

Widget 2.1: Position, This will change its left property by whatever value that AnimatedBuilder provides. This way the animated cursor will seem to be moving from left to right over text.

Positioned(
left: animation.value * textWidth,
child: Visibility(...),
);

Widget 2.2: Visibility, We want our animated cursor to be invisible at first (Before animation) and last(after animation).

Visibility(
visible: true,
child: Container(...),
)

Widget 2.3: Container, This widget is used to calculate the width of an animated cursor based on the width of the Text widget. Currently, it is set to 50% of the width of the Text widget.

Container(
width: textWidth * 0.5,
child: Stack(...),
)

Widget 2.4: Stack, We need to show cursor over trailing collapsing animation on top. Here is code to do that.

Stack(
alignment: alignmentDirectional,
children: <Widget>[
AnimatedContainer(...),//UI to display gradient box
Container(...), //UI to display cursor
],
),

Widget 2.4.1: AnimatedContainer, This is used to create a gradient box effect when the animation starts.

AnimatedContainer(
duration: Duration(milliseconds: 100),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: AlignmentDirectional.centerEnd,
end: AlignmentDirectional.centerStart,
stops: [
0.1,
1.0
],
colors: [
Colors.blueAccent[100],
Colors.white70
])),
width: 20,
height: 50,
),

Widget 2.4.1: Container, Cursor is created using this widget.

Container(
width: 3,
height: 50.0,
color: Colors.grey[400],
),

Finally, we are now done with creating UI structure.

Image credit: https://giphy.com

Wiring up Animation

First, have a little look at the code.

Animation<double> animation1;
AnimationController animationController1;
@override
void initState() {
super.initState();
animationController1 =
AnimationController(vsync: this, duration: Duration(milliseconds: 500));
animation1 = Tween(begin: 0.0, end: 0.5).animate(CurvedAnimation(
parent: animationController1,
curve: Curves.fastOutSlowIn,
))
..addStatusListener(handler1);
animationController1.forward();
}

animationController and animation are used together to control the animation, We are giving time to finish animation as duration to animationController and begin and end position to animation using Tween. Twin object is used to create intermediate values from begin to end value.

The animation is started using animationController1.forward();

In order to determine the animation status ..addStatusListener() is attached.

void handler1(status) {
if (status == AnimationStatus.forward) {
setState(() {
_text1Visiblity = true;
_alignm1 = AlignmentDirectional.centerEnd;

});
} else if (status == AnimationStatus.completed) {
setState(() {
_text1Visiblity = false;
});
}
}

We can check whether the animation is started or completed using this.

if (status == AnimationStatus.forward) {

} else if (status == AnimationStatus.completed) {

}

In our case when the animation starts we need to display cursor and move that cursor directly to the right side of the gradient box.

_text1Visiblity = true;
_alignm1 = AlignmentDirectional.centerEnd;

And when the animation stops we can again hide the animated cursor.

_text1Visiblity = false;

That’s it.

I hope you have understood the basic idea of implementing this.

Clone it. Play with it

Thanks for reading this article. If you like it, click on 👏 to rate it out of 50 and also share with your friends. It means a lot to me.

For more about programming, follow me and Aubergine Solutions, so you’ll get notified when we write new posts.

Check out my other article on Options to Animate in Flutter.

Aubergine Solutions

Among the World’s Top 50 UX Agencies. We’re a team of UX Designers and IT Engineers. We balance our madness and method. To know more about us visit www.auberginesolutions.com

Pinkesh Darji

Written by

Passionate Flutter Dev | Google Certified Associate Android Developer | Cross-platform App Dev | Kotlin Android Dev https://github.com/pinkeshdarji

Aubergine Solutions

Among the World’s Top 50 UX Agencies. We’re a team of UX Designers and IT Engineers. We balance our madness and method. To know more about us visit www.auberginesolutions.com

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade