Flutter动态颜色填充动画实现方案咨询
在Flutter中实现动态颜色填充效果
你要实现的这种从底部向上渐变填充的效果,Flutter里没有现成的Widget,但可以通过两种方式快速实现:
方法一:Stack + AnimatedContainer(简单场景首选)
这种方式代码简洁,适合矩形填充的基础场景,核心思路是用Stack将填充层放在底层,通过动画控制填充高度:
class FillAnimationWidget extends StatefulWidget { const FillAnimationWidget({super.key}); @override State<FillAnimationWidget> createState() => _FillAnimationWidgetState(); } class _FillAnimationWidgetState extends State<FillAnimationWidget> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _fillAnimation; @override void initState() { super.initState(); // 初始化动画控制器,时长2秒,循环往复 _controller = AnimationController( duration: const Duration(seconds: 2), vsync: this, )..repeat(reverse: true); // 定义从0到1的填充进度动画 _fillAnimation = Tween<double>(begin: 0, end: 1).animate(_controller); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Container( width: 120, height: 120, // 外层边框模拟演示效果 decoration: BoxDecoration( border: Border.all(color: Colors.black, width: 2), borderRadius: BorderRadius.circular(8), ), child: Stack( fit: StackFit.expand, children: [ // 动态填充层 AnimatedBuilder( animation: _fillAnimation, builder: (context, child) { return Align( alignment: Alignment.bottomCenter, child: Container( // 根据动画进度计算填充高度 height: _fillAnimation.value * 120, width: double.infinity, color: Colors.blue, ), ); }, ), // 可选:上层叠加内容(比如文字) const Center(child: Text("填充动画")), ], ), ); } }
方法二:自定义CustomPainter(灵活适配复杂形状)
如果需要圆形、不规则图形的填充,或者更精细的动画控制,用CustomPainter自定义绘制是更好的选择:
// 自定义绘制填充逻辑 class FillPainter extends CustomPainter { final double fillProgress; final Color fillColor; final Color borderColor; FillPainter({ required this.fillProgress, required this.fillColor, required this.borderColor, }); @override void paint(Canvas canvas, Size size) { // 绘制边框 final borderPaint = Paint() ..color = borderColor ..style = PaintingStyle.stroke ..strokeWidth = 2; final rect = RRect.fromRectAndRadius( Rect.fromLTWH(0, 0, size.width, size.height), const Radius.circular(8), ); canvas.drawRRect(rect, borderPaint); // 绘制填充区域 final fillPaint = Paint()..color = fillColor; final fillRect = Rect.fromLTWH( 0, size.height * (1 - fillProgress), size.width, size.height * fillProgress, ); final fillRRect = RRect.fromRectAndRadius(fillRect, const Radius.circular(8)); canvas.drawRRect(fillRRect, fillPaint); } @override bool shouldRepaint(covariant FillPainter oldDelegate) { // 只有填充进度变化时才重绘 return oldDelegate.fillProgress != fillProgress; } } // 使用自定义Painter的动画Widget class CustomFillAnimation extends StatefulWidget { const CustomFillAnimation({super.key}); @override State<CustomFillAnimation> createState() => _CustomFillAnimationState(); } class _CustomFillAnimationState extends State<CustomFillAnimation> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _fillAnimation; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(seconds: 2), vsync: this, )..repeat(reverse: true); // 加入缓动曲线让动画更自然 _fillAnimation = Tween<double>(begin: 0, end: 1).animate(CurvedAnimation( parent: _controller, curve: Curves.easeInOut, )); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return SizedBox( width: 120, height: 120, child: AnimatedBuilder( animation: _fillAnimation, builder: (context, child) { return CustomPaint( painter: FillPainter( fillProgress: _fillAnimation.value, fillColor: Colors.blue, borderColor: Colors.black, ), child: const Center(child: Text("自定义填充")), ); }, ), ); } }
额外说明
- 如果不需要循环动画,只需修改
_controller的触发逻辑,比如在点击事件中调用_controller.forward()启动填充,_controller.reset()重置状态。 - 可以通过修改
Tween的begin和end值,调整填充的起始和结束位置(比如从左到右填充)。
内容的提问来源于stack exchange,提问作者Sal
相关产品推荐
相关产品推荐

