【Flutter】Widgetのフェードイン・アウトのアニメーションUIを作る

エキサイトの武藤です。

FlutterにおけるWidgetのフェードイン・アウトアニメーションUIについて簡単に紹介します。

Visibilityの表示・非表示の場合

まずは、アニメーションがない単純な表示、非表示です。 単純な表示・非表示の場合は、Visibilityで実現できます。

class _MyHomePageState extends State<MyHomePage> {
  bool isVisible = true;

  void _switchVisible() {
    setState(() {
      isVisible = !isVisible;
    });
  }

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Visibility(
          visible: isVisible,
          child: const FlutterLogo(size: 100),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _switchVisible,
        child: const Icon(Icons.touch_app),
      ),
    );
  }
}

VisibilityによるWidgetの表示・非表示

図のように、素早く切り替わります。 要件によりますが、簡単なものであればこれで十分です。

AnimatedSwitcherを使ったフェードイン・アウトのアニメーションUI

今度は、フェードイン・アウトアニメーションをつけた表示・非表示をしてみます。 AnimatedSwitcherを使ってみます。

class _MyHomePageState extends State<MyHomePage> {
  bool isVisible = true;

  void _switchAnimation() {
    setState(() {
      isVisible = !isVisible;
    });
  }

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: AnimatedSwitcher(
          duration: const Duration(milliseconds: 800),
          reverseDuration: const Duration(milliseconds: 800),
          child: isVisible
              ? const FlutterLogo(
                  key: Key('1'),
                  size: 100,
                )
              : const SizedBox(
                  key: Key('2'),
                ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _switchAnimation,
        child: const Icon(Icons.animation),
      ),
    );
  }
}

AnimatedSwitcherによるフェードイン・アウトアニメーション

表示判定フラグでFlutterLogoと空のSizedBoxを出しわけています。

AnimatedSwitcherのchildに設定するWidgetには、一意にKeyを設定することでアニメーションが動きます。

キーを設定しない場合、フレームワークからは同じものとみなされて、パラメータの更新のみを行い、アニメーションは動きません。

FadeTransitionを使ったフェードイン・アウトのアニメーションUI

次は、FadeTransitionを使って実現します。

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

  void _switchAnimation() {
    if (animationController.isCompleted) {
      animationController.reverse();
      return;
    }
    animationController.forward();
  }

  @override
  void initState() {
    animationController = AnimationController(
      duration: const Duration(milliseconds: 800),
      vsync: this,
    );

    super.initState();
  }

  @override
  void dispose() {
    animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final tweenAnimation =
        Tween(begin: 1.0, end: 0.0).animate(animationController);

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Visibility(
          visible: tweenAnimation.value != 0,
          child: FadeTransition(
            opacity: tweenAnimation,
            child: const FlutterLogo(size: 100),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _switchAnimation,
        child: const Icon(Icons.animation),
      ),
    );
  }
}

FadeTransition, Visibilityのフェードイン・アウトアニメーション

FadeTransitionとVisibility を組み合わせても、フェードイン・アウトを実現できます。

こちらはAnimationControllerでアニメーションの設定や制御をできます。 凝ったことをする場合には、こちらのほうが自由が効きそうです。

終わりに

簡単な内容ですが、Flutter Wiidgetのフェードイン・アウトアニメーションUIを紹介しました。

AnimatedOpacityを使った透明化もありますが、表示領域は残ってしまいます。操作がある画面の場合、タップできてしまい、意図した挙動にならないことがあります。 AnimatedSwitcherやFadeTransitionを使った方法では、透明化と表示領域を消すことができます。 その他にも実現方法はあるかもしれません。

どなたかの参考になれば幸いです。

採用情報

エキサイトではフロントエンジニア、バックエンドエンジニア、アプリエンジニアを随時募集しております。長期インターンも歓迎していますので、興味があれば連絡いただければと思います。

www.wantedly.com

参考

api.flutter.dev

api.flutter.dev

FadeTransitionの、opacityがゼロになったらVisibilityのvisibleがfalseになる版(透明Widgetの無駄がほぼゼロになる) · GitHub

api.flutter.dev