manual_speech_to_text
음성에서 텍스트로 변환 기능을 수동으로 제어할 수 있는 Flutter 패키지로, 음성 중단 시 자동 일시정지 없이 사용자 정의 시작, 일시정지, 재개 및 중지 콜백을 사용하여 지속적인 듣기를 가능하게 합니다.
ManualSpeechToText는 음성에서 텍스트로 변환 기능에 대한 완전한 수동 제어를 위해 설계된 Flutter 패키지로, 지속적인 듣기와 사용자 정의 일시 중지 및 재개 콜백을 제공합니다. 표준 플러그인이 발화 중단 시 자동으로 일시 중지하는 반면, ManualSpeechToText는 수동으로 중지되기 전까지 중단 없이 듣기를 계속할 수 있습니다.
{"sdk":"flutter"}^11.3.1^7.0.0{"sdk":"flutter"}^4.0.0아래는 영문 원문 스냅샷입니다. 최신 내용은 GitHub에서 확인하세요.
A Flutter plugin that enhances the standard speech-to-text functionality by providing manual control over speech recognition, including pause and resume capabilities, and improved continuous listening. This package addresses common issues like automatic stopping during silence and lack of manual control in the standard speech-to-text implementation.
Sagar Kalel
final controller = ManualSttController();
// Set up listeners
controller.listen(
onListeningStateChanged: (state) => print('State: $state'),
onListeningTextChanged: (text) => print('Text: $text'),
onSoundLevelChanged: (level) => print('Level: $level'),
);
// Start recognition
controller.startStt();
| Feature | manual_speech_to_text | speech_to_text |
|---|---|---|
| Pause/Resume | ✅ | ❌ |
| Continuous Listening | ✅ | ❌ |
| Sound Level Monitoring | ✅ | ✅ |
| Manual Control | ✅ | Limited |
| Auto-restart on interrupt | ✅ | ❌ |
Add this to your package's pubspec.yaml file:
dependencies:
manual_speech_to_text: ^1.0.4
This package depends on the following packages:
dependencies:
permission_handler: ^11.3.1
speech_to_text: ^7.0.0
Add the following permission to your Android Manifest (android/app/src/main/AndroidManifest.xml):
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<!-- other tags -->
<queries>
<intent>
<action android:name="android.speech.RecognitionService" />
</intent>
</queries>
import 'package:manual_speech_to_text/manual_speech_to_text.dart';
void main() {
final controller = ManualSttController();
// Set up listeners
controller.listen(
onListeningStateChanged: (ManualSttState state) {
print('State changed to: $state');
},
onListeningTextChanged: (String text) {
print('Recognized text: $text');
},
onSoundLevelChanged: (double level) {
print('Sound level: $level');
},
);
// Start listening
controller.startStt();
// Pause recognition
controller.pauseStt();
// Resume recognition
controller.resumeStt();
// Stop recognition
controller.stopStt();
// Don't forget to dispose when done
controller.dispose();
}
import 'package:flutter/material.dart';
import 'package:manual_speech_to_text/manual_speech_to_text.dart';
void main(List<String> args) {
runApp(const MaterialApp(home: ManualSpeechRecognitionExample()));
}
class ManualSpeechRecognitionExample extends StatefulWidget {
const ManualSpeechRecognitionExample({super.key});
@override
State<ManualSpeechRecognitionExample> createState() =>
_ManualSpeechRecognitionStateExample();
}
class _ManualSpeechRecognitionStateExample
extends State<ManualSpeechRecognitionExample> {
late ManualSttController _controller;
String _finalRecognizedText = '';
ManualSttState _currentState = ManualSttState.stopped;
double _soundLevel = 0.0;
@override
void initState() {
super.initState();
_controller = ManualSttController(context);
_setupController();
}
void _setupController() {
_controller.listen(
onListeningStateChanged: (state) {
setState(() => _currentState = state);
},
onListeningTextChanged: (recognizedText) {
setState(() => _finalRecognizedText = recognizedText);
},
onSoundLevelChanged: (level) {
setState(() => _soundLevel = level);
},
);
//? Optional: clear text on start
// _controller.clearTextOnStart = false;
//? Optional: Set language
// _controller.localId = 'en-US';
//? Optional: Enable haptic feedback
// _controller.enableHapticFeedback = true;
//? Optional: pause if mute for specified duration
// _controller.pauseIfMuteFor = Duration(seconds: 10);
//? Optional: Handle permanently denied microphone permission
// _controller.handlePermanentlyDeniedPermission(() {
// ScaffoldMessenger.of(context).showSnackBar(
// const SnackBar(content: Text('Microphone permission is required')),
// );
// });
//? Optional: Customize Permission Dialog
// NOTE: if [handlePermanentlyDeniedPermission] this function is used, then below dialog's customization won't work.
//? Optional:
// _controller.permanentDenialDialogTitle = 'Microphone Access Required';
//? Optional:
// _controller.permanentDenialDialogContent =
// 'Speech-to-text functionality needs microphone permission.';
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Manual Speech Recognition")),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('State: ${_currentState.name}'),
const SizedBox(height: 16),
Text(
'Final Recognized Text: $_finalRecognizedText',
style: const TextStyle(fontSize: 20),
),
const SizedBox(height: 16),
LinearProgressIndicator(value: _soundLevel),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: _currentState == ManualSttState.stopped
? _controller.startStt
: null,
child: const Text(
'Start',
),
),
ElevatedButton(
onPressed: _currentState == ManualSttState.listening
? _controller.pauseStt
: _currentState == ManualSttState.paused
? _controller.resumeStt
: null,
child: Text(_currentState == ManualSttState.paused
? 'Resume'
: 'Pause'),
),
ElevatedButton(
onPressed: _currentState != ManualSttState.stopped
? _controller.stopStt
: null,
child: const Text('Stop'),
),
],
),
],
),
),
);
}
}
The main controller class for managing speech recognition.
listen({required onListeningStateChanged, required onListeningTextChanged, onSoundLevelChanged}): Set up callbacks for state changes, text recognition, and sound level monitoringstartStt(): Start speech recognitionstopStt(): Stop speech recognitionpauseStt(): Pause speech recognitionresumeStt(): Resume paused speech recognitiondispose(): Clean up resourcesenableHapticFeedback: Enable/disable haptic feedback during recognitionlocalId: Set the locale for speech recognition (e.g., 'en-US')handlePermanentlyDeniedPermission: Handle Permanently denied microphone permissionpermanentDenialDialogTitle : Set the title of the permanent denialization dialogpermanentDenialDialogContent : Set the content of the permanent denialization dialogclearTextOnStart: Clears recognized text on [startStt()] methodpauseIfMuteFor: Pause if user doesn't speak for specified durationEnum representing the possible states of speech recognition:
enum ManualSttState {
listening, // Currently listening for speech
paused, // Recognition is paused
stopped // Recognition is stopped
}
NOTE: PLEASE ADD MANIFEST CONFIGURATION IN AndroidManifest.xml AS MENTIONED IN DOCUMENT, FOR MORE DETAIL PLEASE REFER STANDARD speech_to_text PACKAGE.
The package now includes built-in microphone permission handling:
If microphone permission is permanently denied, the package:
Example of custom permission handling:
// Optional: Handle permanently denied microphone permission
_controller.handlePermanentlyDeniedPermission(() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Microphone permission is required')),
);
});
_controller.permanentDenialDialogTitle = 'Microphone Access Required';
_controller.permanentDenialDialogContent = 'Speech-to-text functionality needs microphone permission.';
Ensure your Android Manifest (android/app/src/main/AndroidManifest.xml) includes:
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<!-- other tags -->
<queries>
<intent>
<action android:name="android.speech.RecognitionService" />
</intent>
</queries>
Resource Management
// Proper disposal in StatefulWidget
@override
void dispose() {
controller.dispose();
super.dispose();
}
Error Handling
try {
await controller.startStt();
} catch (e) {
print('Speech recognition error: $e');
// Handle error appropriately
}
Microphone Permission
<action android:name="android.speech.RecognitionService" /> tag in AndroidManifest.xml between proper tagsRecognition Stops Unexpectedly
Recognition Quality Issues
Performance Optimization
Beef Sound
startStt method).stopStt or pauseStt methods).Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
This project is licensed under the MIT License - see the LICENSE file for details.
This package builds upon the speech_to_text package, adding manual control and continuous listening capabilities.