コンサルティング事業 2021.01.14

Flutter × UI #06 ~ agexコーポレートサイトをトレースしてみた (スライダー編 part5) ~

「AnimatedSwitcher」Widgetを利用して、Textがフェードアニメーションで変化するような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) ~

今回のおはなし
今回は、弊社HPの「スライダー」をFlutterでトレースした際のおはなし part5です。

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

[ここまでの成果]
アイテムの形は平行四辺形
アイテムは水平方向に、一定間隔で配列されている
アイテムは一定時間間隔で自動的にスライドされる
アイテムのスライドは左方向
アイテムが最左端へスライドする際、アイテムのサイズが大きくなる
最左端のアイテムのみ、最右端へスライドする
最右端へのスライド時、アイテムはフェードアウトアニメーションがついている
最右端へのスライド時、アイテムは他要素の後ろ側を通過する
マウスがアイテム内へ移動すると、アイテム内画像がズームされる
マウスがアイテム外へ移動すると、アイテム内画像は元サイズへズームアウトされる
・ スライダー下部には、最左端アイテムについてのアイテム説明が配置される
・ アイテム説明は、アイテムのスライドに合わせてフェードアニメーションによって切り替わる
・ スライダー下部のアイテム説明右側に、スライドボタンが配置される
・ マウスがスライドボタン内に移動すると、スライドボタンは各方向に少し移動する
・ マウスがスライドボタン外に移動すると、スライドボタンは元の位置に移動する

再現してみる
今回で「スライダー編」は最終回です!
一気に進めていきましょう!

スライダー下部には、最左端アイテムについてのアイテム説明が配置される
アイテム説明は、アイテムのスライドに合わせてフェードアニメーションによって切り替わる
「アイテム説明」では、水平方向に並べた最左端のアイテムについての情報を表示します。
また、最左端のアイテムが切り替わる際に、「アイテム説明」がフェードアニメーションによって新たに最左端へと移動するアイテムの情報に変化します。

このフェードアニメーションによる「アイテム説明」の切り替わりは、「AnimatedSwitcher」Widgetによって実現することができます。

List titleList = [
  ‘page1’,
  ‘scene2’,
  ‘item3’,
];

AnimatedSwitcher(
  duration: Duration(seconds: 1),
  child: Container(
    key: ValueKey(i), // iはタイマーで遷移させる
    width: 200,
    child: Text(
      titleList[i],
      style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold),
    ),
  ),
),

これにより、リストに格納した文字列を、下記画像のようにフェードアニメーションによって遷移させることができます。

このアニメーションと、スライダーのアニメーションを連動させたいので、「アイテム説明」内の情報を格納するリストのインデックスには、「tIndex」を利用します。
(「tIndex」については「#03 スライダー編 part2」をご覧ください)

class ItemInfo extends StatelessWidget {
  final itemCount;
  final tIndex;

  ItemInfo({Key key, this.itemCount, this.tIndex}) : super(key: key);

  final List titleList = [
    ‘page1’,
    ‘scene2’,
    ‘item3’,
  ];

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(20.0),
      child: AnimatedSwitcher(
        duration: Duration(seconds: 1),
        child: Container(
          key: ValueKey(itemCount – tIndex), // iはタイマーで遷移させる
          width: 400,
          child: Text(
            titleList[itemCount – tIndex],
            style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold),
          ),
        ),
      ),
    );
  }
}

この「ItemInfo」Widgetと、すでに作成しているスライダー(リンク)を「Stack」Widget上で組み合わせると、下記のように、「アイテム説明」の遷移をスライダー上のアイテムの遷移に連動させることができます。

マウスがスライドボタン内に移動すると、スライドボタンは各方向に少し移動する
マウスがスライドボタン外に移動すると、スライドボタンは元の位置に移動する
これは、毎度おなじみ「MouseRegion」Widgetで実現することができます。
(「#01 ヘッダー編」「#05 スライダー編 part4」などで利用しています)

「MouseRegion」Widgetの onEnter / onExitそれぞれに、Widgetの位置を変化させるメソッドを登録します。

Widgetの位置変化については、「#03 スライダー編 part2」でも利用した「AnimatedPositioned」Widgetを利用します。

class CustomButton extends StatefulWidget {
  final onPressHandler; // tIndexを変化させるメソッドを受け取る。ボタン押下時にコールする。

  const CustomButton({Key key, this.onPressHandler}) : super(key: key);

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

class _CustomButtonState extends State {
  double position = 0;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 45,
      height: 45,
      child: Stack(
        alignment: Alignment.center,
        children: [
          AnimatedPositioned(
            right: position,  // この「position」の値を変化させることで、ボタンの移動を実現する。
            duration: Duration(milliseconds: 100),
            child: IconButton(
              icon: MouseRegion(
                onEnter: (details) => _onEnterHandler(details, context),
                onExit: (details) => _onExitHandler(details, context),
                child: Icon(
                  Icons.arrow_back_ios,
                ),
              ),
              iconSize: 30,
              onPressed: () {
                /* tIndexを変化させるメソッドをコールする */
                widget.onPressHandler();
              },
              focusColor: Colors.transparent,
              hoverColor: Colors.transparent,
            ),
          ),
        ],
      ),
    );
  }

  void _onEnterHandler(PointerEvent details, BuildContext context) {
    position = 5;
    setState(() {});
  }

  void _onExitHandler(PointerEvent details, BuildContext context) {
    position = 0;
    setState(() {});
  }
}

この「CustomButton」Widgetをベースに、右側 / 左側のスライドボタンそれぞれ作成します。

スライダー下部のアイテム説明右側に、スライドボタンが配置される
「アイテム説明」「スライドボタン(左側)」「スライドボタン(右側)」の順で、水平方向に並べるとよいので、「#01 ヘッダー編」でも利用した「Row」Widgetを利用して、上記の配置を実現していきます。

Row(
  children: [
    // 「アイテム説明」Widget
    ItemInfo(
      itemCount: itemCount,
      tIndex: tIndex,
    ),
     //「スライドボタン(左側)」Widget
    CustomBackButton(
      onPressHandler: _onPressBackHandler,
    ),
     //「スライドボタン(左側)」Widget
    CustomForwardButton(
      onPressHandler: _onPressForwardHandler,
    ),
  ],
)

// tIndexを-1後、タイマーをリセット
void _onPressForwardHandler() {
  tIndex = (tIndex == 1) ? widget.itemCount : tIndex = tIndex – 1;
  timer.cancel();
  timer = Timer.periodic(
    Duration(seconds: 3),
    _timerHandler,
  );
  setState(() {});
}

// tIndexを+1後、タイマーをリセット
void _onPressBackHandler() {
  tIndex = (tIndex == widget.itemCount) ? 1 : tIndex + 1;
  timer.cancel();
  timer = Timer.periodic(
    Duration(seconds: 3),
    _timerHandler,
  );
  setState(() {});
}

上記の「Row」Widgetと、すでに作成済みのスライダーを組み合わせると、、ついに!!!

ついに!とうとう!やっと!!スライダーが完成しました!!!
長かった、、、いや長かった、、、非常に長かった、、、

[ここまでの成果]
アイテムの形は平行四辺形
アイテムは水平方向に、一定間隔で配列されている
アイテムは一定時間間隔で自動的にスライドされる
アイテムのスライドは左方向
アイテムが最左端へスライドする際、アイテムのサイズが大きくなる
最左端のアイテムのみ、最右端へスライドする
最右端へのスライド時、アイテムはフェードアウトアニメーションがついている
最右端へのスライド時、アイテムは他要素の後ろ側を通過する
マウスがアイテム内へ移動すると、アイテム内画像がズームされる
マウスがアイテム外へ移動すると、アイテム内画像は元サイズへズームアウトされる
スライダー下部には、最左端アイテムについてのアイテム説明が配置される
アイテム説明は、アイテムのスライドに合わせてフェードアニメーションによって切り替わる
スライダー下部のアイテム説明右側に、スライドボタンが配置される
マウスがスライドボタン内に移動すると、スライドボタンは各方向に少し移動する
マウスがスライドボタン外に移動すると、スライドボタンは元の位置に移動する

次回からは、「ブログアイテム編」が始まります。
久しぶりの新シリーズですが、こつこつ地道に進めていきたいと思います!

今日の一言
達成感。。。

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

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

RECOMMEND

おすすめ記事