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

如何在Flutter中实现类似iPhone关机按钮的文字滑动变色动画?

实现Flutter文字滑过变色动画(类似iPhone关机按钮效果)

要实现白色图层滑过文字、逐步将黑色文字转为白色再恢复的循环动画,不能直接用AnimationController控制文字颜色(这种方式会一次性切换颜色)。我们可以通过双层文字+裁剪动画的方式模拟滑过效果,具体实现如下:

核心思路

用Stack堆叠两个相同的文字:

  • 底层为黑色文字(初始显示状态)
  • 上层为白色文字,通过ClipRect结合动画控制其显示区域,让白色文字从左到右(或指定方向)逐步覆盖底层黑色文字,再反向隐藏,实现“滑过”的视觉效果

完整代码示例

import 'package:flutter/material.dart';

class SlideTextAnimation extends StatefulWidget {
  final String text;
  final TextStyle style;
  final Duration duration;

  const SlideTextAnimation({
    super.key,
    required this.text,
    required this.style,
    this.duration = const Duration(seconds: 2),
  });

  @override
  State<SlideTextAnimation> createState() => _SlideTextAnimationState();
}

class _SlideTextAnimationState extends State<SlideTextAnimation>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    // 初始化动画控制器,设置循环反向动画
    _controller = AnimationController(
      vsync: this,
      duration: widget.duration,
    )..repeat(reverse: true);

    // 创建从0到1的动画值,用于控制裁剪区域
    _animation = 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 Stack(
      alignment: Alignment.center,
      children: [
        // 底层黑色文字
        Text(widget.text, style: widget.style.copyWith(color: Colors.black)),
        // 上层白色文字,通过裁剪控制显示区域
        AnimatedBuilder(
          animation: _animation,
          builder: (context, child) {
            return ClipRect(
              // 自定义裁剪器,根据动画值控制显示宽度
              clipper: _SlideClipper(_animation.value),
              child: child,
            );
          },
          child: Text(widget.text, style: widget.style.copyWith(color: Colors.white)),
        ),
      ],
    );
  }
}

// 自定义裁剪器:根据动画值裁剪出从左到右的区域
class _SlideClipper extends CustomClipper<Rect> {
  final double offset;

  _SlideClipper(this.offset);

  @override
  Rect getClip(Size size) {
    // offset从0到1时,裁剪区域从左到右扩展;从1到0时,区域从右到左收缩
    return Rect.fromLTRB(0, 0, size.width * offset, size.height);
  }

  @override
  bool shouldReclip(_SlideClipper oldClipper) {
    return oldClipper.offset != offset;
  }
}

// 使用示例
void main() {
  runApp(const MaterialApp(
    home: Scaffold(
      backgroundColor: Colors.grey.shade900,
      body: Center(
        child: SlideTextAnimation(
          text: "滑动关机",
          style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
        ),
      ),
    ),
  ));
}

关键细节说明

  1. 动画控制器配置:通过repeat(reverse: true)让动画自动循环并反向播放,实现“变白→变黑”的往复效果
  2. 裁剪器逻辑:_SlideClipper根据动画值动态调整裁剪区域的宽度,当offset从0到1时,白色文字从左到右逐步显示;从1到0时,白色文字逐步隐藏
  3. 样式复用:通过copyWith复用传入的文字样式,仅修改颜色,保证两层文字的大小、字体完全一致

如果需要改变滑过方向(比如从右到左、从上到下),只需修改_SlideClipper的getClip方法即可:

  • 从右到左:Rect.fromLTRB(size.width * (1 - offset), 0, size.width, size.height)
  • 从上到下:Rect.fromLTRB(0, 0, size.width, size.height * offset)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 07:07:48