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