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

如何在Flutter应用中实现语音交互动画?

实现Flutter语音状态动态圆形动画方案

一、现成可用的Flutter包

  • flutter_sound:核心用于音频录制与播放,可通过其音量监听回调,配合自定义动画组件实现随音量变化的圆形动画。
  • animations:官方动画包,提供基础动画组件,能快速构建缩放、渐变的圆形动画,结合语音状态切换触发。
  • wave_animation:专注波形动画的包,支持自定义圆形波形样式,可直接关联音频输入的音量数据。

二、自定义实现方案(代码示例)

如果不想依赖第三方包,可完全自定义StatefulWidget实现,核心思路是:监听语音输入的音量变化,根据音量值动态调整圆形的缩放比例、透明度或边框宽度;机器人回复时,用循环的缩放渐变动画模拟加载状态。

自定义语音状态动画组件代码

import 'package:flutter/material.dart';

class VoiceStatusCircle extends StatefulWidget {
  final bool isUserSpeaking; // 用户是否处于说话状态
  final double? volume; // 用户说话时的音量值(范围0-1)
  final bool isBotResponding; // 机器人是否处于回复状态

  const VoiceStatusCircle({
    super.key,
    required this.isUserSpeaking,
    this.volume,
    required this.isBotResponding,
  });

  @override
  State<VoiceStatusCircle> createState() => _VoiceStatusCircleState();
}

class _VoiceStatusCircleState extends State<VoiceStatusCircle> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _scaleAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 800),
      vsync: this,
    )..repeat(reverse: true);
    _scaleAnimation = Tween<double>(begin: 0.8, end: 1.2).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
    );
  }

  @override
  void didUpdateWidget(covariant VoiceStatusCircle oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 机器人回复状态变化时控制动画播放/暂停
    if (widget.isBotResponding != oldWidget.isBotResponding) {
      if (widget.isBotResponding) {
        _controller.repeat(reverse: true);
      } else {
        _controller.stop();
        _controller.value = 0.8;
      }
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: Listenable.merge([_controller, widget]),
      builder: (context, child) {
        double scale = 1.0;
        Color circleColor = Colors.grey.shade300;

        if (widget.isUserSpeaking && widget.volume != null) {
          // 用户说话时,根据音量调整缩放比例
          scale = 0.8 + (widget.volume! * 0.4);
          circleColor = Colors.blue.shade400;
        } else if (widget.isBotResponding) {
          // 机器人回复时使用循环缩放动画
          scale = _scaleAnimation.value;
          circleColor = Colors.green.shade400;
        }

        return Transform.scale(
          scale: scale,
          child: Container(
            width: 60,
            height: 60,
            decoration: BoxDecoration(
              color: circleColor.withOpacity(0.6),
              shape: BoxShape.circle,
              border: Border.all(
                color: circleColor,
                width: widget.isUserSpeaking ? 2 + (widget.volume! * 2) : 2,
              ),
            ),
            child: Center(
              child: Icon(
                widget.isUserSpeaking ? Icons.mic : Icons.speaker,
                color: Colors.white,
              ),
            ),
          ),
        );
      },
    );
  }
}

使用示例

// 在页面中引入组件
VoiceStatusCircle(
  isUserSpeaking: _isRecording, // 你的录制状态变量
  volume: _currentVolume, // 从音频插件获取的音量值(范围0-1)
  isBotResponding: _isBotReplying, // 机器人回复状态变量
)

三、实现关键点说明

  • 音量监听:若使用flutter_sound,可通过RecorderController的onProgress回调获取当前音量,转换为0-1的范围传入组件。
  • 状态切换:在用户停止说话、机器人开始/结束回复时,需正确更新组件的状态参数,触发动画变化。
  • 性能优化:使用AnimatedBuilder和SingleTickerProviderStateMixin避免不必要的重绘,保证动画流畅。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 10:43:22