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

Flutter中如何对double、int类型数值实现lerp线性插值?

Flutter 按压渐变Card阴影实现方案

核心实现逻辑

不用手动编写lerp插值计算逻辑,直接依托Flutter原生动画体系完成两段动效控制:

  • 用AnimationController托管elevation数值变化,初始值设为50,取值区间0-50
  • 绑定InkWell的三个手势回调:
    • 按下(onTapDown)时,触发线性动画,在设定的按压时长内将数值从当前值匀速降到0,对应按住时阴影逐渐消失的效果
    • 松开(onTapUp)、手势取消(onTapCancel)时,触发短时长快速动画,将数值从当前值快速恢复到50,对应松手后阴影回弹效果
  • 用AnimatedBuilder监听动画值变化,实时更新Card的elevation属性,保证渲染性能

完整可复用代码

import 'package:flutter/material.dart';

class PressSinkingCard extends StatefulWidget {
  const PressSinkingCard({
    super.key,
    required this.child,
    this.pressDuration = const Duration(milliseconds: 300),
    this.releaseDuration = const Duration(milliseconds: 100),
    this.initElevation = 50,
  });

  /// Card内部内容
  final Widget child;
  /// 按住到阴影完全消失的时长
  final Duration pressDuration;
  /// 松手后阴影回弹恢复时长
  final Duration releaseDuration;
  /// 初始阴影高度
  final double initElevation;

  @override
  State<PressSinkingCard> createState() => _PressSinkingCardState();
}

class _PressSinkingCardState extends State<PressSinkingCard> {
  late AnimationController _elevationCtrl;

  @override
  void initState() {
    super.initState();
    _elevationCtrl = AnimationController(
      vsync: this,
      value: widget.initElevation,
      lowerBound: 0,
      upperBound: widget.initElevation,
    );
  }

  @override
  void dispose() {
    _elevationCtrl.dispose();
    super.dispose();
  }

  void _handlePressDown(TapDownDetails _) {
    _elevationCtrl.animateTo(
      0,
      duration: widget.pressDuration,
      curve: Curves.linear, // 按压阶段用线性插值,符合匀速递减需求
    );
  }

  void _handlePressUp(TapUpDetails _) {
    _elevationCtrl.animateTo(
      widget.initElevation,
      duration: widget.releaseDuration,
      curve: Curves.easeOut, // 回弹阶段用快出曲线,手感更自然
    );
  }

  void _handlePressCancel() {
    _elevationCtrl.animateTo(
      widget.initElevation,
      duration: widget.releaseDuration,
      curve: Curves.easeOut,
    );
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _elevationCtrl,
      builder: (context, _) {
        return Card(
          elevation: _elevationCtrl.value,
          clipBehavior: Clip.antiAlias,
          child: InkWell(
            onTapDown: _handlePressDown,
            onTapUp: _handlePressUp,
            onTapCancel: _handlePressCancel,
            onTap: () {}, // 必须设置非空onTap,否则InkWell不响应手势
            child: widget.child,
          ),
        );
      },
    );
  }
}

参数调整说明

  • 想要按住后阴影消失得更慢,就调大pressDuration的时长,反之调小
  • 想要松手后阴影回弹更快,就调小releaseDuration的时长
  • 回弹阶段如果想要弹跳效果,可以把Curves.easeOut换成Curves.bounceOut
  • 动画内部自动执行lerpDouble插值计算,不需要额外手动处理进度映射,和需求里的线性插值逻辑完全一致

注意:如果需要给Card加圆角、背景色等属性,直接透传给Card组件即可,不会影响按压动效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 09:54:23