import 'dart:async'; import 'package:flutter/material.dart'; import 'package:speech_to_text/speech_recognition_error.dart'; import 'package:speech_to_text/speech_recognition_event.dart'; import 'package:speech_to_text/speech_recognition_result.dart'; import 'package:speech_to_text/speech_to_text.dart'; /// Simplifies interaction with [SpeechToText] by handling all the callbacks and notifying /// listeners as events happen. /// /// Here's an example of using the [SpeechToTextProvider] /// ``` /// var speechProvider = SpeechToTextProvider( SpeechToText()); /// var available = await speechProvider.initialize(); /// StreamSubscription _subscription; /// _subscription = speechProvider.recognitionController.stream.listen((recognitionEvent) { /// if (recognitionEvent.eventType == SpeechRecognitionEventType.finalRecognitionEvent ) { /// print("I heard: ${recognitionEvent.recognitionResult.recognizedWords}"); /// } /// }); /// speechProvider.addListener(() { /// var words = speechProvider.lastWords; /// }); class SpeechToTextProvider extends ChangeNotifier { final StreamController _recognitionController = StreamController.broadcast(); final SpeechToText _speechToText; SpeechRecognitionResult _lastResult; double _lastLevel = 0; List _locales = []; LocaleName _systemLocale; /// Only construct one instance in an application. /// /// Do not call `initialize` on the [SpeechToText] that is passed as a parameter, instead /// call the [initialize] method on this class. SpeechToTextProvider(this._speechToText); Stream get stream => _recognitionController.stream; /// Returns the last result received, may be null. SpeechRecognitionResult get lastResult => _lastResult; /// Returns the last error received, may be null. SpeechRecognitionError get lastError => _speechToText.lastError; /// Returns the last sound level received. /// /// Note this is only available when the `soundLevel` is set to true on /// a call to [listen], will be 0 at all other times. double get lastLevel => _lastLevel; /// Initializes the provider and the contained [SpeechToText] instance. /// /// Returns true if [SpeechToText] was initialized successful and can now /// be used, false otherwse. Future initialize() async { if (isAvailable) { return isAvailable; } bool availableBefore = _speechToText.isAvailable; bool available = await _speechToText.initialize(onStatus: _onStatus, onError: _onError); if (available) { _locales = []; _locales.addAll(await _speechToText.locales()); _systemLocale = await _speechToText.systemLocale(); } if (availableBefore != available) { notifyListeners(); } return available; } /// Returns true if the provider has been initialized and can be used to recognize speech. bool get isAvailable => _speechToText.isAvailable; /// Returns true if the provider cannot be used to recognize speech, either because it has not /// yet been initialized or because initialization failed. bool get isNotAvailable => !_speechToText.isAvailable; /// Returns true if [SpeechToText] is listening for new speech. bool get isListening => _speechToText.isListening; /// Returns true if [SpeechToText] is not listening for new speech. bool get isNotListening => _speechToText.isNotListening; /// Returns true if [SpeechToText] has a previous error. bool get hasError => _speechToText.hasError; /// Returns true if [lastResult] has a last result. bool get hasResults => null != _lastResult; /// Returns the list of locales that are available on the device for speech recognition. List get locales => _locales; /// Returns the locale that is currently set as active on the device. LocaleName get systemLocale => _systemLocale; /// Start listening for new events, set [partialResults] to true to receive interim /// recognition results. /// /// [soundLevel] set to true to be notified on changes to the input sound level /// on the microphone. /// /// [listenFor] sets the maximum duration that it will listen for, after /// that it automatically stops the listen for you. /// /// [pauseFor] sets the maximum duration of a pause in speech with no words /// detected, after that it automatically stops the listen for you. /// /// Call this only after a successful [initialize] call void listen( {bool partialResults = false, bool soundLevel = false, Duration listenFor, Duration pauseFor}) { _lastLevel = 0; _lastResult = null; if (soundLevel) { _speechToText.listen( partialResults: partialResults, listenFor: listenFor, pauseFor: pauseFor, cancelOnError: true, onResult: _onListenResult, // onSoundLevelChange: _onSoundLevelChange); ); } else { _speechToText.listen( partialResults: partialResults, listenFor: listenFor, pauseFor: pauseFor, cancelOnError: true, onResult: _onListenResult); } } /// Stops a current active listening session. /// /// Call this after calling [listen] to stop the recognizer from listening further /// and return the current result as final. void stop() { _speechToText.stop(); notifyListeners(); } /// Cancel a current active listening session. /// /// Call this after calling [listen] to stop the recognizer from listening further /// and ignore any results recognized so far. void cancel() { _speechToText.cancel(); notifyListeners(); } void _onError(SpeechRecognitionError errorNotification) { _recognitionController.add(SpeechRecognitionEvent( SpeechRecognitionEventType.errorEvent, null, errorNotification, isListening, null)); notifyListeners(); } void _onStatus(String status) { _recognitionController.add(SpeechRecognitionEvent( SpeechRecognitionEventType.statusChangeEvent, null, null, isListening, null)); notifyListeners(); } void _onListenResult(SpeechRecognitionResult result) { _lastResult = result; _recognitionController.add(SpeechRecognitionEvent( result.finalResult ? SpeechRecognitionEventType.finalRecognitionEvent : SpeechRecognitionEventType.partialRecognitionEvent, result, null, isListening, null)); notifyListeners(); } // void _onSoundLevelChange(double level) { // _lastLevel = level; // _recognitionController.add(SpeechRecognitionEvent( // SpeechRecognitionEventType.soundLevelChangeEvent, // null, // null, // null, // level)); // notifyListeners(); // } }