flutter_davoice 0.0.4 copy "flutter_davoice: ^0.0.4" to clipboard
flutter_davoice: ^0.0.4 copied to clipboard

Flutter voice AI plugin for on-device STT, neural TTS, speaker verification, audio playback, and native iOS/Android voice flows.

flutter_davoice #

Flutter voice AI plugin for Davoice on iOS and Android.

Core Capabilities #

  • Speaker verification / speaker identification: speaker onboarding and live verification for speaker-gated voice experiences.
  • Wake-word speaker JSON support: accepts the speaker enrollment JSON or saved enrollment JSON path created by the Davoice wake-word library: https://github.com/frymanofer/Flutter_WakeWordDetection/
  • Multi-language STT: speech-to-text for conversational apps, including speaker identification and speaker-isolated recognition flows.
  • On-device TTS: low-latency Davoice text-to-speech with local voice models.
  • VAD / voice activity detection flows: voice activity detection is used in the Davoice voice pipeline and in the companion wake-word package for always-listening and conversational microphone control.

See STT with speaker isolation in action: https://www.youtube.com/watch?v=uYpaCXAvjew

Full Example App #

The runnable Flutter example app is distributed separately because it includes large voice, wake-word, and speaker-verification model assets.

Example app: https://github.com/frymanofer/Flutter_DaVoice

The example app demonstrates:

  • voice model selection
  • license setup
  • speaker verification onboarding
  • saved speaker signature reuse
  • live speaker verification before wake-word/STT activation
  • wake-word detection through flutter_wake_word
  • speaker-isolated STT initialization
  • on-device TTS
  • safe STT pause / TTS speak / STT resume sequencing
  • manual TTS testing
  • speech echo testing
  • Full AI Chat style turn taking

Installation #

dependencies:
  flutter_davoice: ^0.0.1

For wake word, speaker onboarding, speaker verification mic flows, and VAD / wake-word front-end behavior, also add the companion package:

dependencies:
  flutter_wake_word: ^0.0.41

Companion package: https://github.com/frymanofer/Flutter_WakeWordDetection/

Platform Setup #

Android #

The plugin requires Android minSdk 29 or newer.

Add permissions used by voice apps:

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.INTERNET" />

iOS #

Add usage descriptions:

<key>NSMicrophoneUsageDescription</key>
<string>This app uses the microphone for voice features.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>This app uses speech recognition to convert speech to text.</string>

The plugin supports iOS 13.0 or newer.

Model Assets #

Add Davoice voice models to your Flutter app assets:

flutter:
  assets:
    - assets/models/model_ex_ariana_fast.dm
    - assets/models/model_ex_rich_fast.dm

Then pass the same asset key to initAll or initTTS:

const arianaFastModel = 'assets/models/model_ex_ariana_fast.dm';
const richFastModel = 'assets/models/model_ex_rich_fast.dm';

Create The Speech API #

import 'dart:async';

import 'package:flutter_davoice/flutter_davoice.dart';

final speech = FlutterDavoice();
final subscriptions = <StreamSubscription<dynamic>>[];

License #

Call setLicense before initializing STT/TTS.

final licensed = await speech.setLicense('YOUR_DAVOICE_LICENSE_KEY');
if (!licensed) {
  throw StateError('Davoice license is not valid.');
}

You can also validate a key explicitly:

final valid = await speech.isLicenseValid('YOUR_DAVOICE_LICENSE_KEY');

Listen To STT/TTS Events #

Register event listeners before starting the voice flow:

subscriptions.addAll([
  speech.onSpeechStart.listen((_) {
    print('STT started');
  }),
  speech.onSpeechPartialResults.listen((event) {
    final text = event.value.join(' ').trim();
    print('partial: $text');
  }),
  speech.onSpeechResults.listen((event) {
    final text = event.value.join(' ').trim();
    print('final: $text');
  }),
  speech.onSpeechError.listen((event) {
    print('speech error: ${event.code} ${event.message}');
  }),
  speech.onSpeechVolumeChanged.listen((event) {
    print('volume: ${event.value}');
  }),
  speech.onFinishedSpeaking.listen((_) {
    print('TTS or marked playback finished');
  }),
  speech.onNewSpeechWAV.listen((event) {
    print('new speech wav: ${event.path}');
  }),
]);

Dispose subscriptions and native resources when your screen/app shuts down:

for (final subscription in subscriptions) {
  await subscription.cancel();
}
await speech.destroyAll();

Initialize STT + TTS #

Use initAll when your app needs both STT and TTS in the same flow.

await speech.initAll(
  const DavoiceInitAllOptions(
    locale: 'en-US',
    model: 'assets/models/model_ex_ariana_fast.dm',
    timeoutMs: 30000,
  ),
);

For speaker-isolated STT, pass the saved speaker enrollment JSON file path:

await speech.initAll(
  DavoiceInitAllOptions(
    locale: 'en-US',
    model: 'assets/models/model_ex_ariana_fast.dm',
    timeoutMs: 30000,
    onboardingJsonPath: savedSpeakerEnrollmentJsonPath,
  ),
);

The onboardingJsonPath value is the enrollment JSON path produced by the Davoice wake-word package speaker verification onboarding flow.

Start, Pause, And Resume STT #

Start recognition:

await speech.start(
  'en-US',
  options: const DavoiceStartOptions(
    extraPartialResults: true,
    extraMaxResults: 5,
  ),
);

Pause STT:

await speech.pauseSpeechRecognition();

Resume STT:

await speech.unPauseSpeechRecognition(-1);

unPauseSpeechRecognition parameters:

  • times: how many recognition cycles to resume for. Use -1 for continuous.
  • preFetchMs: optional prefetch window used by native flows.
  • timeoutMs: native resume timeout.

Stop or cancel recognition:

await speech.stop();
await speech.cancel();

Check availability:

final available = await speech.isAvailable();
final recognizing = await speech.isRecognizing();

Correct STT/TTS Sequencing #

When your app speaks, pause STT first. Resume STT only after speak returns.

Future<void> speakReply(String text) async {
  await speech.pauseSpeechRecognition();
  await speech.speak(
    text,
    speakerId: 0,
    speed: 0.88,
  );
  await speech.unPauseSpeechRecognition(-1);
}

This sequence matters in real voice apps because it prevents STT from hearing the app's own TTS output.

On-Device TTS #

If you only need TTS, initialize the TTS model directly:

await speech.setLicense('YOUR_DAVOICE_LICENSE_KEY');
await speech.initTTS('assets/models/model_ex_ariana_fast.dm');

await speech.speak(
  'Hello from Davoice.',
  speakerId: 0,
  speed: 0.88,
);

Stop current TTS playback:

await speech.stopSpeaking();

Speaker Verification + Speaker-Isolated STT #

Speaker onboarding and live verification are handled by the Davoice wake-word package. This package consumes the enrollment JSON path to initialize speaker-aware STT.

Typical production flow:

  1. Use flutter_wake_word to onboard the speaker.
  2. Save the returned enrollment JSON to a file, for example sv_enrollment.json.
  3. On next launch, offer:
    • use saved signature
    • create new signature
    • skip speaker verification
  4. If speaker verification is enabled, verify the speaker first.
  5. Pass the saved enrollment JSON path to flutter_davoice.
  6. Initialize wake word and STT with the same saved enrollment path.

The Davoice STT side:

await speech.initAll(
  DavoiceInitAllOptions(
    locale: 'en-US',
    model: 'assets/models/model_ex_ariana_fast.dm',
    onboardingJsonPath: savedSpeakerEnrollmentJsonPath,
  ),
);

You can also start speech with a speaker-verification enrollment JSON path:

await speech.startWithSVOnboardingJson(
  'en-US',
  savedSpeakerEnrollmentJsonPath,
);

The companion wake-word package also accepts the same enrollment path when starting wake-word detection, so wake-word activation and STT can use the same speaker identity.

Wake-word package: https://github.com/frymanofer/Flutter_WakeWordDetection/

Speech Echo Pattern #

The standalone example app includes a useful test flow:

  1. Wake word fires.
  2. App pauses wake-word detection.
  3. App pauses STT.
  4. Selected Davoice speaker says an intro line.
  5. App resumes STT only after await speak(...) returns.
  6. User speaks.
  7. App waits for a silence timeout.
  8. App pauses STT.
  9. App repeats what the user said with TTS.
  10. App resumes STT.

Minimal implementation:

Timer? silenceTimer;
String transcript = '';

void onTranscript(String text) {
  transcript = text.trim();
  silenceTimer?.cancel();
  silenceTimer = Timer(const Duration(seconds: 2), () async {
    if (transcript.isEmpty) return;
    final reply = transcript;
    transcript = '';
    await speech.pauseSpeechRecognition();
    await speech.speak(reply, speakerId: 0, speed: 0.88);
    await speech.unPauseSpeechRecognition(-1);
  });
}

subscriptions.addAll([
  speech.onSpeechPartialResults.listen((event) {
    if (event.value.isNotEmpty) {
      onTranscript(event.value.first);
    }
  }),
  speech.onSpeechResults.listen((event) {
    if (event.value.isNotEmpty) {
      onTranscript(event.value.first);
    }
  }),
]);

Full AI Chat Pattern #

The same STT/TTS control pattern works for AI chat:

  1. Collect partial/final STT.
  2. Wait for a silence timeout.
  3. Pause STT.
  4. Send the transcript to your AI service.
  5. Speak the AI reply with Davoice TTS.
  6. Resume STT.
Timer? silenceTimer;
String pendingUserText = '';
bool aiTurnInFlight = false;

void onUserSpeech(String text) {
  pendingUserText = text.trim();
  silenceTimer?.cancel();
  silenceTimer = Timer(const Duration(seconds: 2), () async {
    final userText = pendingUserText;
    if (userText.isEmpty || aiTurnInFlight) return;
    pendingUserText = '';
    aiTurnInFlight = true;

    await speech.pauseSpeechRecognition();
    try {
      final aiReply = await sendToYourAIService(userText);
      await speech.speak(aiReply, speakerId: 0, speed: 0.88);
    } finally {
      aiTurnInFlight = false;
      await speech.unPauseSpeechRecognition(-1);
    }
  });
}

Audio Playback #

Play a WAV file or URL:

await speech.playWav('/path/to/file.wav');

If markAsLast is true, playWav completes after onFinishedSpeaking:

await speech.playWav('/path/to/file.wav', markAsLast: true);

Play PCM:

await speech.playPCM(
  pcmBytes,
  sampleRate: 16000,
  channels: 1,
  format: DavoicePcmFormat.i16,
);

Or pass an external PCM descriptor:

await speech.playBuffer(
  DavoiceExternalPcm(
    base64: base64Pcm,
    sampleRate: 16000,
    channels: 1,
    format: DavoicePcmFormat.i16,
  ),
);

iOS Permission Helpers #

final hasMic = await speech.hasIOSMicPermissions();
final micGranted = hasMic || await speech.requestIOSMicPermissions(2500);

final hasSpeech = await speech.hasIOSSpeechRecognitionPermissions();
final speechGranted =
    hasSpeech || await speech.requestIOSSpeechRecognitionPermissions(2500);

Microphone And Audio Controls #

await speech.pauseMicrophone();
await speech.unPauseMicrophone();

await speech.setAECEnabled(true);

Android Remote STT Helpers #

For Android remote-STT workflows:

await speech.initAllRemoteSTT(
  model: 'assets/models/model_ex_ariana_fast.dm',
  onboardingJsonPath: savedSpeakerEnrollmentJsonPath,
);

await speech.initAllRemoteSTTAndTTS(
  model: 'assets/models/model_ex_ariana_fast.dm',
  onboardingJsonPath: savedSpeakerEnrollmentJsonPath,
);

API Reference #

Initialization #

  • FlutterDavoice({ttsCompletionTimeout})
  • initAll(DavoiceInitAllOptions options)
  • destroyAll()
  • initWithoutModel()
  • destroyWithoutModel()
  • destroyWihtouModel()
  • initTTS(String model)
  • initAllRemoteSTT({required String model, String? onboardingJsonPath})
  • initAllRemoteSTTAndTTS({String? model, String? onboardingJsonPath})

Speech Recognition #

  • start(String locale, {DavoiceStartOptions? options})
  • startWithSVOnboardingJson(String locale, String onboardingJsonPath)
  • stop()
  • cancel()
  • isAvailable()
  • isRecognizing()
  • pauseSpeechRecognition()
  • unPauseSpeechRecognition(int times, {int preFetchMs, int timeoutMs})
  • pauseMicrophone()
  • unPauseMicrophone()

Text To Speech And Playback #

  • speak(String text, {int speakerId, double speed})
  • stopSpeaking()
  • playWav(String pathOrUrl, {bool markAsLast})
  • playPCM(Uint8List data, {required int sampleRate, int channels, bool interleaved, DavoicePcmFormat format, bool markAsLast})
  • playBuffer(DavoiceExternalPcm pcm)

License And Audio #

  • setLicense(String licenseKey)
  • isLicenseValid(String licenseKey)
  • setAECEnabled(bool enabled)

Events #

  • events
  • onSpeechStart
  • onSpeechRecognized
  • onSpeechEnd
  • onSpeechError
  • onSpeechResults
  • onSpeechPartialResults
  • onSpeechVolumeChanged
  • onNewSpeechWAV
  • onFinishedSpeaking

iOS Permissions #

  • hasIOSMicPermissions()
  • requestIOSMicPermissions(int waitTimeoutMs)
  • hasIOSSpeechRecognitionPermissions()
  • requestIOSSpeechRecognitionPermissions(int waitTimeoutMs)

Validation #

flutter analyze
flutter test
flutter pub publish --dry-run

The package repo intentionally excludes the runnable example app and large model assets. Use the standalone example repo for a complete app: https://github.com/frymanofer/Flutter_DaVoice

3
likes
140
points
232
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Flutter voice AI plugin for on-device STT, neural TTS, speaker verification, audio playback, and native iOS/Android voice flows.

Homepage

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on flutter_davoice

Packages that implement flutter_davoice