talk_it

Simple native speech-to-text for Flutter. Talk, get text.

Inspired by speech_to_text — same easy API, fewer moving parts, better Arabic (ar-EG) support, and an optional push-to-talk mode that does not stop on silence.

Android iOS
Speech

Uses platform-native recognizers only — no cloud APIs, no third-party STT SDKs.

  • Android: SpeechRecognizer + RecognizerIntent (Google app preferred)
  • iOS: Speech framework (SFSpeechRecognizer, AVAudioEngine)

Install

dependencies:
  talk_it: ^0.1.0

Permissions

The plugin does not request permissions. Add them in your app.

Android — android/app/src/main/AndroidManifest.xml

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

<queries>
  <intent>
    <action android:name="android.speech.RecognitionService"/>
  </intent>
</queries>

Install the Google app on the device for best results (especially Arabic).

iOS — ios/Runner/Info.plist

<key>NSMicrophoneUsageDescription</key>
<string>We need the microphone for voice input.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>We need speech recognition to transcribe your voice.</string>

Request permissions in your app (e.g. with permission_handler):

import 'dart:io';
import 'package:permission_handler/permission_handler.dart';

Future<bool> ensureMic() async {
  if (!await Permission.microphone.request().isGranted) return false;
  if (Platform.isIOS && !await Permission.speech.request().isGranted) return false;
  return true;
}

Minimal example

import 'package:talk_it/talk_it.dart';

final talk = TalkIt();

Future<void> start() async {
  final ok = await talk.initialize();
  if (!ok) return;

  await ensureMic();
  await talk.listen(onResult: (result) {
    print(result.recognizedWords); // live + final text
    if (result.isFinal) {
      // done — send to your API, update UI, etc.
    }
  });
}

// later…
await talk.stop();   // manual stop (push-to-talk mode)
await talk.cancel(); // discard session

Complete Flutter example

import 'package:flutter/material.dart';
import 'package:talk_it/talk_it.dart';

class VoicePage extends StatefulWidget {
  const VoicePage({super.key});
  @override
  State<VoicePage> createState() => _VoicePageState();
}

class _VoicePageState extends State<VoicePage> {
  final TalkIt _talk = TalkIt();
  bool _enabled = false;
  String _words = '';

  @override
  void initState() {
    super.initState();
    _init();
  }

  Future<void> _init() async {
    _enabled = await _talk.initialize(
      localeId: 'ar-EG',
      onStatus: (s) => debugPrint(s),
      onError: (e) => debugPrint(e),
    );
    setState(() {});
  }

  Future<void> _start() async {
    await _talk.listen(onResult: (r) => setState(() => _words = r.recognizedWords));
    setState(() {});
  }

  Future<void> _stop() async {
    await _talk.stop();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Talk It')),
      body: Center(
        child: Text(
          _talk.isListening ? _words : (_enabled ? 'Tap mic…' : 'Not available'),
          textDirection: TextDirection.rtl,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _talk.isNotListening ? _start : _stop,
        child: Icon(_talk.isNotListening ? Icons.mic : Icons.stop),
      ),
    );
  }
}

Listening modes

Default is auto-stop on silence (like speech_to_text):

await talk.listen(
  onResult: (r) => print(r.recognizedWords),
  listenOptions: const TalkItListenOptions(
    mode: TalkItMode.untilSilence, // default
  ),
);

Push-to-talk — keeps listening through pauses until you call stop():

await talk.listen(
  onResult: (r) => print(r.recognizedWords),
  listenOptions: const TalkItListenOptions(
    mode: TalkItMode.untilStopped,
  ),
);
// user speaks with long pauses…
await talk.stop(); // final result in onResult with isFinal: true

API

Method Description
initialize() Call once per app session. Returns true if STT is available.
listen(onResult:) Start listening. onResult fires for partial and final text.
stop() End session and get final result.
cancel() Abort without final result.
isListening true while active.
isAvailable true after successful initialize.
lastWords Last recognized text.

TalkItResult

Field Type Description
recognizedWords String Current transcript
isFinal bool true when the session ended

Arabic example

final talk = TalkIt();
await talk.initialize(localeId: 'ar-EG');
await talk.listen(
  localeId: 'ar-EG',
  onResult: (r) {
    if (r.isFinal) sendToLlm(r.recognizedWords);
  },
);

vs speech_to_text

speech_to_text talk_it
API initialize + listen(onResult:) Same
Arabic ar-EG Often unreliable Battle-tested native approach
Silence handling Auto-stops (Android ~5s) Configurable: auto-stop or push-to-talk
Platforms 5+ Android + iOS (mobile only)
Dependencies Multiple packages Flutter only

Audio / sounds

The plugin does not play sounds. Use your own audio player before/after listen() / stop(). Android system beeps are muted during sessions; iOS uses mixWithOthers so app audio keeps working.

Example app

cd example
flutter run

Publish

flutter pub publish --dry-run

License

MIT

Libraries

talk_it