本記事は、マルチプラットフォーム開発のSDK【Flutter】を用いたモバイルアプリ開発入門の為の記事です。
 ダーフク
ダーフクモバイルアプリでは、アニメーションを用いることで視認性の高いアプリを開発することができます。本記事では、Flutterに標準装備されているAnimatedIconsについて簡単に解説します。
利用可能なアイコン
AnimatedIconで利用可能なアイコンは合計14種類あります。
そしてその14種類は双方向の遷移が準備されているため(例えば、追加アイコンからイベントアイコンと、イベントアイコンから追加アイコンへの2つのアニメーションがある)、実質7パターン存在することになります。
- 追加 <=> イベント
- 左矢印 <=> メニュー
- バツ <=> メニュー
- 3点リーダー <=> 検索
- ホーム <=> メニュー
- リスト <=> ビュー
- 再生 <=> 停止

実装方法
まず、アニメーションを使いたいWidgetにSingleTickerProviderStateMixinを実装します。
class _HomeScreenState extends State<HomeScreen>
  with SingleTickerProviderStateMixin {
}その後、AnimationControllerを定義し、initState内で初期化、dispose内でStateがdisposeされる前にdisposeします。
AnimationController _controller;
@override
void initState() {
  super.initState();
  _controller = AnimationController(
    duration: const Duration(milliseconds: 300),
    vsync: this,
  );
}
@override
void dispose() {
  _controller.dispose();
  super.dispose();
}最後にAnimatedIconの実装です。iconプロパティにAnimatedIconを指定し、progressにAnimationControllerを設定します。
AnimatedIcon(
  icon: AnimatedIcons.arrow_menu,
  progress: _controller,
),そしてボタン等を用意して、それが押下されるとアニメーションが走り、アイコンが変わります。
floatingActionButton: FloatingActionButton(
  child: Icon(Icons.check),
  onPressed: () async {
    await _controller.forward();
    await Future.delayed(Duration(seconds: 2));
    await _controller.reverse();
  },
),これだけでAnimatedIconを用いたアニメーションは実装することができます。
ソースコードの全容
import 'package:flutter/material.dart';
void main() {
  runApp(new UserApp());
}
class UserApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}
class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 300),
      vsync: this,
    );
  }
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Animated Icons Sample"),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Center(
          child: Column(
            children: <Widget>[
              _buildAnimatedIconsCard(
                AnimatedIcons.add_event,
                AnimatedIcons.event_add,
                label1: "add_event",
                label2: "event_add",
              ),
              SizedBox(height: 10.0),
              _buildAnimatedIconsCard(
                AnimatedIcons.arrow_menu,
                AnimatedIcons.menu_arrow,
                label1: "arrow_menu",
                label2: "menu_arrow",
              ),
              SizedBox(height: 10.0),
              _buildAnimatedIconsCard(
                AnimatedIcons.close_menu,
                AnimatedIcons.menu_close,
                label1: "close_menu",
                label2: "menu_close",
              ),
              SizedBox(height: 10.0),
              _buildAnimatedIconsCard(
                AnimatedIcons.ellipsis_search,
                AnimatedIcons.search_ellipsis,
                label1: "ellipsis_search",
                label2: "search_ellipsis",
              ),
              SizedBox(height: 10.0),
              _buildAnimatedIconsCard(
                AnimatedIcons.home_menu,
                AnimatedIcons.menu_home,
                label1: "home_menu",
                label2: "menu_home",
              ),
              SizedBox(height: 10.0),
              _buildAnimatedIconsCard(
                AnimatedIcons.list_view,
                AnimatedIcons.view_list,
                label1: "list_view",
                label2: "view_list",
              ),
              SizedBox(height: 10.0),
              _buildAnimatedIconsCard(
                AnimatedIcons.play_pause,
                AnimatedIcons.pause_play,
                label1: "play_pause",
                label2: "pause_play",
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.check),
        onPressed: () async {
          await _controller.forward();
          await Future.delayed(Duration(seconds: 2));
          await _controller.reverse();
        },
      ),
    );
  }
  Widget _buildAnimatedIconWidget(AnimatedIconData icon,
      {@required String label}) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        AnimatedIcon(
          icon: icon,
          progress: _controller,
        ),
        Text(label),
      ],
    );
  }
  Widget _buildAnimatedIconsCard(
    AnimatedIconData icon1,
    AnimatedIconData icon2, {
    @required String label1,
    @required String label2,
  }) {
    return Card(
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          _buildAnimatedIconWidget(
            icon1,
            label: label1,
          ),
          SizedBox(width: 40.0),
          _buildAnimatedIconWidget(
            icon2,
            label: label2,
          ),
        ],
      ),
    );
  }
}まとめ
本記事ではAnimatedIconについて解説をしました。Flutterはこれ以外にも簡単にアニメーションが実装できるWidgetを多数揃えています。
それらについてもまた別の記事で解説していきたいと思います。

