speech_to_text 2.0.1

  • Readme
  • Changelog
  • Example
  • Installing
  • 96

speech_to_text #

pub package build status

A library that exposes device specific speech recognition capability.

This plugin contains a set of classes that make it easy to use the speech recognition capabilities of the mobile device in Flutter. It supports both Android and iOS. The target use cases for this library are commands and short phrases, not continuous spoken conversion or always on listening.

Recent Updates #

The 2.0.0 version uses the new Flutter 1.12.x plugin APIs, although it may work with older with older versions of Flutter it is not guaranteed. It also blocks duplicate notifications on some Android versions, if you feel you have missed an onResult notification please post an issue. See the change log for more details.

The 1.0.0 version adds the ability to automatically cancel listening on a permanent error. This is a new parameter on the listen method, defaulted to false for backward compatibility. It also adds the ability to control whether partial and complete or only complete results are sent during listening.

Note: Feedback from any test devices is welcome.

Using #

To recognize text from the microphone import the package and call the plugin, like so:

import 'package:speech_to_text/speech_to_text.dart' as stt;

    stt.SpeechToText speech = stt.SpeechToText();
    bool available = await speech.initialize( onStatus: statusListener, onError: errorListener );
    if ( available ) {
        speech.listen( onResult: resultListener );
    }
    else {
        print("The user has denied the use of speech recognition.");
    }
    // some time later...
    speech.stop()

Permissions #

Applications using this plugin require user permissions.

iOS #

Add the following keys to your Info.plist file, located in <project root>/ios/Runner/Info.plist:

  • NSSpeechRecognitionUsageDescription - describe why your app uses speech recognition. This is called Privacy - Speech Recognition Usage Description in the visual editor.
  • NSMicrophoneUsageDescription - describe why your app needs access to the microphone. This is called Privacy - Microphone Usage Description in the visual editor.

Android #

Add the record audio permission to your AndroidManifest.xml file, located in <project root>/android/app/src/main/AndroidManifest.xml.

  • android.permission.RECORD_AUDIO - this permission is required for microphone access.
  • android.permission.INTERNET - this permission is required because speech recognition may use remote services.

Adding Sounds for iOS (optional) #

Android automatically plays system sounds when speech listening starts or stops but iOS does not. This plugin supports playing sounds to indicate listening status on iOS if sound files are available as assets in the application. To enable sounds in an application using this plugin add the sound files to the project and reference them in the assets section of the application pubspec.yaml. The location and filenames of the sound files must exactly match what is shown below or they will not be found. The example application for the plugin shows the usage.

  assets:
  - assets/sounds/speech_to_text_listening.m4r
  - assets/sounds/speech_to_text_cancel.m4r
  - assets/sounds/speech_to_text_stop.m4r
  • speech_to_text_listening.m4r - played when the listen method is called.
  • speech_to_text_cancel.m4r - played when the cancel method is called.
  • speech_to_text_stop.m4r - played when the stop method is called.

Troubleshooting #

Not working on a particular Android device #

The symptom for this issue is that the initialize method will always fail. If you turn on debug logging using the debugLogging: true flag on the initialize method you'll see 'Speech recognition unavailable' in the Android log. There's a lengthy issue discussion here https://github.com/csdcorp/speech_to_text/issues/36 about this. The issue seems to be that the recognizer is now always automatically enabled on the device. Two key things helped resolve the issue in this case at least.

First

  1. Go to Google Play
  2. Search for 'Google'
  3. You should find this app: https://play.google.com/store/apps/details?id=com.google.android.googlequicksearchbox If 'Disabled' enable it

This is the SO post that helped: https://stackoverflow.com/questions/28769320/how-to-check-wether-speech-recognition-is-available-or-not

Second

Ensure the app has the required permissions. The symptom for this that you get a permanent error notification 'error_audio_error` when starting a listen session. Here's a Stack Overflow post that addresses that https://stackoverflow.com/questions/46376193/android-speechrecognizer-audio-recording-error Here's the important excerpt:

You should go to system setting, Apps, Google app, then enable its permission of microphone.

Changelog #

2.0.1 #

  • Resolves an issue with the Android implementation not handling permission requests properly on apps that didn't use the 1.12.x plugin APIs for registration. The permission dialog would not appear and permission was denied.

Fix #

2.0.0 #

Breaking #

  • Upgraded to New Swift 1.12 plugin structure, may work with older Flutter version but not guaranteed

New #

  • the plugin now requests both speech and microphone permission on initialize on iOS
  • added debugLogging parameter to the initialize method to control native logging

Fix #

  • The Android implementation now blocks duplicate results notifications. It appears that at least on some Android versions the final results notification onResults is notified twice when Android automatically terminates the session due to a pause time. The de-duplication looks for successive notifications with < 100 ms between them and blocks the second. If you miss any onResult notifications please post an issue.

1.1.0 #

New #

  • error_timeout has been separated into error_network_timeout and error_speech_timeout

1.0.0 #

New #

  • hasPermission to check for the current permission without bringing up the system dialog
  • listen has a new optional cancelOnError parameter to support automatically canceling a listening session on a permanent error.
  • listen has a new optional partialResults parameter that controls whether the callback receives partial or only final results.

0.8.0 #

New #

  • speech recognizer now exposes multiple possible transcriptions for each recognized speech
  • alternates list on SpeechRecognitionResult exposes alternate transcriptions of voice
  • confidence on SpeechRecognitionResult gives an estimate of confidence in the transcription
  • isConfident on SpeechRecognitionResult supports testing confidence
  • hasConfidenceRating on SpeechRecognitionResult indicates if confidence was provided from the device
  • new SpeechRecognitionWords class gives details on per transcription words and confidence

Fix #

  • speechRecognizer availabilityDidChange was crashing if invoked due to an invalid parameter type
  • Added iOS platform 10 to example Podfile to resolve compilation warnings

0.7.2 #

Breaking #

  • Upgrade Swift to version 5 to match Flutter. Projects using this plugin must now switch to 5.

0.7.1 #

Fix #

  • Upgrade Kotlin to 1.3.5 to match the Flutter 1.12 version
  • Upgrade Gradle build to 3.5.0 to match the Flutter 1.12 version
  • Android version of the plugin was repeating the system default locale in the locales list

0.7.0 #

New #

  • locales method returns the list of available languages for speech
  • new optional localeId parameter on listen method supports choosing the comprehension language separately from the current system locale.

Breaking #

  • cancel and stop are now async

0.6.3 #

Fix #

  • request permission fix on Android to ensure it doesn't conflict with other requests

0.6.2 #

Fix #

  • channel invoke wasn't being done on the main thread in iOS

0.6.1 #

Fix #

  • listening sound was failing due to timing, now uses play and record mode on iOS.

0.6.0 #

Breaking #

  • The filenames for the optional sounds for iOS have changed.

New #

  • Added an optional listenFor parameter to set a max duration to listen for speech and then automatically cancel.

Fix #

  • Was failing to play sounds because of record mode. Now plays sounds before going into record mode and after coming out.
  • Status listener was being ignored, now properly notifies on status changes.

0.5.1 #

  • Fixes a problem where the recognizer left the AVAudioSession in record mode which meant that subsequent sounds couldn't be played.

0.5.0 #

Initial draft with limited functionality, supports:

  • initializing speech recognition
  • asking the user for permission if required
  • listening for recognized speech
  • canceling the current recognition session
  • stopping the current recognition session
  • Android and iOS 10+ support

Missing:

  • some error handling
  • testing across multiple OS versions
  • and more, to be discovered...

example/lib/main.dart

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:speech_to_text/speech_recognition_error.dart';
import 'package:speech_to_text/speech_recognition_result.dart';
import 'package:speech_to_text/speech_to_text.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool _hasSpeech = false;
  bool _stressTest = false;
  double level = 0.0;
  int _stressLoops = 0;
  String lastWords = "";
  String lastError = "";
  String lastStatus = "";
  String _currentLocaleId = "";
  List<LocaleName> _localeNames = [];
  final SpeechToText speech = SpeechToText();

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

  Future<void> initSpeechState() async {
    bool hasSpeech = await speech.initialize(
        onError: errorListener, onStatus: statusListener);
    if (hasSpeech) {
      _localeNames = await speech.locales();

      var systemLocale = await speech.systemLocale();
      _currentLocaleId = systemLocale.localeId;
    }

    if (!mounted) return;

    setState(() {
      _hasSpeech = hasSpeech;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Speech to Text Example'),
        ),
        body: Column(children: [
          Center(
            child: Text(
              'Speech recognition available',
              style: TextStyle(fontSize: 22.0),
            ),
          ),
          Container(
            child: Column(
              children: <Widget>[
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: <Widget>[
                    FlatButton(
                      child: Text('Initialize'),
                      onPressed: _hasSpeech ? null : initSpeechState,
                    ),
                    FlatButton(
                      child: Text('Stress Test'),
                      onPressed: stressTest,
                    ),
                  ],
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: <Widget>[
                    FlatButton(
                      child: Text('Start'),
                      onPressed: !_hasSpeech || speech.isListening
                          ? null
                          : startListening,
                    ),
                    FlatButton(
                      child: Text('Stop'),
                      onPressed: speech.isListening ? stopListening : null,
                    ),
                    FlatButton(
                      child: Text('Cancel'),
                      onPressed: speech.isListening ? cancelListening : null,
                    ),
                  ],
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: <Widget>[
                    DropdownButton(
                      onChanged: (selectedVal) => _switchLang(selectedVal),
                      value: _currentLocaleId,
                      items: _localeNames
                          .map(
                            (localeName) => DropdownMenuItem(
                              value: localeName.localeId,
                              child: Text(localeName.name),
                            ),
                          )
                          .toList(),
                    ),
                  ],
                )
              ],
            ),
          ),
          Expanded(
            flex: 4,
            child: Column(
              children: <Widget>[
                Center(
                  child: Text(
                    'Recognized Words',
                    style: TextStyle(fontSize: 22.0),
                  ),
                ),
                Expanded(
                  child: Stack(
                    children: <Widget>[
                      Container(
                        color: Theme.of(context).selectedRowColor,
                        child: Center(
                          child: Text(
                            lastWords,
                            textAlign: TextAlign.center,
                          ),
                        ),
                      ),
                      Positioned.fill(
                        bottom: 10,
                        child: Align(
                          alignment: Alignment.bottomCenter,
                          child: Container(
                            width: 40,
                            height: 40,
                            alignment: Alignment.center,
                            decoration: BoxDecoration(
                              boxShadow: [
                                BoxShadow(
                                    blurRadius: .26,
                                    spreadRadius: level * 1.5,
                                    color: Colors.black.withOpacity(.05))
                              ],
                              color: Colors.white,
                              borderRadius:
                                  BorderRadius.all(Radius.circular(50)),
                            ),
                            child: IconButton(icon: Icon(Icons.mic)),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
          Expanded(
            flex: 1,
            child: Column(
              children: <Widget>[
                Center(
                  child: Text(
                    'Error Status',
                    style: TextStyle(fontSize: 22.0),
                  ),
                ),
                Center(
                  child: Text(lastError),
                ),
              ],
            ),
          ),
          Container(
            padding: EdgeInsets.symmetric(vertical: 20),
            color: Theme.of(context).backgroundColor,
            child: Center(
              child: speech.isListening
                  ? Text(
                      "I'm listening...",
                      style: TextStyle(fontWeight: FontWeight.bold),
                    )
                  : Text(
                      'Not listening',
                      style: TextStyle(fontWeight: FontWeight.bold),
                    ),
            ),
          ),
        ]),
      ),
    );
  }

  void stressTest() {
    if (_stressTest) {
      return;
    }
    _stressLoops = 0;
    _stressTest = true;
    print("Starting stress test...");
    startListening();
  }

  void changeStatusForStress(String status) {
    if (!_stressTest) {
      return;
    }
    if (speech.isListening) {
      stopListening();
    } else {
      if (_stressLoops >= 100) {
        _stressTest = false;
        print("Stress test complete.");
        return;
      }
      print("Stress loop: $_stressLoops");
      ++_stressLoops;
      startListening();
    }
  }

  void startListening() {
    lastWords = "";
    lastError = "";
    speech.listen(
        onResult: resultListener,
        listenFor: Duration(seconds: 10),
        localeId: _currentLocaleId,
        onSoundLevelChange: soundLevelListener,
        cancelOnError: true,
        partialResults: true);
    setState(() {});
  }

  void stopListening() {
    speech.stop();
    setState(() {
      level = 0.0;
    });
  }

  void cancelListening() {
    speech.cancel();
    setState(() {
      level = 0.0;
    });
  }

  void resultListener(SpeechRecognitionResult result) {
    setState(() {
      lastWords = "${result.recognizedWords} - ${result.finalResult}";
    });
  }

  void soundLevelListener(double level) {
    setState(() {
      this.level = level;
    });
  }

  void errorListener(SpeechRecognitionError error) {
    setState(() {
      lastError = "${error.errorMsg} - ${error.permanent}";
    });
  }

  void statusListener(String status) {
    changeStatusForStress(status);
    setState(() {
      lastStatus = "$status";
    });
  }

  _switchLang(selectedVal) {
    setState(() {
      _currentLocaleId = selectedVal;
    });
    print(selectedVal);
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  speech_to_text: ^2.0.1

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:speech_to_text/speech_to_text.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
92
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
96
Learn more about scoring.

We analyzed this package on Apr 3, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.7.1
  • pana: 0.13.6
  • Flutter: 1.12.13+hotfix.8

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
flutter 0.0.0
json_annotation ^3.0.0 3.0.1
Transitive dependencies
collection 1.14.11 1.14.12
meta 1.1.8
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
build_runner ^1.0.0
flutter_test
json_serializable ^3.0.0