Animations and Transistions

There are two ways to animate widgets in Flutter: animating them within a given screen and animating them between different screens.

Animating Within a Screen: the AnimatedContainer and Similar Widgets

The AnimatedContainer is one of the simplest widgets to start animating your Flutter app: it’s a regular Container but it animates to when a property is changed, giving a nicer transition to a different state. For example, changing its height or width will make the widget shrink or expand gradually, and similar effects can be had by changing the border or color properties of the widget. The duration of the animation can optionally be changed using the duration argument and its curve (whether or not and how much the animation slows down or speeds up over time) can be changed using the curve argument.

The duration is of type duration, so you’d use the Duration we used earlier during authentication, while the curve is of type Curve. A collection of the most common curves is found in the Curves class. It is a big collection, so you might consider reading the full list on the official Flutter reference manual[51] in the Constants section if the following basic curves aren’t enough for you or if you want to know the mathematical function that produces each curve:

  • Curves.easeIn, which starts out slowly and quickens over time.
  • Curves.easeOut, which starts out fast and slows down over time.

If you want to animate just some padding, you can replace a Padding widget with some AnimatedPadding, and if you want to anymate changes in TextStyle you can use the animated version of the DefaultTextStyle (which is a class that applies its style TextStyle arguments to all of the descendants of its child), called the AnimatedDefaultTextStyle (which animates the change in the style) by providing, optionally, a curve and a duration to these animated widgets.

Those are all interesting, but the most useful is certainly the AnimatedList, which animates additions made to a list or deletions of elements of a list.

AnimatedList

The AnimatedList starts out as a normal ListView, but its state object can be used (via a GlobalKey) to insert or delete items, showing a transition when one of these operations is performed.

We can construct an AnimatedList just like a ListView.builder-constructed ListView, but we have to set a key that we’ll use to interact with it later, which has to be a GlobalKey<AnimatedListState>, and the itemCount will instead be called initialItemCount.

The itemBuilder gets an extra animation argument, which provides the current state of the animation, which can be used by a SizeTransition widget inside the itemBuilder to actually visually create the transition when an item is inserted by increasing the size of the widget (clipping it at the start and gradually showing it all) to animate the insertion of a widget.

All of that adds up to the following:

 AnimatedList(
  key: key,
  initialItemCount: n,
  itemBuilder(context, i, animation) =>
  SizeTransition(
  sizeFactor: animation,
  child: WidgetToShow()
  )
 )

The SizeTransition isn’t the only widget that can perform similar transitions (for example the similar ScaleTransition scales the widget up and down instead of clipping it), but it’s the most effective for the purpose of animating a list.

Having the same key stored somewhere allows us to get the AnimatedListState object relative to that AnimatedList by writing:

 var​ listState = key.currentState ​as​ AnimatedListState;

which allows us to add a list item by wrapping the following in a call to setState:

 listState.insertItem(position);

keeping in mind that the AnimatedList needs to be able to build the new element at that position: if it’s taking elements from a List, make sure there is actually new data to build the new item from.

A full example of a widget (or, more accurately, a stateful widget’s state) that takes advantage of an AnimatedList to dynamically add items to a list when a floating action button is pressed is the following:

 GlobalKey _key = GlobalKey<AnimatedListState>();
 int​ _nextWidget = 3;
 var​ _strings = [
 "First"​,
 "Second"​,
 "Third"
 ];
 
 @override
 Widget ​build​(BuildContext context) {
 return​ Scaffold(
  appBar: AppBar(title: Text(​"Animated List"​)),
  body: AnimatedList(
  key: _key,
  initialItemCount: _nextWidget,
  itemBuilder: (context, i, animation) =>
  SizeTransition(
  sizeFactor: animation,
  child: ListTile(title: Text(_strings[i]),),
  ),
  ),
  floatingActionButton: FloatingActionButton(
  child: Icon(Icons.add),
  onPressed: () {
  setState(() {
  (_key.currentState ​as​ AnimatedListState).insertItem(
  _nextWidget++
  );
  _strings.add(
 "​​${_nextWidget}​​th"
  );
  });
  },
  ),
  );
 }

These widgets provide the simplest kind of animation available in Flutter: the most interesting animations are provided by the Hero widget, which allows you to animate the movement of a widget across different screens.

Hero Animation

Hero animations are performed by the Flutter framework on Hero widgets. An Hero widget needs a tag that identifies it and a child (which is the widget to animate). Adding the same tag to two Hero widgets in two different screens, one of which is pushed from the other, will make the widget animate to move to its new position on the screen.

A simple example that demonstrates the use of the Hero widget is the following: there are two pages that have in common a widget (I used FlutterLogo as an example) that is wrapped in an Hero widget with the same tag. Switching between the views by clicking the floating action button will cause the widget to animate between its centered position in the first page to a top-left position in the second page and vice versa:

 class​ FirstPage ​extends​ StatelessWidget {
  @override
  Widget build(BuildContext context) =>
  Scaffold(
  body:Center(
  child:Hero(
  tag: ​"Flutter Logo Hero"​,
  child: FlutterLogo(),
  )
  ),
  floatingActionButton: FloatingActionButton(
  child: Icon(Icons.looks_two),
  onPressed: () => Navigator.pushReplacement(
  context,
  MaterialPageRoute(
  builder: (context) => SecondPage()
  )
  ),
  ),
  );
 }
 
 class​ SecondPage ​extends​ StatelessWidget {
  @override
  Widget build(BuildContext context) =>
  Scaffold(
  body:Hero(
  tag: ​"Flutter Logo Hero"​,
  child: FlutterLogo(),
  ),
  floatingActionButton: FloatingActionButton(
  child: Icon(Icons.looks_one),
  onPressed: () => Navigator.pushReplacement(
  context,
  MaterialPageRoute(
  builder: (context) => FirstPage()
  )
  ),
  ),
  );
 }
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset