Flutterには、アプリのアニメーション効果を作成するための膨大な量のすばらしいパッケージがありますが、より微調整されたアニメーションを手動で作成するための組み込みのメソッドもあります。

前提条件

クロススクリーンアニメーションでは、簡単にするために、基本的なルートを作成する方法をすでに知っていると想定します。 まだ慣れていない場合は、ドキュメントをここで確認できます。

線形アニメーション

アニメーションの主な3つの部分は、時間を制御するticker、継続時間などのパラメーターを登録するcontroller、そして変更する値です。 ウィジェットがレンダリングされる前に、initStateで、コントローラーをパラメーターに設定し、方向を設定し、リスナーを追加して、変更のたびにウィジェットの状態をリセットできます。

デフォルトでは、コントローラーは、設定した時間内に0から1に変化を移動します。リスナーにcontroller.valueを出力して、これが発生するのを確認できます。 upperBoundまたはlowerBoundプロパティを設定することにより、デフォルトの開始値と終了値を変更できます。

main.dart
class MyHomePage extends StatefulWidget {
  
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  AnimationController controller;

  void initState() {
    super.initState();
    controller = AnimationController(
      duration: Duration(seconds: 1),
      vsync: this); // Links this controller to this widget so it won't run if the parent widget isn't rendered, to save on resources.

    controller.forward();
    controller.addListener(() => setState(() {}));
  }
}

アニメーションを使用するには、不透明度など、必要なものをcontroller.valueに設定する必要があります。


  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Opacity(
          opacity: controller.value,
          child: Container(width: 50, height: 50, color: Colors.red),
        ),
      ),
    );
  }

湾曲したアニメーション

退屈な線形アニメーションの代わりに、さまざまな曲線のバリエーションを使用して、コントローラーの変更方法を正確に制御できます。 これを行うには、CurvedAnimationを使用して、元のコントローラー上に一種のラッパーを作成できます。 このラッパーは、その親、コントローラー、および適用するカーブを取得します。 曲線アニメーションを使用する場合、0と1以外に下限と上限を設定することはできないため、適用すると、その値を乗算するだけで済みます。 docsには非常に広範なオプションのリストがあります。

controllerのもう1つの便利な方法は、addStatusListenerです。これにより、そのライフサイクルを利用できるようになります。 コントローラーは、値が上限または下限に達するたびに、完了または却下されたイベントをトリガーします。 これをforwardおよびreverseメソッドで使用して、アニメーションを無限にループさせることができます。

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation animation;

  void initState() {
    super.initState();
    controller = AnimationController(
        duration: Duration(seconds: 1), 
        vsync: this);
    animation = CurvedAnimation(parent: controller, curve: Curves.slowMiddle);

    controller.forward();
    animation.addListener(() => setState(() {}));

    controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) controller.reverse(from: 400);
      else if (status == AnimationStatus.dismissed) controller.forward();
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          margin: EdgeInsets.only(bottom: animation.value * 400),
          width: 50,
          height: 50,
          color: Colors.red),
      ),
    );
  }
}

トゥイーン

数値だけでなく、トゥイーン(betweenの略)を使用して、色などの他の範囲を操作することもできます。 湾曲したアニメーションと同様に、コントローラーまたはアニメーションのラッパーとして機能し、色をその値に設定できます。

main.dart
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation animation;
  Animation changeColor;

  void initState() {
    super.initState();
    controller =
        AnimationController(duration: Duration(seconds: 1), vsync: this);
    animation = CurvedAnimation(parent: controller, curve: Curves.slowMiddle);
    changeColor = ColorTween(begin: Colors.red, end: Colors.blue).animate(animation);
    controller.forward();
    animation.addListener(() => setState(() {}));

    controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) controller.reverse(from: 400);
      else if (status == AnimationStatus.dismissed) controller.forward();
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          margin: EdgeInsets.only(bottom: animation.value * 400),
          width: 50,
          height: 50,
          color: changeColor.value),
      ),
    );
  }
}

クロススクリーン

もう1つの優れた手法は、異なる画面上のウィジェット間で遷移が必要な場合にHeroウィジェットを使用することです。 それぞれを、一致するタグを使用して独自のHeroウィジェットでラップする必要があります。 タグは一意である必要があり、画面上に複数存在することはできません。

firstScreen
Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
      SizedBox(height: 1),
      Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: <Widget>[
          Hero(tag: 'icon', child: Icon(Icons.add)),
        ]),
      Navbar()
  ]));
}
secondScreen
Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
        SizedBox(height: 1),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: <Widget>[
            SizedBox(width: 1),
            Hero(
              tag: 'icon',
              child: Icon(Icons.add, color: Colors.red, size: 75)),
          ]),
        Navbar()
      ]),
  );
}

結論

過去のほとんどのフロントエンドWebテクノロジーでは、アニメーションは後付けとして残されていましたが、この素晴らしいフレームワークを開発する際に、フラッターチームはアニメーションを念頭に置いて素晴らしい仕事をしました。 私たちは実際、アニメーションに関してFlutterができることのほんの一部にすぎませんでした。