コンサルティング事業 2021.01.19

Flutter × UI #08 ~ agexコーポレートサイトをトレースしてみた (ブログアイテム編 part2) ~

Transformを利用して、Widget全体がズームイン・ズームアウトするアニメーションを作成する

前回までのおはなし
○ ヘッダー編
Flutter × UI #01 ~ agexコーポレートサイトをトレースしてみた (ヘッダー編) ~

○ スライダー編
Flutter × UI #02 ~ agexコーポレートサイトをトレースしてみた (スライダー編 part1) ~
Flutter × UI #03 ~ agexコーポレートサイトをトレースしてみた (スライダー編 part2) ~
Flutter × UI #04 ~ agexコーポレートサイトをトレースしてみた (スライダー編 part3) ~
Flutter × UI #05 ~ agexコーポレートサイトをトレースしてみた (スライダー編 part4) ~
Flutter × UI #06 ~ agexコーポレートサイトをトレースしてみた (スライダー編 part5) ~

○ ブログアイテム編
Flutter × UI #07 ~ agexコーポレートサイトをトレースしてみた (ブログアイテム編 part1) ~

今回のおはなし
今回は、弊社HPの「ブログアイテム」をFlutterでトレースした際のおはなし part2です。

引きづづき、実装を進めていきます。

[ここまでの成果]
アイキャッチ画像がブログアイテム上部に配置される(以降、アイキャッチ要素)
アイキャッチ画像下部に、やや左側にずれる形でタイトルを含む矩形要素が配置される(以降、タイトル要素)
・ ブログアイテムは、表示時ズームインアニメーションで表示される
・ ブログアイテムは、マウスホバー時にアイテム全体がややズームされる

再現してみる
今回は、「#07 ブログアイテム編 part1」で作成したブログアイテムの基本形に、アニメーションをつけていきます。

ブログアイテムは、表示時ズームインアニメーションで表示される
これは、「animate_do」というパッケージを使用して実装することにします。

このパッケージで提供されている「ZoomIn」という機能(Widget)を利用することで、表示時のズームインアニメーションを再現することができます。

class BlogItem extends StatelessWidget {
  BlogItem({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ZoomIn( // 「ZoomIn」Widgetでブログアイテムをラップする
        duration: Duration(milliseconds: 300), // durationを設定
        child: ConstrainedBox(
          constraints: const BoxConstraints.expand(
            width: 370,
            height: 380,
          ),
        
          ・
          ・
          ・

        ),
      ),
    );
  }
}

たったこれだけの実装変更で、以下のようなズームインアニメーションをブログアイテムに追加することができます。

ブログアイテムは、マウスホバー時にアイテム全体がややズームされる
マウスホバーのイベント処理については、今までと同様に「MouseRegion」Widgetを利用します。「Stack」Widgetをラップする形で「MouseRegion」を利用してしまうと、「Stack」Widget内における、アイキャッチ要素・タイトル要素が存在しない箇所にマウスが移動した際にも、「onEnter」イベントが発火してしまいます。そのため今回は、アイキャッチ要素・タイトル要素それぞれを「MouseRegion」Widgetでラップします。

Stack(
  children: [
    Positioned(
      left: 70,
      top: 0,
      child: MouseRegion(
        onEnter: (detail) => _onEnterHandler(detail, context),
        onExit: (detail) => _onExitHandler(detail, context),
        child: Container( // アイキャッチ要素
          width: 300,
          height: 250,
          decoration: BoxDecoration(
            image: DecorationImage(
              fit: BoxFit.cover,
              image: AssetImage(‘lib/assets/images/eyecatch.png’),
            ),
          ),
        ),
      ),
    ),
    Positioned(
      left: 0,
      top: 180,
      child: MouseRegion(
        onEnter: (detail) => _onEnterHandler(detail, context),
        onExit: (detail) => _onExitHandler(detail, context),
        child: Container( // タイトル要素
          width: 300,
          height: 200,
          color: Colors.lightBlue[300],
          child: Center(
            child: Text(
              “Title”,
              style: TextStyle(
                color: Colors.white,
                fontSize: 40
              ),
            ),
          ),
        ),
      ),
    ),
  ],
),

void _onEnterHandler(PointerEvent details, BuildContext context) {
  // ズームイン
}

void _onExitHandler(PointerEvent details, BuildContext context) {
  // ズームアウト
}

これで、アイキャッチ要素・タイトル要素それぞれにマウスホバーイベント処理の準備ができました。ここで準備した各ハンドラーに対して、ズームイン・ズームアウトの処理を記述すれば良いことになります。

ズームイン・ズームアウトアニメーションについては「#05 スライダー編 part4」でも紹介したように、animationControllerを利用してスケール値を遷移させ、そのスケール値をWidgetのスケールとして利用することで実現することができます。

今回はWidget全体のスケールを変化させる必要があるため、「Transform.scale」を利用します。このWidgetの「scale」プロパティに対して、上記のスケール値をセットすることで、子要素をそのスケール値に応じたサイズに変化させることが出来きます。

// StatefulWidgetに変更
class BlogItem extends StatefulWidget {
  BlogItem({Key key}) : super(key: key);

  @override
  _BlogItemState createState() => _BlogItemState();
}

class _BlogItemState extends State with TickerProviderStateMixin {
  AnimationController zoomController;
  Animation zoomAnimation;
  Animation zoomCurvedAnimation;

  @override
  void initState() {
    super.initState();
    zoomController = AnimationController(
      duration: const Duration(milliseconds: 50),
      vsync: this,
    );

    zoomController.addListener(() {
      setState(() {});
    });

    zoomCurvedAnimation = CurvedAnimation(
      parent: zoomController,
      curve: Curves.linear,
    );

    zoomAnimation = Tween(
      begin: 1.0,
      end: 1.03,
    ).animate(zoomCurvedAnimation);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Transform.scale(
        scale: zoomAnimation.value,
        child: ZoomIn(
        
          ・
          ・
          ・

        ),
      ),
    );
  }

  void _onEnterHandler(PointerEvent details, BuildContext context) {
    zoomController.stop();
    zoomController.forward();
  }

  void _onExitHandler(PointerEvent details, BuildContext context) {
    zoomController.stop();
    zoomController.reverse();
  }
}

これにより、以下のようにアイキャッチ要素・タイトル要素へのマウスホバー時に、ブログアイテム全体がズームされ、マウスが要素外へ移動した際にズームアウトするようなアニメーションを付けることができます。

なんと、この実装によりブログアイテムの実装が完了してしまいました!
[ここまでの成果]
アイキャッチ画像がブログアイテム上部に配置される(以降、アイキャッチ要素)
アイキャッチ画像下部に、やや左側にずれる形でタイトルを含む矩形要素が配置される(以降、タイトル要素)
ブログアイテムは、表示時ズームインアニメーションで表示される
ブログアイテムは、マウスホバー時にアイテム全体がややズームされる

要素が少ないこともあり、あっさり実装を終えることができました!

次回からは、「ブログリスト編」を開始しようと思います。これがまた難しい。。。

今日の一言
今までの記事で紹介したことの組み合わせで、様々な要素を再現できるようになってきました。まだまだ積み重ねが足りないので引き続き学習を続けながら、学んだことをご紹介できればと思います!

この記事をシェアする
事業について詳しく知る

コンサルティング事業について詳しく知りたい方はこちら

RECOMMEND

おすすめ記事