Skip to main content

wixzel_voice_flutter 0.1.1 copy "wixzel_voice_flutter: ^0.1.1" to clipboard
wixzel_voice_flutter: ^0.1.1 copied to clipboard

Flutter widgets for Wixzel Voice. Draw an agent's animated mascot in your app and make it move and lip-sync with a live realtime call.

example/lib/main.dart

// An agent's mascot, and a sketch of a talk with it.
//
//   flutter run --dart-define=BACKEND_URL=https://your-server.example.com
//
// Your API key stays on your server. This app asks your server for two
// things, and your server answers with the API's own JSON:
//
//   GET  /agent    -> await client.agents.retrieve(agentId)
//   POST /session  -> await client.realtime.createSession(
//                       CreateRealtimeSession(agentId: agentId))
//
// Without BACKEND_URL it shows the default mascot, and Talk is off.
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';

import 'package:flutter/material.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 = String.fromEnvironment('BACKEND_URL');

void main() => runApp(const MaterialApp(home: AgentScreen()));

/// An agent made locally, for when there is no backend to ask.
Agent demoAgent() => Agent.fromJson({
      'id': 'agt_demo',
      'name': 'Front desk',
      'system_prompt': '',
      'voice': const <String, dynamic>{},
      'language': 'en',
      'appointment_booking_enabled': false,
      'created_at': '2026-10-21T00:00:00Z',
      'mascot': {'spec': defaultMascot, 'updated_at': '2026-10-21T00:00:00Z'},
    });

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

  @override
  State<AgentScreen> createState() => _AgentScreenState();
}

class _AgentScreenState extends State<AgentScreen> {
  final mascot = MascotController();
  Agent agent = demoAgent();
  RealtimeConnection? connection;
  MascotRealtimeLink? link;
  StreamSubscription<RealtimeEvent>? events;
  String caption = 'Tap the mascot, or press Talk.';

  @override
  void initState() {
    super.initState();
    if (backendUrl.isNotEmpty) unawaited(_loadAgent());
  }

  Future<void> _loadAgent() async {
    final res = await http.get(Uri.parse('$backendUrl/agent'));
    final loaded = Agent.fromJson(jsonDecode(res.body) as Map<String, dynamic>);
    if (mounted) setState(() => agent = loaded);
  }

  Future<void> _talk() async {
    // 1. Your server mints a one-minute, single-use session.
    final res = await http.post(Uri.parse('$backendUrl/session'));
    final session =
        RealtimeSession.fromJson(jsonDecode(res.body) as Map<String, dynamic>);

    // 2. Connect, and let the mascot follow the call.
    final conn = RealtimeConnection.forSession(session);
    link = MascotRealtimeLink(mascot, conn);
    events = conn.events.listen((event) {
      switch (event) {
        case RealtimeTranscript(:final role, :final text):
          setState(() => caption = role == 'user' ? 'You: $text' : text);
        case RealtimeAudio(:final mulaw):
          // 3. Play the agent's voice: 16-bit PCM at 8 kHz.
          _play(mulawToPcm16(mulaw));
        case RealtimeAudioClear():
        // The user spoke over the agent: flush your player's queue.
        case RealtimeEnded():
          _hangUp();
        default:
      }
    });
    setState(() => connection = conn);

    // 4. Send the microphone: 16-bit PCM at 8 kHz, in 160-sample frames.
    //    From a recorder plugin (e.g. record), for each frame:
    //      conn.sendAudio(pcm16ToMulaw(frame));
    //      link.micAudio(frame); // the mascot leans in while you talk
  }

  /// Feed your PCM player here, e.g. the flutter_pcm_sound plugin. The
  /// mascot's mouth already follows this audio through the link.
  void _play(Int16List pcm) {}

  Future<void> _hangUp() async {
    final conn = connection;
    if (conn == null) return;
    setState(() => connection = null);
    await events?.cancel();
    await link?.dispose();
    await conn.end();
  }

  @override
  void dispose() {
    unawaited(_hangUp());
    mascot.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final talking = connection != null;
    return Scaffold(
      appBar: AppBar(title: Text(agent.name)),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            AgentMascotView(agent: agent, controller: mascot, size: 220),
            const SizedBox(height: 24),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 24),
              child: Text(caption, textAlign: TextAlign.center),
            ),
            const SizedBox(height: 24),
            FilledButton(
              onPressed:
                  backendUrl.isEmpty ? null : (talking ? _hangUp : _talk),
              child: Text(talking ? 'Hang up' : 'Talk'),
            ),
          ],
        ),
      ),
    );
  }
}
0
likes
150
points
0
downloads

Documentation

API reference

Publisher

verified publisheraqeelshamz.com

Weekly Downloads

Flutter widgets for Wixzel Voice. Draw an agent's animated mascot in your app and make it move and lip-sync with a live realtime call.

Topics

#voice-ai #flutter #ai-agents #animation #mascot

License

MIT (license)

Dependencies

flutter, sensors_plus, visibility_detector, wixzel_voice

More

Packages that depend on wixzel_voice_flutter