Muestra la mascota de tu agente en Flutter con Wixzel Voice
Al final de esta guía, tu app Flutter muestra la mascota de tu agente, la cara animada que tiene cada agente de Wixzel Voice, y la mascota escucha, piensa y habla al ritmo de una llamada en vivo. El paquete wixzel_voice_flutter la dibuja con AgentMascotView y la mantiene sincronizada con la llamada con MascotRealtimeLink; el SDK de Dart wixzel_voice se encarga de la llamada. Calcula alrededor de una hora, sobre todo para conectar una grabadora y un reproductor. Una sesión se factura como una llamada telefónica con el mismo motor: $0.0491 por minuto conectado con Gemini Live, unos $0.20 por cuatro minutos, sin línea telefónica.
Por Aqeel Shamsudheen · Publicado el · Actualizado el
- Duración
- Alrededor de una hora
- Motor
- gemini-live
- Tiempo de motor
- $0.0491 por minuto conectado

¿Qué necesitas antes de empezar?
- Un agente de Wixzel Voice y una clave de API con crédito, guardada en tu servidor. Sirve cualquier agente: todos tienen una mascota, empezando por la verde predeterminada.
- Flutter 3.27 o posterior.
- Un servidor que controles y que pueda llamar a la API. El ejemplo usa Node.js y el SDK de TypeScript.
- Un plugin de grabación que transmita PCM de 16 bits, idealmente a 8 kHz mono con cancelación de eco, y un reproductor que acepte PCM sin procesar, como
recordyflutter_pcm_sound.
¿Cómo construyes esto en Wixzel Voice, paso a paso?
01Añade los paquetes
wixzel_voice_flutterdibuja la mascota;wixzel_voicees el SDK de Dart sobre el que se construye:pubspec.yamldependencies: wixzel_voice: ^0.8.0 wixzel_voice_flutter: ^0.1.0 http: ^1.2.0El paquete depende de
sensors_plus, para que los ojos de la mascota sigan la inclinación del teléfono, y devisibility_detector, para que solo se anime mientras está en pantalla. No graba ni reproduce audio.02Dale a la app el agente y una sesión, desde tu servidor
Tu clave de API nunca va en la app. Tu servidor obtiene el agente y crea una sesión de un solo uso válida durante un minuto, y la app le pide ambas cosas a tu servidor:
server.mjsimport express from 'express'; import { WixzelVoice } from 'wixzel-voice'; const wixzel = new WixzelVoice({ apiKey: process.env.WIXZEL_API_KEY }); const app = express(); const AGENT_ID = process.env.AGENT_ID; // Put your own sign-in check in front of both routes. app.get('/agent', async (req, res) => { res.json(await wixzel.agents.retrieve(AGENT_ID)); }); app.post('/session', async (req, res) => { res.json(await wixzel.realtime.createSession({ agent_id: AGENT_ID, max_duration_seconds: 300 })); }); app.listen(3000);El JSON del agente incluye
mascot.spec, la pequeña especificación a partir de la cual se dibuja la mascota. Crear una sesión no cuesta nada; la facturación empieza cuando la app se conecta.03Dibuja la mascota
Agent.fromJsonlee lo que envió tu servidor, yAgentMascotViewlo dibuja:agent_face.dartimport 'dart:convert'; import 'package:flutter/widgets.dart'; import 'package:http/http.dart' as http; import 'package:wixzel_voice/wixzel_voice.dart'; import 'package:wixzel_voice_flutter/wixzel_voice_flutter.dart'; const backendUrl = 'https://api.example.com'; Future<Agent> loadAgent() async { final res = await http.get(Uri.parse('$backendUrl/agent')); return Agent.fromJson(jsonDecode(res.body) as Map<String, dynamic>); } Widget agentFace(Agent agent, MascotController controller) => AgentMascotView(agent: agent, controller: controller, size: 220); // In a list, draw one frame and run no animation: Widget agentRow(Agent agent) => AgentMascotView(agent: agent, size: 40, still: true);Un agente sin spec recibe el aspecto predeterminado. Una mascota casi negra o casi blanca se coloca sobre un fondo de su propio color, para que no desaparezca en una pantalla oscura o clara; desactívalo con
stage: false. Un toque reproduce una expresión, yMascotControllerreproduce movimientos por nombre, comomascot.play('hello').04Vincula la mascota a una llamada
Conéctate con
RealtimeConnectiondel SDK de Dart y pásaselo aMascotRealtimeLink. El vínculo hace que la mascota piense mientras la llamada se conecta, salude, escuche mientras el usuario habla y mueva la boca con el audio del agente:talk.dartimport 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; import 'package:http/http.dart' as http; import 'package:wixzel_voice/wixzel_voice.dart'; import 'package:wixzel_voice_flutter/wixzel_voice_flutter.dart'; /// Your audio plugins, behind a small interface. abstract class PcmPlayer { void add(Int16List samples); // 16-bit PCM, 8 kHz mono void clear(); } class MascotCall { MascotCall(this.mascot, this.player); final MascotController mascot; final PcmPlayer player; RealtimeConnection? _conn; MascotRealtimeLink? _link; StreamSubscription<Int16List>? _mic; /// [micFrames] is 16-bit PCM at 8 kHz in 160-sample (20 ms) frames. Future<void> start(Stream<Int16List> micFrames) async { final res = await http.post(Uri.parse('https://api.example.com/session')); final session = RealtimeSession.fromJson(jsonDecode(res.body) as Map<String, dynamic>); final conn = RealtimeConnection.forSession(session); _conn = conn; _link = MascotRealtimeLink(mascot, conn); conn.events.listen((event) { switch (event) { case RealtimeAudio(:final mulaw): player.add(mulawToPcm16(mulaw)); // the mouth follows this audio through the link case RealtimeAudioClear(): player.clear(); // the user spoke over the agent case RealtimeEnded(): unawaited(end()); default: } }); await conn.ready; _mic = micFrames.listen((frame) { conn.sendAudio(pcm16ToMulaw(frame)); _link?.micAudio(frame); // it leans in while the user talks }); } Future<void> end() async { await _mic?.cancel(); await _link?.dispose(); // leaves the mascot idle; does not end the call await _conn?.end(); _mic = null; _link = null; _conn = null; } }Muéstrala con
AgentMascotView(agent: agent, controller: mascot), el mismo controlador que maneja la llamada. Libera el controlador cuando la pantalla desaparezca.Activa la cancelación de eco de la plataforma en tu grabadora, o el agente se oirá a sí mismo por el altavoz y se interrumpirá.
05Haz exacta la sincronización labial, si tu reproductor programa el audio
Por defecto, el vínculo supone que tu reproductor empieza cada fragmento unos 120 ms después de recibirlo. Si tu reproductor sabe exactamente cuándo suena un fragmento, díselo al vínculo:
exact_timing.dartimport 'dart:typed_data'; import 'package:wixzel_voice/wixzel_voice.dart'; import 'package:wixzel_voice_flutter/wixzel_voice_flutter.dart'; MascotRealtimeLink exactLink(MascotController mascot, RealtimeConnection conn) => MascotRealtimeLink(mascot, conn, estimatePlayback: false); // In your player, each time it schedules a chunk, on the player's own clock: void scheduled(MascotRealtimeLink link, Int16List pcm, double playAt, double now) { link.agentAudio(pcm, playAt: playAt, currentTime: now, sampleRate: 8000); }
¿Cuánto cuesta esto en Wixzel Voice?
Tiempo de motor para sesiones de 4 minutos con el motor gemini-live a $0.0491 por minuto conectado: unos $0.20 por sesión. No hay costo de telefonía, porque no hay línea telefónica.
| Sesiones al mes | Minutos conectados | Costo de motor al mes |
|---|---|---|
| 500 | 2000 | $98.20 |
| 2500 | 10.000 | $491 |
| 10.000 | 40.000 | $1,964 |
Los paquetes son gratuitos y tienen licencia MIT. Dibujar una mascota no cuesta nada; solo una sesión en vivo gasta crédito, al precio por minuto del motor, por segundo, desde tu saldo prepago.
Prepago, sin suscripción y sin plan gratuito: agrega crédito desde $5 y el uso lo va consumiendo. Calcula tu propia combinación.
¿Qué límites de Wixzel Voice se aplican a esto?
- El paquete no graba ni reproduce audio. Tu app aporta ambas cosas mediante los plugins que elija.
- Audio de calidad telefónica: G.711 µ-law a 8 kHz en ambas direcciones.
- Las sesiones cuentan para el límite de llamadas simultáneas de la cuenta, cinco por defecto.
- Flutter 3.27 o posterior. La inclinación necesita un acelerómetro; sin él, la mascota mira a su alrededor por sí sola.
- Cambiar el aspecto de una mascota es una llamada a la API desde tu servidor (
agents.updateMascot), no desde la app, porque necesita tu clave de API.
Preguntas frecuentes
¿Cómo muestro la mascota de un agente de Wixzel Voice en Flutter?
wixzel_voice y wixzel_voice_flutter a tu pubspec, lee el agente que obtuvo tu servidor con Agent.fromJson y dibuja su mascota con AgentMascotView(agent: agent). Pasa un MascotController para controlarla desde el código.¿Cómo sincroniza la mascota los labios con el agente?
MascotRealtimeLink escucha los eventos de RealtimeConnection y mueve la boca con el audio del agente, sincronizada según su propia estimación del retraso de tu reproductor o, con estimatePlayback: false, según los tiempos que tu reproductor informa mediante agentAudio. También fija los estados: pensando mientras se conecta, escuchando mientras el usuario habla y en reposo cuando termina la llamada.¿La mascota se ve igual que en el sitio web?
¿Existe lo mismo para apps web?
mountMascot de wixzel-voice/mascot (wixzel-voice 0.10.0 en npm) dibuja la mascota en un canvas, y face.connect(session) inicia la llamada en tiempo real con la mascota ya conectada, incluidos el micrófono y la reproducción.Dónde leer más
- SDKdocumentación
Los SDK de Dart y TypeScript y los widgets de la mascota.
- Mascotasdocumentación
El spec, cómo cambiar el aspecto y cómo generar uno a partir de un sitio web.
- Realtime: web y móvildocumentación
Crear sesiones, el protocolo y la facturación.
El caso de uso y el precio de un mes de sesiones.
La llamada en sí, paso a paso, con la grabadora y el reproductor conectados.
Un solo modelo en tiempo real para todo el turno, y su precio por minuto.
Acerca de Wixzel Voice
Wixzel Voice es una API de IA de voz para crear agentes de IA que hacen y contestan llamadas telefónicas reales a través de tu propia troncal SIP, o que hablan con personas en tus apps web y móviles. Una clave de API y un saldo prepagado cubren todos los motores de voz, con cobro por segundo de uso real.
Más guías
Claude, en Claude Code o claude.ai, haciendo una llamada real por tu troncal SIP y leyéndote la transcripción, con una confirmación antes de marcar.
Tu número actual de Twilio atendido por un recepcionista de IA que agenda citas en la API, hecho en Node.js.
Dos agentes telefónicos, uno en hindi y otro en malayalam, con los modelos de Sarvam para idiomas de la India, que atienden o hacen llamadas por tu troncal SIP.
Una app Flutter en la que los usuarios hablan con tu agente de voz por WebSocket, con la clave de API guardada en tu servidor y sin ningún número de teléfono.
Un agente Gemini Live llamando a un teléfono real por tu troncal SIP a partir de cuatro peticiones cURL, y el registro de la llamada con su transcripción y su costo.
Una lista de leads llamados por tu agente a través de tu troncal SIP, en lotes ajustados a tu límite de llamadas simultáneas, con el estado y la transcripción de cada llamada.
Una mascota que habla en la esquina de tu sitio web, a la que los visitantes llaman desde el navegador, limitada a tus dominios y a tus límites diarios, con cada persona que llama registrada.
Crea tu primer agente de voz

