Featured Image
Software Development

Text Reader Animation in Flutter

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.

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

Flutter animation replicating Google IO19 text reader animation

And the final output looks like this.

Final output of the animation

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 containing two widgets

Stack contains two widgets-

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

Widget 1: Container containing text that displays the text

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.

Widget 2.3: Container for cursor width calculation (50% of Text widget width)

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.

Widget 2.4.1: AnimatedContainer for gradient box effect during animation start

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.

Widget 2.4.1: Container for creating the cursor

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

Finally, we are now done with creating UI structure.

Image credit: https://giphy.com

Also read: Mastering Flutter’s Chip Widget: A Comprehensive Guide

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.

You can get the code here.

Also read: Creating a Superhero Interaction App with Flutter’s Core Animation APIs

author
Pinkesh Darji
I love to solve problems using technology that improves user’s life on a major scale. Over the last several years, I have been developing and leading various mobile apps in different areas. More than just programming, I love to write technical articles. I have written many high-quality technical articles. I have worked with various startups to build their dream app. I have been involved in product development since my early days and know insights into it. I have provided my valuable input while taking some crucial decisions of the app by brainstorming with a design, QA team. Over the last 3 years, I have been developing mobile apps and libraries using Google’s Flutter framework. I mentor junior Flutter developers and review their code. You will also find me talking on various Flutter topics in local meetups.