[Flutter Web] マウスホバー時に展開されるアイコンボタンを作ってみる


作ったモノ

マウスホバーによってラベルが表示されるアイコンボタンをイメージしました。
マウスホバー時なので、Flutter Web想定です。

実装

全ソース
class AccordionIconButton extends StatefulWidget {
  final IconData _iconData;
  final double _iconSize;
  final String _label;
  final double _maxWidth;
  final void Function()? _onTap;

  const AccordionIconButton(
      {Key? key,
      required IconData iconData,
      required double iconSize,
      required String label,
      required double maxWidth,
      void Function()? onTap})
      : _iconData = iconData,
        _iconSize = iconSize,
        _label = label,
        _maxWidth = maxWidth,
        _onTap = onTap,
        super(key: key);

  @override
  State<StatefulWidget> createState() => _State(width: this._iconSize);
}

class _State extends State<AccordionIconButton> {
  static const double INITIAL_OPACITY = 0.0;

  double opacity;
  double width;

  _State({
    required this.width,
  })  : opacity = INITIAL_OPACITY,
        super();

  @override
  Widget build(BuildContext context) {
    final Duration duration = Duration(milliseconds: 250);
    final Widget iconWidget = Icon(
      widget._iconData,
      size: widget._iconSize,
    );
    final Widget textWidget = Text(
      widget._label,
      softWrap: false,
      overflow: TextOverflow.fade,
    );

    return AnimatedContainer(
      constraints: BoxConstraints(maxWidth: widget._maxWidth),
      width: this.width,
      curve: Curves.easeIn,
      duration: duration,
      child: InkWell(
        borderRadius: BorderRadius.all(Radius.circular(10.0)),
        onHover: (isHover) {
          setState(() {
            if (isHover) {
              opacity = 1.0;
              width = double.infinity;
            } else {
              opacity = INITIAL_OPACITY;
              width = widget._iconSize;
            }
          });
        },
        onTap: widget._onTap,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            iconWidget,
            Flexible(
                child: AnimatedOpacity(
              curve: Curves.easeIn,
              duration: duration,
              opacity: this.opacity,
              child: textWidget,
            ))
          ],
        ),
      ),
    );
  }
}

Github

説明

非常にシンプルです。

マウスホバー判断

InkWellクラスのonHover(bool)で検知出来ます。
ホバー状態を判断して全体の幅テキスト項目の透明度を変更しているだけです。

その他にもMouseRegionクラス等でもマウスの判定は可能です。
(こちらはマウスカーソルの位置等まで追跡可能)

      InkWell(
        onHover: (isHover) {
          setState(() {
            if (isHover) {
              opacity = 1.0;
              width = double.infinity;
            } else {
              opacity = INITIAL_OPACITY;
              width = widget._iconSize;
            }
          });
        },
        ...
      )

アニメ―ション表示

Animated系ウィジェットを使用して実現しています。
詳しい解説は公式やこちらのサイトが非常に参考になります。

AnimatedContainer(
      constraints: BoxConstraints(maxWidth: widget._maxWidth),
      width: this.width, // ←widthが変更された際に指定されたアニメーションでWidgetが変化する
      curve: Curves.easeIn,
      duration: duration,
      ...
)

まとめ

たのしい!!!(KONAMI感)😊

少し触った程度ですがマウス操作の検知等モバイル以外の用途でも使いやすいライブラリがそろってる印象でした。

コピペ出来なかったり、ホイールクリックが効かなかったり等まだまだありますが、
FlutterWebでも色々作っていきたくなりますね!