如何实现Flutter ElevatedButton悬停时的颜色滑入滑出过渡动画
直接嵌套InkWell和ElevatedButton容易和按钮自身的交互状态冲突,推荐通过自定义StatefulWidget结合Stack与SlideTransition来实现滑入效果,以下是可行方案:
核心思路
- 用
MouseRegion监听鼠标悬停状态,替代嵌套InkWell - 通过
AnimationController和Tween控制滑入动画的进度 - 用
Stack将按钮本体和滑入颜色层叠加,通过SlideTransition控制颜色层的位置
完整代码示例
import 'package:flutter/material.dart'; class HoverSlideButton extends StatefulWidget { final String text; final VoidCallback onPressed; final Color defaultColor; final Color hoverColor; final Duration animationDuration; const HoverSlideButton({ super.key, required this.text, required this.onPressed, this.defaultColor = Colors.blue, this.hoverColor = Colors.blueAccent, this.animationDuration = const Duration(milliseconds: 300), }); @override State<HoverSlideButton> createState() => _HoverSlideButtonState(); } class _HoverSlideButtonState extends State<HoverSlideButton> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<Offset> _slideAnimation; bool _isHovered = false; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: widget.animationDuration, ); _slideAnimation = Tween<Offset>( begin: const Offset(-1, 0), // 从左侧滑入,可改为(1,0)从右侧滑入 end: Offset.zero, ).animate(CurvedAnimation( parent: _controller, curve: Curves.easeInOut, )); } @override void dispose() { _controller.dispose(); super.dispose(); } void _handleHover(bool isHovered) { setState(() => _isHovered = isHovered); isHovered ? _controller.forward() : _controller.reverse(); } @override Widget build(BuildContext context) { return MouseRegion( onEnter: (_) => _handleHover(true), onExit: (_) => _handleHover(false), child: Stack( children: [ ElevatedButton( onPressed: widget.onPressed, style: ElevatedButton.styleFrom( backgroundColor: widget.defaultColor, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: Text(widget.text), ), SlideTransition( position: _slideAnimation, child: ElevatedButton( onPressed: widget.onPressed, style: ElevatedButton.styleFrom( backgroundColor: widget.hoverColor, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: Text(widget.text), ), ), ], ), ); } } // 使用示例 class DemoPage extends StatelessWidget { const DemoPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: HoverSlideButton( text: 'Hover Me', onPressed: () {}, defaultColor: Colors.grey[700]!, hoverColor: Colors.orange, ), ), ); } }
关键说明
MouseRegion直接监听按钮区域的悬停状态,避免和ElevatedButton内部的交互逻辑冲突- 修改
Tween<Offset>的begin值可自定义滑入方向:比如Offset(0,-1)是从顶部滑入 - 若要实现颜色渐变覆盖效果,可将上层按钮替换为
ColoredBox并设置blendMode调整混合样式
内容的提问来源于stack exchange,提问作者Poonam Kalra
相关产品推荐
相关产品推荐

