Skip to content

LLM prompt — add the Flutter SDK

Use this prompt when you have an existing Flutter app and want to add a 2wai avatar screen with Transport 0 (LiveKit).

Copy, paste, fill {PLACEHOLDERS}:

I'm adding a 2wai talking avatar to an existing Flutter app.
CONTEXT
2wai is a lip-synced talking-avatar platform. Transport 0 (LiveKit) is the only live transport. Integration pattern is MINT-PROXY: a backend holds `sk_...` and mints short-lived LiveKit participant tokens.
The Flutter SDK ships inside `2waiorg/2wai-toolkit-public`:
- `twai_avatar_sdk` — UI widgets + Transcriber
- `twai_avatar_sdk_core` — pure-Dart core (REST + types + errors)
- `twai_avatar_sdk_livekit` — Transport 0 engine (`TwaiTransport0Player` widget)
- `twai_speech_to_text_patched` — vendored speech_to_text fork with iOS LiveKit audio-session fix (MANDATORY on iOS)
MY INPUTS
- avatar_id: {AVATAR_ID}
- voice_id (optional): {VOICE_ID}
- target platforms: {IOS / ANDROID / WEB / DESKTOP / ANY_SUBSET}
- mint-proxy URL: {URL — e.g. https://my-app.com/api/mint, OR "I need help setting one up"}
- where I want the avatar screen: {ROUTE_NAME or FILE_PATH}
WHAT I NEED YOU TO DO
1. **Add dependencies** to my `pubspec.yaml`:
```yaml
dependencies:
twai_avatar_sdk:
git:
url: https://github.com/2waiorg/2wai-toolkit-public
path: packages/twai_avatar_sdk
ref: main
twai_avatar_sdk_core:
git:
url: https://github.com/2waiorg/2wai-toolkit-public
path: packages/twai_avatar_sdk_core
ref: main
twai_avatar_sdk_livekit:
git:
url: https://github.com/2waiorg/2wai-toolkit-public
path: packages/twai_avatar_sdk_livekit
ref: main
```
2. **Add dependency_overrides** (MANDATORY for iOS to work — this is non-negotiable):
```yaml
dependency_overrides:
livekit_client: 2.8.1
flutter_webrtc: 1.4.0
speech_to_text:
git:
url: https://github.com/2waiorg/2wai-toolkit-public
path: packages/twai_speech_to_text_patched
ref: main
```
Without these pins:
- Newer `livekit_client` breaks iOS receive-only audio.
- Upstream `speech_to_text` unconditionally deactivates AVAudioSession on stop, killing the avatar audio after mic turn 1.
3. **Run** `flutter pub get`.
4. **Create the avatar screen** at the file path I specified:
```dart
import 'package:flutter/material.dart';
import 'package:twai_avatar_sdk/twai_avatar_sdk.dart';
import 'package:twai_avatar_sdk_core/twai_avatar_sdk_core.dart';
import 'package:twai_avatar_sdk_livekit/transport0.dart' as t0;
class AvatarScreen extends StatefulWidget {
const AvatarScreen({super.key});
@override
State<AvatarScreen> createState() => _AvatarScreenState();
}
class _AvatarScreenState extends State<AvatarScreen> {
late final TwaiApiRestClient _rest;
t0.Engine? _engine;
t0.EngineState _state = t0.EngineState.idle;
bool _speaking = false;
@override
void initState() {
super.initState();
_rest = TwaiApiRestClient(
baseUrl: Uri.parse('{MINT_PROXY_URL}'),
credentials: const AnonymousCredentials(),
);
}
Future<t0.MintResult> _mint() async {
try {
final r = await _rest.createLiveKitSession(
TwaiApiLiveKitSessionRequest(avatarId: '{AVATAR_ID}'),
);
return t0.MintResultEnabled(
url: r.livekitUrl.toString(),
token: r.participantToken,
room: r.roomName,
);
} on TwaiApiRateLimitedException catch (e) {
return t0.MintRateLimited(e.retryAfterSeconds ?? 60);
} catch (e) {
return t0.MintUnavailable('mint failed: $e', cause: e);
}
}
@override
Widget build(BuildContext ctx) => Scaffold(
body: Column(children: [
Expanded(
child: t0.TwaiTransport0Player(
mint: _mint,
idleBuilder: (ctx) => Container(color: Colors.black),
onEngine: (e) => setState(() => _engine = e),
onState: (s) => setState(() => _state = s),
onSpeaking: (e) => setState(() => _speaking = e.active),
),
),
ElevatedButton(
onPressed: _state == t0.EngineState.ready && !_speaking
? () => _engine?.send('Hello!')
: null,
child: const Text('Say hi'),
),
]),
);
}
```
Mount it under my app's existing router at the route I specified.
5. **Verify**:
- `flutter run` on {chrome / my iPhone / my Android / Windows / macOS}
- Avatar video appears within ~5s.
- Tap "Say hi" — avatar speaks.
- No red screens, no console errors beyond the usual Flutter noise.
GOTCHAS TO HONOR (iOS especially)
- The vendored `speech_to_text` pin is NOT optional on iOS. Without it, the avatar audio dies after the user's first mic turn.
- `livekit_client` pin 2.8.1 is NOT optional on iOS. Newer versions break receive-only audio.
- Don't configure AVAudioSession yourself in `ios/Runner/AppDelegate.swift` — the SDK owns it.
- On Android, `android/app/build.gradle` needs minSdk 21+ for LiveKit. If my app is older, bump it.
- Web target needs the LiveKit JS SDK but the Flutter SDK handles that transitively. Just `flutter build web`.
DELIVERABLE
- Updated `pubspec.yaml` (deps + dependency_overrides).
- New `avatar_screen.dart`.
- Router wiring to mount it.
- One-line README note on which platforms I've tested and which are TBD.
If you need anything (my package manager, my router setup, my iOS minimum version, whether I have a mint proxy running yet), ASK.
Reference docs: https://developers.2wai.ai/sdks/flutter/
Reference app: https://github.com/2waiorg/2wai-toolkit-public/tree/main/apps/avatar_only_flutter
iOS integration traps (read if you hit a weird mic/audio issue):
https://github.com/2waiorg/2wai-toolkit-public/blob/main/docs/flutter_consumer_integration_gotchas.md