アニメーション付きアンダーラインはAnimatedContainerで表現する。
Text Widgetのwidth取得方法はこちらを参考に実装。
header_menu_item.dart
class HeaderMenuItem extends StatefulWidget {
final String text;
final double fontSize = 16;
const HeaderMenuItem({Key key, this.text}) : super(key: key);
@override
_HeaderMenuItemState createState() => _HeaderMenuItemState();
}
class _HeaderMenuItemState extends State<HeaderMenuItem> {
double _opacity = 0;
double _lineRatio = 0.5;
@override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (details) => _onEnterHandler(details, context),
onExit: (details) => _onExitHandler(details, context),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Container(
alignment: Alignment.center,
child: Text(
widget.text,
style: TextStyle(
fontSize: widget.fontSize,
letterSpacing: 5,
),
),
),
),
AnimatedContainer(
width: lineWidth(widget.text) * _lineRatio,
height: 2,
color: Colors.black.withOpacity(_opacity),
curve: Curves.linear,
duration: Duration(milliseconds: 200),
),
],
),
),
);
}
// マウスホバー時
void _onEnterHandler(PointerEvent details, BuildContext context) {
setState(() {
_lineRatio = 1;
_opacity = 1;
});
}
// マウスホバー解除時
void _onExitHandler(PointerEvent details, BuildContext context) {
setState(() {
_lineRatio = 0.5;
_opacity = 0;
});
}
// TextWidgetのwidthを取得
double lineWidth(String text) {
var renderParagraph = RenderParagraph(
TextSpan(
text: text,
style: TextStyle(
fontSize: widget.fontSize,
letterSpacing: 5,
),
),
textDirection: TextDirection.ltr,
)..layout(BoxConstraints(maxWidth: 200));
return renderParagraph.getMinIntrinsicWidth(widget.fontSize);
}
}
参考
https://stackoverflow.com/questions/52659759/how-can-i-get-the-size-of-the-text-widget-in-flutter