You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter自定义Tile中实现滑动高亮动画?

自定义Tile滑动高亮动画实现方案

根据你描述的滑动跟随高亮效果,以下是不同平台的可行实现建议:

Android 平台实现

  • 核心逻辑:通过自定义View的触摸监听+动态渐变Shader绘制高亮层,配合动画实现平滑过渡
  • 具体步骤:
    1. 给自定义Tile(ViewGroup/View)添加OnTouchListener,捕获ACTION_DOWN/ACTION_MOVE/ACTION_UP事件,记录触摸坐标
    2. 自定义绘制逻辑,使用LinearGradient创建透明到半白的渐变Shader,根据触摸位置动态调整渐变的起止点
    3. 在Tile的onDraw方法中,根据触摸状态绘制高亮层;手指离开时,用ValueAnimator实现高亮淡出动画
    4. 关键代码片段:
      tileView.setOnTouchListener { v, event ->
          when (event.action) {
              MotionEvent.ACTION_DOWN -> startHighlight(event.x, event.y)
              MotionEvent.ACTION_MOVE -> updateHighlight(event.x, event.y)
              MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> endHighlight()
          }
          true
      }
      
      // 自定义绘制高亮的方法
      private fun drawHighlight(canvas: Canvas, touchX: Float) {
          val gradient = LinearGradient(
              touchX - 200, 0f, touchX + 200, 0f,
              intArrayOf(Color.TRANSPARENT, Color.parseColor("#66FFFFFF"), Color.TRANSPARENT),
              floatArrayOf(0f, 0.5f, 1f),
              Shader.TileMode.CLAMP
          )
          paint.shader = gradient
          canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
      }
      

iOS 平台实现

  • 核心逻辑:利用CALayer创建渐变高亮遮罩,通过触摸事件控制遮罩层的位置与动画
  • 具体步骤:
    1. 自定义Tile继承自UIView,重写touchesBegan/touchesMoved/touchesEnded方法获取触摸点
    2. 创建CAGradientLayer作为高亮层,设置渐变颜色为[UIColor.clear, UIColor.white.withAlphaComponent(0.3), UIColor.clear],初始隐藏
    3. 触摸移动时实时更新高亮层的position和startPoint/endPoint,用UIViewPropertyAnimator实现平滑过渡;手指离开时执行淡出动画并移除层
    4. 关键代码片段:
      private var highlightLayer: CAGradientLayer?
      
      override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
          guard let touch = touches.first else { return }
          let point = touch.location(in: self)
          setupHighlightLayer(at: point)
      }
      
      override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
          guard let touch = touches.first, let layer = highlightLayer else { return }
          let point = touch.location(in: self)
          layer.position = CGPoint(x: point.x, y: bounds.midY)
      }
      
      private func setupHighlightLayer(at point: CGPoint) {
          highlightLayer = CAGradientLayer()
          highlightLayer?.colors = [UIColor.clear.cgColor, UIColor.white.withAlphaComponent(0.3).cgColor, UIColor.clear.cgColor]
          highlightLayer?.startPoint = CGPoint(x: 0, y: 0.5)
          highlightLayer?.endPoint = CGPoint(x: 1, y: 0.5)
          highlightLayer?.frame = bounds
          highlightLayer?.position = CGPoint(x: point.x, y: bounds.midY)
          layer.addSublayer(highlightLayer!)
      }
      

Flutter 平台实现

  • 核心逻辑:用GestureDetector监听触摸,配合CustomPaint绘制动态高亮层,结合AnimationController控制动画
  • 具体步骤:
    1. 用GestureDetector包裹Tile组件,监听onPanDown/onPanUpdate/onPanEnd事件,记录触摸位置
    2. 在Stack布局中,Tile上方叠加CustomPaint组件,通过自定义Painter绘制跟随触摸点的渐变高亮
    3. 手指离开时,用AnimationController驱动高亮层的透明度从0.3降到0,实现淡出效果
    4. 关键代码片段:
      class HighlightTile extends StatefulWidget {
        @override
        State<HighlightTile> createState() => _HighlightTileState();
      }
      
      class _HighlightTileState extends State<HighlightTile> with SingleTickerProviderStateMixin {
        Offset? _touchPos;
        late AnimationController _animController;
        late Animation<double> _opacityAnim;
      
        @override
        void initState() {
          super.initState();
          _animController = AnimationController(vsync: this, duration: Duration(milliseconds: 300));
          _opacityAnim = Tween<double>(begin: 0.3, end: 0).animate(_animController);
        }
      
        @override
        Widget build(BuildContext context) {
          return GestureDetector(
            onPanDown: (details) => setState(() => _touchPos = details.localPosition),
            onPanUpdate: (details) => setState(() => _touchPos = details.localPosition),
            onPanEnd: (details) {
              _animController.forward().then((_) {
                setState(() => _touchPos = null);
                _animController.reset();
              });
            },
            child: Stack(
              children: [
                // 你的Tile内容组件
                Container(width: 200, height: 100, color: Colors.blueGrey),
                if (_touchPos != null)
                  AnimatedBuilder(
                    animation: _opacityAnim,
                    builder: (context, child) => CustomPaint(
                      painter: HighlightPainter(_touchPos!, opacity: _opacityAnim.value),
                      size: Size.infinite,
                    ),
                  )
              ],
            ),
          );
        }
      }
      
      class HighlightPainter extends CustomPainter {
        final Offset touchPos;
        final double opacity;
      
        HighlightPainter(this.touchPos, {required this.opacity});
      
        @override
        void paint(Canvas canvas, Size size) {
          final paint = Paint()
            ..shader = LinearGradient(
              colors: [
                Colors.transparent,
                Colors.white.withOpacity(opacity),
                Colors.transparent
              ],
              begin: Offset(touchPos.dx - 150, touchPos.dy),
              end: Offset(touchPos.dx + 150, touchPos.dy),
            ).createShader(Offset.zero & size);
          canvas.drawRect(Offset.zero & size, paint);
        }
      
        @override
        bool shouldRepaint(covariant HighlightPainter oldDelegate) {
          return oldDelegate.touchPos != touchPos || oldDelegate.opacity != opacity;
        }
      }
      

通用优化建议

  • 高亮透明度控制在0.2~0.4之间,避免遮挡Tile原有内容
  • 触摸位置更新时使用动画过渡,避免高亮层跳变
  • 限制高亮层范围在Tile内部,防止超出边界

内容的提问来源于stack exchange,提问作者Mayur Sankhla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 02:34:59