Romain Rastel

Create Simple Animations for your Articles with Flutter

Use Flutter to add animations to your blog posts and articles to spice them up.

Add to Pieces

To create staggered animations, the easiest way is to create one AnimationController that manages all of the animations and to animate them with a CurvedAnimation which takes a parent animation (the animation controller) and a Curve.

Curve Interval Animation

Tags: curve, animation, dart, flutter

The Curve, here, is a special one: the Interval. It allows us define the part of the parent timeline in which the child animation will be animated from beginning to end.

Tween(
  begin: Offset(-1.0, 0.0),
  end: Offset(0.0, 0.0),
).animate(
  CurvedAnimation(
    parent: controller,
    curve: Interval(.30, .60),
  ),
),

Related links:

  1. https://docs.flutter.io/flutter/widgets/ScaleTransition-class.html
  2. https://flutter.io/animations/staggered-animations/
  3. https://flutter.io/animations/staggered-animations/#complete-staggered-animation
  4. https://www.screentogif.com/?l=fr_fr
  5. https://docs.flutter.io/flutter/widgets/SlideTransition-class.html
  6. https://medium.com/flutter-community/create-simple-animations-for-your-articles-with-flutter-7769085108d1
  7. https://github.com/letsar/flutter_slidable
  8. https://docs.flutter.io/flutter/widgets/AnimatedBuilder-class.html

Slidable Drawer Delegate Animation

Tags: flutter-layout, flutter, dart

Like all interactive widgets, the complete animation consists of a widget pair: a stateless and a stateful widget. The stateless widget specifies the Tweens, defines the Animation objects, and provides a build() function responsible for building the animating portion of the widget tree. The stateful widget creates the controller, plays the animation, and builds the non-animating portion of the widget tree. The animation begins when a tap is detected anywhere in the screen.

SlidableDrawerDelegateAnimation({
    Key key,
    this.controller,
  })  : dxPositions = >[
          Tween(
            begin: Offset(-1.0, 0.0),
            end: Offset(1.0, 0.0),
          ).animate(
            CurvedAnimation(
              parent: controller,
              curve: _kAnimInterval03,
            ),
          ),
          Tween(
            begin: Offset(-1.0, 0.0),
            end: Offset(0.0, 0.0),
          ).animate(
            CurvedAnimation(
              parent: controller,
              curve: _kAnimInterval03,
            ),
          ),
          Tween(
            begin: Offset(0.0, 0.0),
            end: Offset(0.5, 0.0),
          ).animate(
            CurvedAnimation(
              parent: controller,
              curve: _kAnimInterval03,
            ),
          ),
        ],
        dyPositions1 = List.generate(
          _kCount,
          (i) => Tween(
                begin: Offset.zero,
                end: Offset(0.0, -i.toDouble() / 2),
              ).animate(
                CurvedAnimation(
                  parent: controller,
                  curve: _kAnimInterval01,
                ),
              ),
        ),
        dyPositions2 = List.generate(
          _kCount,
          (i) => Tween(
                begin: Offset.zero,
                end: Offset(0.0, i.toDouble() / 2),
              ).animate(
                CurvedAnimation(
                  parent: controller,
                  curve: _kAnimInterval04,
                ),
              ),
        ),
        skewTransform1 = Tween(begin: 0.0, end: 0.5).animate(
          CurvedAnimation(
            parent: controller,
            curve: _kAnimInterval02,
          ),
        ),
        skewTransform2 = Tween(begin: 0.0, end: -0.5).animate(
          CurvedAnimation(
            parent: controller,
            curve: _kAnimInterval05,
          ),
        ),
        super(key: key);

  final Animation controller;
  final List> dyPositions1;
  final List> dyPositions2;
  final List> dxPositions;
  final Animation skewTransform1;
  final Animation skewTransform2;


Related links:

  1. https://docs.flutter.io/flutter/widgets/ScaleTransition-class.html
  2. https://flutter.io/animations/staggered-animations/
  3. https://flutter.io/animations/staggered-animations/#complete-staggered-animation
  4. https://www.screentogif.com/?l=fr_fr
  5. https://docs.flutter.io/flutter/widgets/SlideTransition-class.html
  6. https://medium.com/flutter-community/create-simple-animations-for-your-articles-with-flutter-7769085108d1
  7. https://github.com/letsar/flutter_slidable
  8. https://docs.flutter.io/flutter/widgets/AnimatedBuilder-class.html

Const Variables of Animation Intervals

Tags: dart, animation, flutter

The value of the intervals are stored in constants and can be used in an Animated Builder.

const _kAnimInterval01 = const Interval(.00, .30);
const _kAnimInterval02 = const Interval(.00, .30);
const _kAnimInterval03 = const Interval(.30, .60);
const _kAnimInterval04 = const Interval(.60, .90);
const _kAnimInterval05 = const Interval(.60, .90);

Related links:

  1. https://docs.flutter.io/flutter/widgets/ScaleTransition-class.html
  2. https://flutter.io/animations/staggered-animations/
  3. https://flutter.io/animations/staggered-animations/#complete-staggered-animation
  4. https://www.screentogif.com/?l=fr_fr
  5. https://docs.flutter.io/flutter/widgets/SlideTransition-class.html
  6. https://medium.com/flutter-community/create-simple-animations-for-your-articles-with-flutter-7769085108d1
  7. https://github.com/letsar/flutter_slidable
  8. https://docs.flutter.io/flutter/widgets/AnimatedBuilder-class.html

Animated Builder Parent

Tags: builder, animation, controller, dart, flutter

The build() function creates an AnimatedBuilder that takes a builder function (named _buildAnimation() here) and calls it when the animation controller notifies its changes.

@override
Widget build(BuildContext context) {
  return AnimatedBuilder(
    builder: _buildAnimation,
    animation: controller,
  );
}

Related links:

  1. https://docs.flutter.io/flutter/widgets/ScaleTransition-class.html
  2. https://flutter.io/animations/staggered-animations/
  3. https://flutter.io/animations/staggered-animations/#complete-staggered-animation
  4. https://www.screentogif.com/?l=fr_fr
  5. https://docs.flutter.io/flutter/widgets/SlideTransition-class.html
  6. https://medium.com/flutter-community/create-simple-animations-for-your-articles-with-flutter-7769085108d1
  7. https://github.com/letsar/flutter_slidable
  8. https://docs.flutter.io/flutter/widgets/AnimatedBuilder-class.html

Build Animation Function

Tags: build, dart, flutter, animation

The buildAnimation() function which does all the heavy work 💪.

  Widget _buildAnimation(BuildContext context, Widget child) {
    return Container(
      child: SkewTransition(
        skew: skewTransform1,
        child: SkewTransition(
          skew: skewTransform2,
          child: Container(
            width: _kActionSize * 4,
            height: 256.0,
            color: Colors.black87,
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  _buildListTile(context, 0),
                  Stack(
                    children: List.generate(_kCount, (i) {
                      return SlideTransition(
                        position: dyPositions1[i],
                        child: SlideTransition(
                          position: dxPositions[i],
                          child: SlideTransition(
                            position: dyPositions2[i],
                            child: _buildWidget(context, i),
                          ),
                        ),
                      );
                    }),
                  ),
                  _buildListTile(context, 2),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }

Related links:

  1. https://docs.flutter.io/flutter/widgets/ScaleTransition-class.html
  2. https://flutter.io/animations/staggered-animations/
  3. https://flutter.io/animations/staggered-animations/#complete-staggered-animation
  4. https://www.screentogif.com/?l=fr_fr
  5. https://docs.flutter.io/flutter/widgets/SlideTransition-class.html
  6. https://medium.com/flutter-community/create-simple-animations-for-your-articles-with-flutter-7769085108d1
  7. https://github.com/letsar/flutter_slidable
  8. https://docs.flutter.io/flutter/widgets/AnimatedBuilder-class.html

Skew Transition Extending Animated Widget

Tags: dart, ios, javascript, transition, skew

The SkewTransition is a custom animated widget which is heavily inspired by the ScaleTransition widget. It’s the widget which controls the skew of the child.

/// Animates the skew of transformed widget.
class SkewTransition extends AnimatedWidget {
  /// Creates a skew transition.
  ///
  /// The [skew] argument must not be null.
  const SkewTransition({
    Key key,
    @required Animation skew,
    this.child,
  }) : super(key: key, listenable: skew);

  /// The animation that controls the skew of the child.
  Animation get skew => listenable;

  /// The widget below this widget in the tree.
  ///
  /// {@macro flutter.widgets.child}
  final Widget child;

  @override
  Widget build(BuildContext context) {
    final double skewValue = skew.value;
    final Matrix4 transform = new Matrix4.skewX(skewValue);
    return new Transform(
      transform: transform,
      child: child,
    );
  }
}

Related links:

  1. https://docs.flutter.io/flutter/widgets/ScaleTransition-class.html
  2. https://flutter.io/animations/staggered-animations/
  3. https://flutter.io/animations/staggered-animations/#complete-staggered-animation
  4. https://www.screentogif.com/?l=fr_fr
  5. https://docs.flutter.io/flutter/widgets/SlideTransition-class.html
  6. https://medium.com/flutter-community/create-simple-animations-for-your-articles-with-flutter-7769085108d1
  7. https://github.com/letsar/flutter_slidable
  8. https://docs.flutter.io/flutter/widgets/AnimatedBuilder-class.html

Stateful Widget from Flutter.io Tutorial with SlideTransition

Tags: flutter.io, dart, flutter, widget

The SlideTransition widgets control the positions of the different children. The stateful widget looks a lot like the one you can find in the flutter.io tutorial.

class SlidableDrawerDelegateDemo extends StatefulWidget {
  @override
  _SlidableDrawerDelegateDemoState createState() =>
      new _SlidableDrawerDelegateDemoState();
}

class _SlidableDrawerDelegateDemoState extends State
    with TickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(duration: _kDuration, vsync: this);
  }

  Future _playAnimation() async {
    try {
      await _controller.forward().orCancel;
      await _controller.reverse().orCancel;
    } on TickerCanceled {
      // the animation got canceled, probably because we were disposed
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Slidable Drawer Delegate Animation'),
      ),
      body: GestureDetector(
        behavior: HitTestBehavior.opaque,
        onTap: () => _playAnimation(),
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Center(
            child: SlidableDrawerDelegateAnimation(
              controller: _controller.view,
            ),
          ),
        ),
      ),
    );
  }
}

Related links:

  1. https://docs.flutter.io/flutter/widgets/ScaleTransition-class.html
  2. https://flutter.io/animations/staggered-animations/
  3. https://flutter.io/animations/staggered-animations/#complete-staggered-animation
  4. https://www.screentogif.com/?l=fr_fr
  5. https://docs.flutter.io/flutter/widgets/SlideTransition-class.html
  6. https://medium.com/flutter-community/create-simple-animations-for-your-articles-with-flutter-7769085108d1
  7. https://github.com/letsar/flutter_slidable
  8. https://docs.flutter.io/flutter/widgets/AnimatedBuilder-class.html

Code to Run Flutter Animation - Full File

Tags: dart,  flash, flutter, actionscript, animation, run

This is all of the code put together to run the example.

import 'dart:async';

import 'package:flutter/material.dart';

const _kDuration = const Duration(milliseconds: 4000);
const _kActionSize = 64.0;
const _kCount = 3;

const _kAnimInterval01 = const Interval(.00, .30);
const _kAnimInterval02 = const Interval(.00, .30);

const _kAnimInterval03 = const Interval(.30, .60);

const _kAnimInterval04 = const Interval(.60, .90);
const _kAnimInterval05 = const Interval(.60, .90);

const archiveAction = const IconSlideAction(
  icon: Icons.archive,
  color: Colors.blue,
  caption: 'Archive',
);

const shareAction = const IconSlideAction(
  icon: Icons.share,
  color: Colors.indigo,
  caption: 'Share',
);

void main() => runApp(
      MaterialApp(
        home: Material(
          child: SlidableDrawerDelegateDemo(),
        ),
      ),
    );

class SlidableDrawerDelegateDemo extends StatefulWidget {
  @override
  _SlidableDrawerDelegateDemoState createState() =>
      new _SlidableDrawerDelegateDemoState();
}

class _SlidableDrawerDelegateDemoState extends State
    with TickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(duration: _kDuration, vsync: this);
  }

  Future _playAnimation() async {
    try {
      await _controller.forward().orCancel;
      await _controller.reverse().orCancel;
    } on TickerCanceled {
      // the animation got canceled, probably because we were disposed
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Slidable Drawer Delegate Animation'),
      ),
      body: GestureDetector(
        behavior: HitTestBehavior.opaque,
        onTap: () => _playAnimation(),
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Center(
            child: SlidableDrawerDelegateAnimation(
              controller: _controller.view,
            ),
          ),
        ),
      ),
    );
  }
}

class SlidableDrawerDelegateAnimation extends StatelessWidget {
  SlidableDrawerDelegateAnimation({
    Key key,
    this.controller,
  })  : dxPositions = >[
          Tween(
            begin: Offset(-1.0, 0.0),
            end: Offset(1.0, 0.0),
          ).animate(
            CurvedAnimation(
              parent: controller,
              curve: _kAnimInterval03,
            ),
          ),
          Tween(
            begin: Offset(-1.0, 0.0),
            end: Offset(0.0, 0.0),
          ).animate(
            CurvedAnimation(
              parent: controller,
              curve: _kAnimInterval03,
            ),
          ),
          Tween(
            begin: Offset(0.0, 0.0),
            end: Offset(0.5, 0.0),
          ).animate(
            CurvedAnimation(
              parent: controller,
              curve: _kAnimInterval03,
            ),
          ),
        ],
        dyPositions1 = List.generate(
          _kCount,
          (i) => Tween(
                begin: Offset.zero,
                end: Offset(0.0, -i.toDouble() / 2),
              ).animate(
                CurvedAnimation(
                  parent: controller,
                  curve: _kAnimInterval01,
                ),
              ),
        ),
        dyPositions2 = List.generate(
          _kCount,
          (i) => Tween(
                begin: Offset.zero,
                end: Offset(0.0, i.toDouble() / 2),
              ).animate(
                CurvedAnimation(
                  parent: controller,
                  curve: _kAnimInterval04,
                ),
              ),
        ),
        skewTransform1 = Tween(begin: 0.0, end: 0.5).animate(
          CurvedAnimation(
            parent: controller,
            curve: _kAnimInterval02,
          ),
        ),
        skewTransform2 = Tween(begin: 0.0, end: -0.5).animate(
          CurvedAnimation(
            parent: controller,
            curve: _kAnimInterval05,
          ),
        ),
        super(key: key);

  final Animation controller;
  final List> dyPositions1;
  final List> dyPositions2;
  final List> dxPositions;
  final Animation skewTransform1;
  final Animation skewTransform2;

  Widget _buildAnimation(BuildContext context, Widget child) {
    return Container(
      child: SkewTransition(
        skew: skewTransform1,
        child: SkewTransition(
          skew: skewTransform2,
          child: Container(
            width: _kActionSize * 4,
            height: 256.0,
            color: Colors.black87,
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  _buildListTile(context, 0),
                  Stack(
                    children: List.generate(_kCount, (i) {
                      return SlideTransition(
                        position: dyPositions1[i],
                        child: SlideTransition(
                          position: dxPositions[i],
                          child: SlideTransition(
                            position: dyPositions2[i],
                            child: _buildWidget(context, i),
                          ),
                        ),
                      );
                    }),
                  ),
                  _buildListTile(context, 2),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildWidget(BuildContext context, int index) {
    switch (index) {
      case 0:
        return shareAction;
      case 1:
        return archiveAction;
      case 2:
        return _buildListTile(context, 1);
    }
    return null;
  }

  Widget _buildListTile(BuildContext context, int index) {
    return Container(
      color: Colors.grey.shade200,
      width: _kActionSize * 4,
      height: _kActionSize,
      child: ListTile(
        leading: CircleAvatar(
          backgroundColor: Colors.green,
          child: Text('\$index'),
          foregroundColor: Colors.white,
        ),
        title: Text('Title'),
        subtitle: Text('Subtitle'),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      builder: _buildAnimation,
      animation: controller,
    );
  }
}

/// Animates the skew of transformed widget.
class SkewTransition extends AnimatedWidget {
  /// Creates a skew transition.
  ///
  /// The [skew] argument must not be null.
  const SkewTransition({
    Key key,
    @required Animation skew,
    this.child,
  }) : super(key: key, listenable: skew);

  /// The animation that controls the skew of the child.
  Animation get skew => listenable;

  /// The widget below this widget in the tree.
  ///
  /// {@macro flutter.widgets.child}
  final Widget child;

  @override
  Widget build(BuildContext context) {
    final double skewValue = skew.value;
    final Matrix4 transform = new Matrix4.skewX(skewValue);
    return new Transform(
      transform: transform,
      child: child,
    );
  }
}

class IconSlideAction extends StatelessWidget {
  /// Creates a slide action with an icon, a [caption] if set and a
  /// background color.
  ///
  /// The [closeOnTap] argument must not be null.
  const IconSlideAction({
    Key key,
    @required this.icon,
    this.caption,
    this.color,
  }) : super(key: key);

  final IconData icon;

  final String caption;

  /// The background color.
  ///
  /// Defaults to true.
  final Color color;

  @override
  Widget build(BuildContext context) {
    final Color foregroundColor =
        ThemeData.estimateBrightnessForColor(color) == Brightness.light
            ? Colors.black
            : Colors.white;
    final Text textWidget = new Text(
      caption ?? '',
      overflow: TextOverflow.ellipsis,
      style: Theme.of(context)
          .primaryTextTheme
          .caption
          .copyWith(color: foregroundColor),
    );
    return Container(
      color: color,
      width: _kActionSize,
      height: _kActionSize,
      child: new Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            new Flexible(
              child: new Icon(
                icon,
                color: foregroundColor,
              ),
            ),
            new Flexible(child: textWidget),
          ],
        ),
      ),
    );
  }
}

Related links:

  1. https://docs.flutter.io/flutter/widgets/ScaleTransition-class.html
  2. https://flutter.io/animations/staggered-animations/
  3. https://flutter.io/animations/staggered-animations/#complete-staggered-animation
  4. https://www.screentogif.com/?l=fr_fr
  5. https://docs.flutter.io/flutter/widgets/SlideTransition-class.html
  6. https://medium.com/flutter-community/create-simple-animations-for-your-articles-with-flutter-7769085108d1
  7. https://github.com/letsar/flutter_slidable
  8. https://docs.flutter.io/flutter/widgets/AnimatedBuilder-class.html

Build Animation: Changed Interval and Total Duration

Tags: flutter-layout, dart, flutter

For this simpler animation, we have to keep only one SlideTransition, removes the slide actions, and change the interval and total duration.

const _kDuration = const Duration(milliseconds: 2000);
const _kAnimInterval03 = const Interval(.00, .90);
...  
Widget _buildAnimation(BuildContext context, Widget child) {
    return Container(
      child: Container(
        width: _kActionSize * 4,
        height: 256.0,
        color: Colors.black87,
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              _buildListTile(context, 0),
              SlideTransition(
                position: dxPositions[2],
                child: _buildWidget(context, 2),
              ),
              _buildListTile(context, 2),
            ],
          ),
        ),
      ),
    );
  }


Related links:

  1. https://docs.flutter.io/flutter/widgets/ScaleTransition-class.html
  2. https://flutter.io/animations/staggered-animations/
  3. https://flutter.io/animations/staggered-animations/#complete-staggered-animation
  4. https://www.screentogif.com/?l=fr_fr
  5. https://docs.flutter.io/flutter/widgets/SlideTransition-class.html
  6. https://medium.com/flutter-community/create-simple-animations-for-your-articles-with-flutter-7769085108d1
  7. https://github.com/letsar/flutter_slidable
  8. https://docs.flutter.io/flutter/widgets/AnimatedBuilder-class.html

Single SlideTransition Animation with Inverse

Tags: flutter-layout, dart, flutter, animation

For this animation, we have to keep only one SlideTransition, modify the cross axis alignment, inverse the order of the children, and change the interval and total duration.

const _kDuration = const Duration(milliseconds: 2000);
const _kAnimInterval03 = const Interval(.00, .90);
...  
Widget _buildAnimation(BuildContext context, Widget child) {
    return Container(
      child: Container(
        width: _kActionSize * 4,
        height: 256.0,
        color: Colors.black87,
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              _buildListTile(context, 0),
              SlideTransition(
                position: dxPositions[2],
                child: _buildWidget(context, 2),
              ),
              _buildListTile(context, 2),
            ],
          ),
        ),
      ),
    );
  }


Related links:

  1. https://docs.flutter.io/flutter/widgets/ScaleTransition-class.html
  2. https://flutter.io/animations/staggered-animations/
  3. https://flutter.io/animations/staggered-animations/#complete-staggered-animation
  4. https://www.screentogif.com/?l=fr_fr
  5. https://docs.flutter.io/flutter/widgets/SlideTransition-class.html
  6. https://medium.com/flutter-community/create-simple-animations-for-your-articles-with-flutter-7769085108d1
  7. https://github.com/letsar/flutter_slidable
  8. https://docs.flutter.io/flutter/widgets/AnimatedBuilder-class.html