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

# 作ったモノ 

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

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510027/6fd8b0f7-e30e-c7cd-b0b2-2b0eb19635c1.gif" alt="AccordionIconButton.gif" style="zoom:67%;" />



# 実装

<details><summary>全ソース</summary><div>


```dart
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,
            ))
          ],
        ),
      ),
    );
  }
}

```

</div></details>

[Github](https://github.com/yakitama5/accordion_button)

# 説明 

非常にシンプルです。



### マウスホバー判断 :mouse_three_button:

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

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

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



### アニメ―ション表示 :film_frames:

Animated系ウィジェットを使用して実現しています。
詳しい解説は公式や[こちらのサイト](https://medium.com/flutter-jp/implicit-animation-b9d4b7358c28)が非常に参考になります。

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



# まとめ

**たのしい！！！(KONAMI感)**😊

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

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