【Flutter】アニメーション1(Animated系)【日本語】

Flutterのアニメーションについてまとめ。

今回は簡単なアニメーションを実装するときに使う(らしい)Animated系をまとめてみました。

※他のプログラミングに関する記事はこちら

※本記事は以下記事を参考にさせて頂きました

 

スポンサーリンク

前提

画面更新とフローティングアクションボタンに関する知識はある前提で説明していきます。

それぞれ過去にまとめたので、もし不明である場合はチェックして頂ければ。

画面更新

Flutterの初歩である画面の更新を自分なりにメモ。 Flutterのアプリ作成したときに最初から作成されているカウンターアプリが既に画面更新をしているので、それを元に画面の更新について記載。 オマケで(というかオマケの方が記[…]

フローティングアクションボタン

Flutterでフローティングアクションボタン(FloatingActionButton)を作成してみた。 オマケで複数のフローティングアクションボタンを設定する方法もメモ。 他のプログラミングに関する記事はこちら スポン[…]

スポンサーリンク

透明化(AnimatedOpacity)

徐々に消したり、逆に徐々に表示させたりする場合は透明化(AnimatedOpacity)を使います。

とりあえず「全コード」をmain.dartにコピペすれば、「動き」みたいな動きになります。

詳細は「説明」に記載します。

全コード

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.title}) : super(key: key);
  final String? title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  bool flag = false;

  _click() async {
    setState(() {
      flag = !flag;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title!),
      ),
      body: Center(
        child: AnimatedOpacity(
          opacity: flag ? 0.0 : 1.0,
          duration: Duration(seconds: 3),
          child: Text(
            "消える文字",
          )),
        ),
      floatingActionButton:
          Row(mainAxisAlignment: MainAxisAlignment.end, children: [
        FloatingActionButton(onPressed: _click, child: Icon(Icons.add)),
      ]),
    );
  }
}

動き

説明

重要なのは43~48行目の以下部分です。

child: AnimatedOpacity(
  opacity: flag ? 0.0 : 1.0,
  duration: Duration(seconds: 3),
  child: Text(
    "消える文字",
  )),

 

AnimatedOpacityのopacityに透明化のレベルを、durationにアニメーションの時間を、childに透明化したいウィジェットを記載します。

物凄く簡単ですね。

上記の内容だと、「frag」変数がtrueになった場合は1.0から0.0(徐々に消える)、「frag」変数が「false」になった場合は0.0から1.0(徐々に表示される)、という動作を3秒かけて行っています。

 

で、frag変数はフローティングアクションボタンをクリックしたときに「_click」メソッドが呼ばれて(以下記述)

floatingActionButton:
    Row(mainAxisAlignment: MainAxisAlignment.end, children: [
  FloatingActionButton(onPressed: _click, child: Icon(Icons.add)),
]),

 

「_click」内の「setState」メソッドによる変数の更新&画面の更新を行っている感じ(以下記述)ですね。

_click() async {
  setState(() {
    flag = !flag;
  });
}

スポンサーリンク

拡大化(AnimatedSize)

徐々に大きくさせる場合は拡大化(AnimatedSize)を使います。

とりあえず「全コード」をmain.dartにコピペすれば、「動き」みたいな動きになります。

詳細は「説明」に記載します。

※多分AnimatedSizeでは徐々に小さくする、といったことは出来ません

全コード

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.title}) : super(key: key);
  final String? title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  double d = 50;

  _click() async {
    setState(() {
      if (d != 200) {
        d = 200;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title!),
      ),
      body: Center(
        child: AnimatedSize(
          vsync: this,
          duration: Duration(seconds: 3),
          child: SizedBox(
              width: d,
              height: d,
              child: Container(color: Colors.purple))),
        ),
      floatingActionButton:
          Row(mainAxisAlignment: MainAxisAlignment.end, children: [
        FloatingActionButton(onPressed: _click, child: Icon(Icons.add)),
      ]),
    );
  }
}

 

動き

説明

重要なのは45~52行目の以下部分です。

child: AnimatedSize(
  vsync: this,
  duration: Duration(seconds: 3),
  child: SizedBox(
      width: d,
      height: d,
      child: Container(color: Colors.purple))),
),

 

AnimatedSizeのdurationにアニメーションの時間を、childにウィジェットを入れてwidthとheightに変数で値を記載します。

vsyncには調べたところ何をやっているのかよくわかりませんでしたが、とりあえずthisを設定しておけばよさげ。

 

で、widthとheightに設定している変数「d」はフローティングアクションボタンをクリックしたときに「_click」メソッドが呼ばれて(以下記述)

floatingActionButton:
    Row(mainAxisAlignment: MainAxisAlignment.end, children: [
  FloatingActionButton(onPressed: _click, child: Icon(Icons.add)),
]),

 

「_click」内の「setState」メソッドによる変数の更新&画面の更新を行っている感じ(以下記述)ですね。

_click() async {
  setState(() {
    if (d != 200) {
      d = 200;
    }
  });

 

dには初期値が「50」を設定しているので、まだ拡大化してなければ「200」まで拡大させて、もう拡大化済(d == 200)であれば変数の変更はしない、みたいな動きにしています。

備考

冒頭にも書いた通り、おそらくAnimatedSizeでは徐々に小さくする、といったことは出来ません。

もし徐々に小さくしたい場合は後述する「AnimatedContainer」を使えば実装できます。

 

試しに以下みたいなコードを最初作成してみましたが、

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.title}) : super(key: key);
  final String? title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  bool flag = false;

  _click() async {
    setState(() {
      flag = !flag;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title!),
      ),
      body: Center(
        child: AnimatedSize(
          vsync: this,
          duration: Duration(seconds: 3),
          child: SizedBox(
              width: flag ? 50 : 200,
              height: flag ? 50 : 200,
              child: Container(color: Colors.purple))),
        ),
      floatingActionButton:
          Row(mainAxisAlignment: MainAxisAlignment.end, children: [
        FloatingActionButton(onPressed: _click, child: Icon(Icons.add)),
      ]),
    );
  }
}

 

frag変数のtrueとfalseを切り替えても(widthとheightを50から200へ交互に切り替えても)小さくするときだけすぐに小さくなってしまいました。

 

他にも以下みたいなコードにもしてみましたが(フローティングアクションボタンをクリックすることでwidthとheightを200から50に小さくする)

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.title}) : super(key: key);
  final String? title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  double d = 200;

  _click() async {
    setState(() {
      if (d != 50) {
        d = 50;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title!),
      ),
      body: Center(
        child: AnimatedSize(
          vsync: this,
          duration: Duration(seconds: 3),
          child: SizedBox(
              width: d,
              height: d,
              child: Container(color: Colors.purple))),
        ),
      floatingActionButton:
          Row(mainAxisAlignment: MainAxisAlignment.end, children: [
        FloatingActionButton(onPressed: _click, child: Icon(Icons.add)),
      ]),
    );
  }
}

 

以下みたいな動きになりました。

 

以上のことから、AnimatedSizeでは徐々に小さくする、といったことは出来ないのかと。

スポンサーリンク

移動(AnimatedAlign)

移動させる場合には移動(AnimatedAlign)を使います。

とりあえず「全コード」をmain.dartにコピペすれば、「動き」みたいな動きになります。

詳細は「説明」に記載します。

全コード

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.title}) : super(key: key);
  final String? title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  bool flag = false;

  _click() async {
    setState(() {
      flag = !flag;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title!),
      ),
      body: Center(
        child: AnimatedAlign(
          duration: Duration(seconds: 3),
          alignment: flag ? Alignment.topLeft : Alignment.bottomRight,
          child: SizedBox(
              width: 50,
              height: 50,
              child: Container(color: Colors.green)
          )
        ),
      ),
      floatingActionButton:
          Row(mainAxisAlignment: MainAxisAlignment.end, children: [
        FloatingActionButton(onPressed: _click, child: Icon(Icons.add)),
      ]),
    );
  }
}

動き

説明

重要なのは43~51行目の以下部分です。

child: AnimatedAlign(
  duration: Duration(seconds: 3),
  alignment: flag ? Alignment.topLeft : Alignment.bottomRight,
  child: SizedBox(
      width: 50,
      height: 50,
      child: Container(color: Colors.green)
  )
),

 

AnimatedAlignのdurationにアニメーションの時間を、alignmentに移動元と移動先の座標を、childに移動対象を記載します。

上記の内容だと、「frag」変数がtrueになった場合は右下から左上に移動、「frag」変数が「false」になった場合は左上から右下に移動、という動作を3秒かけて行っています。

 

で、frag変数はフローティングアクションボタンをクリックしたときに「_click」メソッドが呼ばれて(以下記述)

floatingActionButton:
    Row(mainAxisAlignment: MainAxisAlignment.end, children: [
  FloatingActionButton(onPressed: _click, child: Icon(Icons.add)),
]),

 

「_click」内の「setState」メソッドによる変数の更新&画面の更新を行っている感じ(以下記述)ですね。

透明化の記述とほぼ同じ。

_click() async {
  setState(() {
    flag = !flag;
  });
}

スポンサーリンク

複合アニメーション(AnimatedContainer)

今まで紹介した透明化とか移動とかを同時に行いたい場合(同時に色々なアニメーションをつけたい場合)はAnimatedContainerを使います。

とりあえず「全コード」をmain.dartにコピペすれば、「動き」みたいな動きになります。

詳細は「説明」に記載します。

全コード

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.title}) : super(key: key);
  final String? title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  bool flag = false;

  _click() async {
    setState(() {
      flag = !flag;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title!),
      ),
      body: Center(
        child: AnimatedContainer(
          duration: Duration(seconds: 3),
          width: flag ? 100 : 50,
          height: flag ? 50 : 100,
          padding: flag ? EdgeInsets.all(0) : EdgeInsets.all(30),
          margin: flag ? EdgeInsets.all(0) : EdgeInsets.all(30),
          transform: flag ? Matrix4.skewX(0.0) : Matrix4.skewX(0.3),
          color: flag ? Colors.blue : Colors.grey),
        ),
      floatingActionButton:
        Row(mainAxisAlignment: MainAxisAlignment.end, children: [
        FloatingActionButton(onPressed: _click, child: Icon(Icons.add)),
      ]),
    );
  }
}

 

動き

説明

重要なのは43~51行目の以下部分です。

child: AnimatedContainer(
  duration: Duration(seconds: 3),
  width: flag ? 100 : 50,
  height: flag ? 50 : 100,
  padding: flag ? EdgeInsets.all(0) : EdgeInsets.all(30),
  margin: flag ? EdgeInsets.all(0) : EdgeInsets.all(30),
  transform: flag ? Matrix4.skewX(0.0) : Matrix4.skewX(0.3),
  color: flag ? Colors.blue : Colors.grey),
),

説明しきれないので省きますが、色々なオプションがあるのでそれらを設定してあげれば複合アニメーションを実装できます。

どんなオプションがあるのかは以下公式サイトに載っているので、必要に応じて調べ調べ設定してあげればいいかな、と。

Flutter Doc JP

Flutterの日本語解説配信サイト…

 

他の動きは透明化とかと同じですね。

frag変数はフローティングアクションボタンをクリックしたときに「_click」メソッドが呼ばれて(以下記述)

floatingActionButton:
    Row(mainAxisAlignment: MainAxisAlignment.end, children: [
  FloatingActionButton(onPressed: _click, child: Icon(Icons.add)),
]),

 

「_click」内の「setState」メソッドによる変数の更新&画面の更新を行っている感じ(以下記述)です。

_click() async {
  setState(() {
    flag = !flag;
  });
}

備考(徐々に縮小)

拡大化(AnimatedSize)で出来なかった徐々に縮小も、AnimatedContainerを使えば実装できます。

例えば、以下みたいなコードにすれば、

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.title}) : super(key: key);
  final String? title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  bool flag = false;

  _click() async {
    setState(() {
      flag = !flag;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title!),
      ),
      body: Center(
        child: AnimatedContainer(
          duration: Duration(seconds: 3),
          width: flag ? 50 : 100,
          height: flag ? 50 : 100,
          color: Colors.blue),
        ),
      floatingActionButton:
        Row(mainAxisAlignment: MainAxisAlignment.end, children: [
        FloatingActionButton(onPressed: _click, child: Icon(Icons.add)),
      ]),
    );
  }
}

 

クリックするたびに拡大と縮小を繰り返すアニメーションを実装できます。

スポンサーリンク

ウィジェットの切り替え(AnimatedSwitcher)

あるウィジェットからあるウィジェットに切り替える場合はAnimatedSwitcherを使います。

とりあえず「全コード」をmain.dartにコピペすれば、「動き」みたいな動きになります。

詳細は「説明」に記載します。

全コード

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.title}) : super(key: key);
  final String? title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  bool flag = false;

  _click() async {
    setState(() {
      flag = !flag;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title!),
      ),
      body: Center(
        child: AnimatedSwitcher(
          duration: Duration(seconds: 3),
          child: flag
              ? Text("なにもない")
              : Icon(Icons.favorite, color: Colors.pink))
        ),
      floatingActionButton:
        Row(mainAxisAlignment: MainAxisAlignment.end, children: [
        FloatingActionButton(onPressed: _click, child: Icon(Icons.add)),
      ]),
    );
  }
}

動き

説明

重要なのは43~47行目の以下部分です。

child: AnimatedSwitcher(
  duration: Duration(seconds: 3),
  child: flag
      ? Text("なにもない")
      : Icon(Icons.favorite, color: Colors.pink))

 

childに変更前と変更後のウィジェットを用意しておいて、flag変数で管理しているだけ。

frag変数はフローティングアクションボタンをクリックしたときに「_click」メソッドが呼ばれて(以下記述)

floatingActionButton:
    Row(mainAxisAlignment: MainAxisAlignment.end, children: [
  FloatingActionButton(onPressed: _click, child: Icon(Icons.add)),
]),

 

「_click」内の「setState」メソッドによる変数の更新&画面の更新を行っている感じ(以下記述)です。

_click() async {
  setState(() {
    flag = !flag;
  });
}

 

こちらも透明化とかと同じですね。

他のプログラミングに関する記事

プログラミングまとめ

プログラミングに関する記事です。 言語とかツールごとにまとめています。 スポンサーリンク [adcode] Flutter ・簡単導入~サンプルアプリのデバック起動まで ・文字の表示(重ねて[…]

IMG

スポンサーリンク