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

Flutter中FlareActor动画时长获取设置及手写数字动画同步问题

嘿,我来帮你搞定这个Flare手写动画同步的问题!硬编码延迟确实会让动画序列显得生硬,咱们可以通过获取Flare动画实际时长或者自定义播放速度来解决,同时也能实现多组件间的有序同步。

一、获取Flare动画的实际时长

FlareActor本身支持通过自定义FlareController来访问动画的原始时长数据,这样就能精准控制后续动画的启动时机,完全不用硬编码延迟。

步骤1:实现自定义Flare控制器

这个控制器会帮我们加载动画并获取其时长,同时提供播放动画的方法:

class HandwrittenFlareController extends FlareController {
  late FlareAnimationLayer _animationLayer;
  double? animationDuration; // 存储动画时长(单位:秒)

  @override
  void initialize(FlutterActorArtboard artboard) {
    // 找到我们定义的"write"动画
    final ActorAnimation? writeAnimation = artboard.getAnimation("write");
    if (writeAnimation != null) {
      animationDuration = writeAnimation.duration;
      _animationLayer = FlareAnimationLayer()
        ..animation = writeAnimation
        ..name = "write";
    }
  }

  @override
  bool advance(FlutterActorArtboard artboard, double elapsed) {
    _animationLayer.advance(elapsed);
    _animationLayer.apply(artboard);
    return true;
  }

  @override
  void setViewTransform(Mat2D viewTransform) {}

  // 暴露方法:重置并播放动画
  void triggerAnimation() {
    _animationLayer.time = 0.0;
  }
}

步骤2:在Widget中使用控制器并联动动画

通过自定义控制器拿到第一个动画的时长后,就可以精准触发第二个动画:

class HandwrittenNumberRow extends StatefulWidget {
  const HandwrittenNumberRow({super.key});

  @override
  State<HandwrittenNumberRow> createState() => _HandwrittenNumberRowState();
}

class _HandwrittenNumberRowState extends State<HandwrittenNumberRow> {
  final HandwrittenFlareController _num1Controller = HandwrittenFlareController();
  final HandwrittenFlareController _num5Controller = HandwrittenFlareController();

  @override
  void initState() {
    super.initState();
    // 等待第一个控制器初始化完成,获取动画时长
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (_num1Controller.animationDuration != null) {
        // 启动第一个动画
        _num1Controller.triggerAnimation();
        setState(() {});

        // 计算第一个动画的结束时间,精准触发第二个动画
        final int durationMs = (_num1Controller.animationDuration! * 1000).round();
        Timer(Duration(milliseconds: durationMs), () {
          _num5Controller.triggerAnimation();
          setState(() {});
        });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        FlareActor(
          "assets/animations/hw_1.flr",
          controller: _num1Controller,
          alignment: Alignment.center,
          fit: BoxFit.fitHeight,
        ),
        const SizedBox(width: 20),
        FlareActor(
          "assets/animations/hw_5.flr",
          controller: _num5Controller,
          alignment: Alignment.center,
          fit: BoxFit.fitHeight,
        ),
      ],
    );
  }
}

二、自定义动画播放时长(调整速度)

如果想统一不同数字的动画时长,或者手动调整某个数字的书写速度,可以在控制器里添加速度参数,通过修改播放速率来改变实际时长:

class HandwrittenFlareController extends FlareController {
  late FlareAnimationLayer _animationLayer;
  double? animationDuration;
  double playSpeed = 1.0; // 1.0为原速,大于1加快,小于1变慢

  @override
  bool advance(FlutterActorArtboard artboard, double elapsed) {
    // 用playSpeed调整播放速率
    _animationLayer.advance(elapsed * playSpeed);
    _animationLayer.apply(artboard);
    return true;
  }

  // ... 其他方法和之前一致
}

比如原动画时长是200ms,想让它变成300ms,就设置playSpeed = 200/300 ≈ 0.666即可。

三、多TextView组件间的动画同步

如果要多个独立的TextView组件按顺序播放动画,可以用状态管理工具(比如Provider、Riverpod)来统一管理动画序列:

1. 创建动画管理器

class AnimationSequenceManager extends ChangeNotifier {
  int _currentPlayingIndex = -1;
  final List<double> _animationDurations; // 存储每个组件对应动画的时长

  AnimationSequenceManager(this._animationDurations);

  void startSequence() {
    _playNextAnimation(0);
  }

  void _playNextAnimation(int index) {
    if (index >= _animationDurations.length) return;
    _currentPlayingIndex = index;
    notifyListeners();

    // 计算当前动画时长,到点自动触发下一个
    final int durationMs = (_animationDurations[index] * 1000).round();
    Timer(Duration(milliseconds: durationMs), () {
      _playNextAnimation(index + 1);
    });
  }

  bool shouldPlay(int index) => _currentPlayingIndex == index;
}

2. 在TextView组件中监听状态

class HandwrittenTextView extends StatelessWidget {
  final String flareAsset;
  final int sequenceIndex;

  const HandwrittenTextView({
    super.key,
    required this.flareAsset,
    required this.sequenceIndex,
  });

  @override
  Widget build(BuildContext context) {
    final manager = Provider.of<AnimationSequenceManager>(context);
    final controller = HandwrittenFlareController();

    if (manager.shouldPlay(sequenceIndex)) {
      controller.triggerAnimation();
    }

    return FlareActor(
      flareAsset,
      controller: controller,
      alignment: Alignment.center,
      fit: BoxFit.fitHeight,
    );
  }
}

3. 启动整个序列

// 在父组件中初始化管理器并启动序列
final manager = AnimationSequenceManager([0.2, 0.5, 0.3]); // 对应三个动画的时长(秒)
manager.startSequence();

这样所有TextView组件就会严格按照动画的实际时长依次播放,完全不用硬编码延迟,序列会自然流畅很多。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:41:52