我正在尝试使用 Flutter 创建一个带有悬停动画的文本,其中前面有一个点。 该设计来自此网站(查看底部部分,您会发现我正在复制的内容)。
这是我的组件的代码:
import 'package:flutter/material.dart';
class MyText extends StatefulWidget {
final String text;
final TextStyle textStyle;
final double width;
final double underlineOffset;
final bool hasDot;
const MyText({
super.key,
required this.text,
required this.textStyle,
required this.width,
required this.underlineOffset,
this.hasDot = false,
});
@override
State<MyText> createState() => _MyTextState();
}
class _MyTextState extends State<MyText> with TickerProviderStateMixin {
late AnimationController _colorAnimationController;
late Animation _colorAnimation;
late AnimationController _underlineAnimationController;
late Animation _underlineAnimation;
@override
void initState() {
_colorAnimationController = AnimationController(
duration: const Duration(milliseconds: 250),
vsync: this,
)..addListener(() {
setState(() {});
});
_colorAnimation = ColorTween(
begin: Colors.black,
end: Colors.orange,
).animate(_colorAnimationController);
_underlineAnimationController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
)..addListener(() {
if (_underlineAnimationController.isCompleted &&
_underlineAnimationController.value == 1) {
_underlineAnimationController.reset();
}
setState(() {});
});
_underlineAnimation = Tween(begin: -widget.width, end: widget.width)
.animate(_underlineAnimationController);
super.initState();
}
@override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (event) {
_colorAnimationController.forward();
_underlineAnimationController.animateTo(0.5);
},
onExit: (event) {
_colorAnimationController.reverse();
_underlineAnimationController.animateTo(1);
},
cursor: SystemMouseCursors.click,
child: ClipRRect(
child: Container(
alignment: Alignment.centerRight,
width: widget.hasDot ? widget.width + 25 : widget.width,
height: widget.underlineOffset + 2,
// color: Colors.green,
child: Stack(
clipBehavior: Clip.none,
children: [
// Points (si il y en a un)
if (widget.hasDot)
const Positioned(
top: 9,
left: -26,
child: Icon(
Icons.circle,
color: Colors.black,
size: 10,
),
),
// Texte
Stack(
children: [
Text(
widget.text,
style: widget.textStyle.copyWith(
color: _colorAnimation.value,
),
),
],
),
// Underline (onHover animation)
Positioned(
top: widget.underlineOffset,
left: _underlineAnimation.value,
child: Container(
width: widget.width,
height: 1,
color: Colors.orange,
),
)
],
),
),
),
);
}
}
一切正常,但是当我设置
hasDot: true
时,橙色线出现在点下方。
注意:我希望下划线动画仅出现在文本下方,而不是点下方。
我的问题是如何实现与
hasDot
为 false
时相同的动画效果,但包含点并且橙色线隐藏在点下。
其他信息:如果您需要 Flutter 项目的完整代码,请查看我的 GitHub 存储库。
提前感谢您的帮助。
祝您编码愉快!
更新: 在发布这个问题之前,我尝试了很多方法来完成这个任务,但没有成功。发布这个问题后,我在大约2秒内找到了解决方案...
因此,这是组件的完整代码,经过少量修改(基本上添加了一行以将点与其余所有部分分开):
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:kanaknaturals_cursor/utilities/my_active_provider.dart';
import 'package:provider/provider.dart';
class MyText extends StatefulWidget {
final int itemID;
final String text;
final TextStyle textStyle;
final double width;
final double underlineOffset;
final bool hasDot;
const MyText({
Key key,
required this.itemID,
required this.text,
required this.textStyle,
required this.width,
required this.underlineOffset,
this.hasDot = false,
});
@override
State<MyText> createState() => _MyTextState();
}
class _MyTextState extends State<MyText> with TickerProviderStateMixin {
late AnimationController _colorAnimationController;
late Animation _colorAnimation;
late AnimationController _underlineAnimationController;
late Animation _underlineAnimation;
@override
void initState() {
_colorAnimationController = AnimationController(
duration: const Duration(milliseconds: 250),
vsync: this,
)..addListener(() {
setState(() {});
});
_colorAnimation = ColorTween(
begin: Colors.black,
end: Colors.orange,
).animate(_colorAnimationController);
_underlineAnimationController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
)..addListener(() {
if (_underlineAnimationController.isCompleted &&
_underlineAnimationController.value == 1) {
_underlineAnimationController.reset();
}
setState(() {});
});
_underlineAnimation = Tween(begin: -widget.width, end: widget.width)
.animate(_underlineAnimationController);
super.initState();
}
@override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (event) {
_colorAnimationController.forward();
_underlineAnimationController.animateTo(0.5);
Provider.of<MyActiveProvider>(context, listen: false)
.setActiveItem(widget.itemID);
},
onExit: (event) {
_colorAnimationController.reverse();
_underlineAnimationController.animateTo(1);
Provider.of<MyActiveProvider>(context, listen: false)
.setActiveItem(null);
},
cursor: SystemMouseCursors.click,
child: Row(
children: [
// Points (if there is one)
if (widget.hasDot)
const Row(
children: [
Positioned(
top: 9,
left: -26,
child: Icon(
Icons.circle,
color: Colors.black,
size: 10,
),
),
Gap(10),
],
),
ClipRRect(
child: Container(
alignment: Alignment.centerRight,
width: widget.hasDot ? widget.width : widget.width,
height: widget.underlineOffset + 2,
// color: Colors.green,
child: Stack(
clipBehavior: Clip.none,
children: [
// Text
Stack(
children: [
Text(
widget.text,
style: widget.textStyle.copyWith(
color: _colorAnimation.value,
),
),
],
),
// Underline (onHover animation)
Positioned(
top: widget.underlineOffset,
left: _underlineAnimation.value,
child: Container(
width: widget.width,
height: 1,
color: Colors.orange,
),
)
],
),
),
),
],
),
);
}
}