flutter - 实现渐变动画矩形边框

最近碰到一个需求需要实现一个矩形,矩形边框为渐变色,并且要求渐变色不断滚动向前,如下图所示

主要思路参考自这篇文章 另外还有来自 chatgpt 的回答,实现效果如上图所示效果

具体实现思路

  1. CustomPainter 实现一个渐变矩形边框

     void paint(Canvas canvas, Size size) {
     // 创建一个矩形区域
     final rect = Rect.fromLTWH(0, 0, 96, 38);
     final paint = Paint()
     ..shader = LinearGradient(
     // 渐变色值
     colors: const [Color.yellow, Colors.green],
     // 此处是实现动画的关键 => 动态传入角度来实现动画
     transform: GradientRotation(animation * 2 * pi),
     // 创建一个线性渐变着色器对象并将其应用于矩形形状的填充
     ).createShader(rect)
     ..style = PaintingStyle.stroke
     // 边框宽度为 2 注意边框宽度是在矩形外边,所以这个矩形的宽高就变为 100*42
     ..strokeWidth = 2; 
     
     // 实现圆角矩形
     final rRect = RRect.fromRectAndRadius(rect, const Radius.circular(8));
     canvas.drawRRect(rRect, paint);
     
     // 另外也可实现直角矩形/圆形
     // 画直角矩形 
     canvas.drawRect(rect, paint);
     // 画圆形 参数:圆心坐标, 半径
     canvas.drawCircle(Offset(48, 20), 50, paint);
     }
  2. 动画实现借助显式动画 AnimationControllerAnimationBuilder,具体写法看下方完整代码

    完整代码

  3. 核心绘图代码

    import 'dart:math';
    import 'package:flutter/material.dart';
    class GradientBound extends StatefulWidget {
     // 矩形 长、宽、边框宽度,其中长、宽已包含边框宽度
     final double width;
     final double height;
     final double border;
     final Widget child;
     const GradientBound({
     super.key,
     required this.width,
     required this.height,
     required this.border,
     required this.child,
     });
     @override
     State<GradientBound> createState() => _GradientBoundState();
    }
    class _GradientBoundState extends State<GradientBound>
     with SingleTickerProviderStateMixin {
     late Animation<double> animation;
     late AnimationController controller;
     @override
     void initState() {
     super.initState();
     controller = AnimationController(
     duration: const Duration(milliseconds: 1000),
     vsync: this,
     );
     animation = Tween<double>(begin: 0, end: 1.0).animate(CurvedAnimation(
     parent: controller,
     curve: Curves.linear,
     ));
     controller.repeat();
     }
     @override
     Widget build(BuildContext context) {
     return AnimatedBuilder(
     animation: animation,
     builder: (BuildContext context, Widget? child) {
     return CustomPaint(
     // 创建 painter
     painter: GradientBoundPainter(
     colors: const [Color.yellow, Colors.green],
     animation: animation.value,
     width: widget.width,
     height: widget.height,
     border: widget.border,
     ),
     // child 内容铺满容器并居中
     child: Container(
     alignment: Alignment.center,
     width: widget.width,
     height: widget.height,
     color: Colors.transparent,
     child: widget.child,
     ),
     );
     });
     }
     @override
     void dispose() {
     controller.dispose();
     super.dispose();
     }
    }
    // 渐变边框核心绘图逻辑
    class GradientBoundPainter extends CustomPainter {
     final List<Color> colors;
     final double animation;
     final double width;
     final double height;
     final double border;
     const GradientBoundPainter({
     Key? key,
     required this.colors,
     required this.animation,
     required this.width,
     required this.height,
     required this.border,
     });
     @override
     void paint(Canvas canvas, Size size) {
     final rect = Rect.fromLTWH(0, 0, width, height);
     final paint = Paint()
     ..shader = LinearGradient(
     colors: colors,
     transform: GradientRotation(animation * 2 * pi),
     ).createShader(rect)
     ..style = PaintingStyle.stroke
     ..strokeWidth = border;
     final rRect = RRect.fromRectAndRadius(rect, const Radius.circular(8));
     canvas.drawRRect(rRect, paint);
     }
     @override
     bool shouldRepaint(covariant GradientBoundPainter oldDelegate) {
     return oldDelegate.colors != colors || oldDelegate.animation != animation;
     }
    }
    
作者:大桔子原文地址:https://segmentfault.com/a/1190000043572745

%s 个评论

要回复文章请先登录注册