👻

Flutterアニメーション(Part 1)

2022/11/14に公開

日本語版:

はじめに

Flutterはアニメーションに優れたサポートを提供しており、アニメーションを以下の2つのカテゴリに分けることができます。

  • トゥイーンアニメーション
  • 物理ベースのアニメーション

トゥイーンアニメーション

トゥイーンアニメーションでは、アニメーションの始点と終点を定義する必要があります。つまり、アニメーションの開始値から始まり、一連の中間値を経て、最終的に終了値に達するということです。また、タイムラインやカーブも用意されており、遷移の時間や速度を定義することができます。ウィジェットフレームワークは、開始点と終了点からどのように遷移するかを計算する機能を提供します。

ColorTween {  
    begin: color.green,  
    end: color.blue,  
}  

物理ベースのアニメーション

このタイプのアニメーションで、アプリのインタラクションをリアルに感じさせたり、インタラクティブにすることができる。例えば、ウィジェットがバネで動く、落ちる、重力で揺れるなど、実世界のアニメーション/動きをシミュレートすることができる。

アニメーションには、主に次のような3つの柱があります。

  • Ticker(ティッカー)
  • Animation Class(アニメーションクラス)
  • AnimationController(アニメーションコントローラー)

ティッカー

ティッカーは、一定間隔、つまり1秒間に60回程度信号を送るクラスです。時計が一定の間隔でチックするのと同じように理解できるだろう。ティッカーは、各ティックで、開始してから1秒ごとの最初のティックからの継続時間をコールバックメソッドで提供します。ティッカーが異なる時刻に開始されても、常に自動的に同期しています。この理由は、ティッカーが開始された後の最初のティックを基準として経過時間を与えるからです。

アニメーション

アニメーションクラスは、アニメーション・システムの中核となる構成要素です。アニメーションは他の何ものでもありませんが、アニメーションの寿命の間に変化することができる値(特定の型)を表します。Flutter では、アニメーションを実行するウィジェットは、パラメータとして アニメーションオブジェクトを受け取ります。この Animation オブジェクトは、アニメーションの現在の値を読み取るための情報と、その値の変化を聞くための情報を提供します。アニメーションクラスには、addListener()addStatusListener() の 2 つのメソッドがあります。アニメーションの値が変更されると、addListener() で追加されたすべてのリスナーに通知されます。また、アニメーションの状態が変化すると、addStatusListener() で追加されたすべてのリスナーに通知します。

最も一般的なアニメーションクラスは次のとおりです。

  • Animation<double>: 2つの10進数間の値を一定時間内挿する。
  • Animation<Color>:2つの色値間の色を補間する。
  • Animation<Size>: 2つのサイズ値の間でサイズを補間する。

アニメーションコントローラー

アニメーションコントローラーは、アニメーションを制御するためのクラスです。アプリケーションが新しいフレームを用意するたびに、常に新しい値を生成します。例えば、アニメーションの開始、停止、前進、繰り返しの制御を行います。一旦、アニメーションコントローラを作成すれば、それを基にリバース・アニメーションやカーブ・アニメーションなどの他のアニメーションを作成することができます。

animcontroller = AnimationController(vsync: this, duration: Duration(milliseconds: 2500));  

ここでは、duration オプションでアニメーション処理の継続時間を制御し、vsync オプションでアニメーションで使用するリソースを最適化する。

AnimationControllerを使うために必要な基本的な手順は以下の通りだ。

ステップ1: まず、durationvsync などのパラメータを指定して AnimationController をインスタンス化する。

ステップ2addListener()addStatusListener()など、必要なリスナーを追加する。

ステップ3: アニメーションを開始する。

ステップ4: リスナーのコールバック・メソッドでアクションを実行する(例えば、setState)。

ステップ5: 最後に、アニメーションを破棄する(dispose)。

アニメーションクラスアニメーションコントローラを使用して、簡単なアニメーションの例を見てみましょう。次の例では、アニメーションの始点と終点を与えるトゥイーンアニメーションを示しています。プロジェクトを開き、main.dart ファイルにある次のコードを置き換えます。

main.dart
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Animation',
      theme: ThemeData(
        // This is the theme of your application.
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late Animation<double> animation;
  late AnimationController animationController;
  
  void initState() {
    super.initState();
    animationController = AnimationController(
        vsync: this, duration: Duration(milliseconds: 2500));
    animation =
        Tween<double>(begin: 0.0, end: 1.0).animate(animationController);
    animation.addListener(() {
      setState(() {
        print(animation.value.toString());
      });
    });
    animation.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        animationController.reverse();
      } else if (status == AnimationStatus.dismissed) {
        animationController.forward();
      }
    });
    animationController.forward();
  }

  
  Widget build(BuildContext context) {
    return Center(
        child: AnimatedLogo(
      animation: animation,
    ));
  }
}

class AnimatedLogo extends AnimatedWidget {
  final Tween<double> _sizeAnimation = Tween<double>(begin: 0.0, end: 500.0);
  AnimatedLogo({Key? key, required Animation animation})
      : super(key: key, listenable: animation);
  
  Widget build(BuildContext context) {
    final Animation<double> animation = listenable as Animation<double>;
    return Transform.scale(
      scale: _sizeAnimation.evaluate(animation),
      child: FlutterLogo(),
    );
  }
}

出力

Android Studioでアプリケーションを実行すると、出力が表示されます。画面では、Flutterのロゴが順方向と逆方向でスケーリングしていることがわかります。

English ver:

Introduction

Flutter provides excellent support for animation and can be divided into two categories of animation

  • Tween animation
  • Physics-based animation

Tween Animation

In tween animation, you have to define a starting point and an end point for the animation. This means that the animation starts with a start value, goes through a series of intermediate values, and finally reaches an end value. Timelines and curves are also provided to define the time and speed of transitions. The widget framework provides the ability to calculate how the transition will occur from the start and end points.

Example

ColorTween {  
    begin: color.green,  
    end: color.blue,  
}  

Physics-based Animation

This type of animation can make application interactions feel realistic and interactive. For example, widgets can simulate real-world animations/movements such as springing, falling, or swaying under gravity.

There are three main pillars of animation

  • Ticker
  • Animation Class
  • AnimationController

Ticker

Ticker is a class that sends signals at regular intervals, or about 60 times per second. It can be understood in the same way that a clock ticks at regular intervals. The Ticker provides a callback method for each tick with a duration from the first tick of every second since it started. Even if the ticker is started at a different time, it is always automatically synchronized. The reason for this is that the elapsed time is given with respect to the first tick after the ticker is started.

Animation

The animation class is the core building block of the animation system. An Animation is nothing else but a value (of a specific type) that can change during the life of the Animation. in Flutter, the widget that performs the animation receives an Animation object as a parameter. This Animation object provides information to read the current value of the animation and to listen for changes in that value. The Animation class has two methods: addListener() and addStatusListener(). When the value of an animation changes, all listeners added by addListener() are notified. Also, when the state of the animation changes, all listeners added with addStatusListener() are notified.

The most common animation classes are:

  • Animation<double>: interpolates a value between two decimal numbers for a fixed time.
  • Animation<Color>: interpolates a color between two color values.
  • Animation<Size>: interpolate size between two size values.

Animation Controller

Animation Controller is a class for controlling animation. It always generates a new value each time the application prepares a new frame. For example, it controls the start, stop, forward, and repeat of an animation. Once an Animation Controller is created, other animations, such as reverse animations or curve animations, can be created based on it.

animcontroller = AnimationController(vsync: this, duration: Duration(milliseconds: 2500));  

Here, the duration option controls the duration of the animation process, and the vsync option optimizes the resources used by the animation.

The basic steps required to use AnimationController are as follows:

Step 1: First, instantiate AnimationController with parameters such as duration and vsync.

Step 2: Add the necessary listeners, such as addListener() and addStatusListener().

Step 3: Start the animation.

Step 4: Execute the action in the listener's callback method (e.g., setState).

Step 5: Finally, dispose the animation.

Let's look at a simple animation example using the animation class and animation controller. The following example shows a tween animation that gives the start and end points of the animation. Open the project and replace the following code in the main.dart file.

main.dart
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Animation',
      theme: ThemeData(
        // This is the theme of your application.
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late Animation<double> animation;
  late AnimationController animationController;
  
  void initState() {
    super.initState();
    animationController = AnimationController(
        vsync: this, duration: Duration(milliseconds: 2500));
    animation =
        Tween<double>(begin: 0.0, end: 1.0).animate(animationController);
    animation.addListener(() {
      setState(() {
        print(animation.value.toString());
      });
    });
    animation.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        animationController.reverse();
      } else if (status == AnimationStatus.dismissed) {
        animationController.forward();
      }
    });
    animationController.forward();
  }

  
  Widget build(BuildContext context) {
    return Center(
        child: AnimatedLogo(
      animation: animation,
    ));
  }
}

class AnimatedLogo extends AnimatedWidget {
  final Tween<double> _sizeAnimation = Tween<double>(begin: 0.0, end: 500.0);
  AnimatedLogo({Key? key, required Animation animation})
      : super(key: key, listenable: animation);
  
  Widget build(BuildContext context) {
    final Animation<double> animation = listenable as Animation<double>;
    return Transform.scale(
      scale: _sizeAnimation.evaluate(animation),
      child: FlutterLogo(),
    );
  }
}

Output.

Run the application in Android Studio and you will see the output. On the screen you will see the Flutter logo scaling in the forward and reverse directions.

Discussion