Flutter Text To Speech: Speak several strings subsequently with different SpeechRates

1,699

Solution 1

This should now work with the latest flutter_tts version.

You simply need to set awaitSpeakCompletion before the speaking happens. You can update your run method like so:

Future<void> runTextToSpeech(String currentTtsString, double currentSpeechRate) async {
  FlutterTts flutterTts;
  flutterTts = new FlutterTts();
  await flutterTts.awaitSpeakCompletion(true);
  await flutterTts.setLanguage("en-GB");
  await flutterTts.setVolume(1.0);
  await flutterTts.setPitch(1.0);
  await flutterTts.isLanguageAvailable("en-GB");
  await flutterTts.setSpeechRate(currentSpeechRate);
  await flutterTts.speak(currentTtsString);
}

Solution 2

import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_tts/flutter_tts.dart';

class SpeakerClass extends StatefulWidget{
  @override
  State<StatefulWidget> createState() {
  // TODO: implement createState
  return _SpeakerClassState();
  }
}

class _SpeakerClassState extends State<SpeakerClass>{
  String text1 = 'eins';
  String text2 = 'zwei';
  String text3 = 'drei';
  String text4 = 'vier';
  String currentTtsString;
  double ttsSpeechRate1 = 0.5;
  double ttsSpeechRate2 = 1.0;
  double currentSpeechRate;

  FlutterTts flutterTts;
  bool bolSpeaking = false;

  Future playTtsString1() async {
    bolSpeaking = true;
    currentTtsString = text1;
    currentSpeechRate = ttsSpeechRate1;
    await runTextToSpeech(currentTtsString, currentSpeechRate);
    return null;
  }

  Future playTtsString2() async {
    bolSpeaking = true;
    currentTtsString = text2;
    currentSpeechRate = ttsSpeechRate2;
    await runTextToSpeech(currentTtsString, currentSpeechRate);
    return null;
  }

  @override
  Widget build(BuildContext context) {
  return Scaffold(
    body: FloatingActionButton (
      backgroundColor: Colors.blue,
      child: Icon(Icons.volume_up, color: Colors.white),
      onPressed: () async {
        // Play String 1
        await playTtsString1();

        // Check String 1 Finish
        new Future.delayed(new Duration(milliseconds: 100), () async {
            // loop until bolSpeaking = false
            while (bolSpeaking) {
              await Thread.sleep(100);
            }

            // play String 2
            await playTtsString2();
        }
      },
    )
  );
  }
}


Future<void> runTextToSpeech(String currentTtsString, double currentSpeechRate) async {
  flutterTts = new FlutterTts();
  await flutterTts.setLanguage("en-GB");
  await flutterTts.setVolume(1.0);
  await flutterTts.setPitch(1.0);
  await flutterTts.isLanguageAvailable("en-GB");
  await flutterTts.setSpeechRate(currentSpeechRate);

  flutterTts.setCompletionHandler(() {
    setState(() {
      // The following code(s) will be called when the TTS finishes speaking
      bolSpeaking = false;
    });
  });

  flutterTts.speak(currentTtsString);
}
Share:
1,699
WeAreThePeople
Author by

WeAreThePeople

Updated on December 11, 2022

Comments

  • WeAreThePeople
    WeAreThePeople over 1 year

    I have a problem with the Flutter Text To Speech package. When clicking on a FloatingActionButton I would like to speak/play several Strings (with different Speechrates) subsequently. However, when doing so, I can only hear the last string that I have passed onto the function and not the first one.

    As you can see in the code below, I have tried to make use of the asynchronus programming (async / await).

    import 'package:flutter/material.dart';
    import 'dart:async';
    import 'package:flutter_tts/flutter_tts.dart';
    
    class SpeakerClass extends StatefulWidget{
      @override
      State<StatefulWidget> createState() {
      // TODO: implement createState
      return _SpeakerClassState();
      }
    }
    
    class _SpeakerClassState extends State<SpeakerClass>{
      String text1 = 'eins';
      String text2 = 'zwei';
      String text3 = 'drei';
      String text4 = 'vier';
      String currentTtsString;
      double ttsSpeechRate1 = 0.5;
      double ttsSpeechRate2 = 1.0;
      double currentSpeechRate;
    
    
      Future playTtsString1() async {
        currentTtsString = text1;
        currentSpeechRate = ttsSpeechRate1;
        await runTextToSpeech(currentTtsString, currentSpeechRate);
        return null;
      }
    
      Future playTtsString2() async {
        currentTtsString = text2;
        currentSpeechRate = ttsSpeechRate2;
        await runTextToSpeech(currentTtsString, currentSpeechRate);
        return null;
      }
    
      @override
      Widget build(BuildContext context) {
      return Scaffold(
        body: FloatingActionButton (
          backgroundColor: Colors.blue,
          child: Icon(Icons.volume_up, color: Colors.white),
          onPressed: () async {
            await playTtsString1();
            await playTtsString2();
          },
        )
      );
      }
    
    }
    
    
    Future<void> runTextToSpeech(String currentTtsString, double currentSpeechRate) async {
      FlutterTts flutterTts;
      flutterTts = new FlutterTts();
      await flutterTts.setLanguage("en-GB");
      await flutterTts.setVolume(1.0);
      await flutterTts.setPitch(1.0);
      await flutterTts.isLanguageAvailable("en-GB");
      await flutterTts.setSpeechRate(currentSpeechRate);
      await flutterTts.speak(currentTtsString);
    }
    

    When pressing the FloatingActionButton I expect the program to first carry out the function playTtsString1 ("eins" with a speed of 0.5) and afterwards the function playTtsString2 ("zwei" with a speed of 1).

    However, somehow I can only hear the program saying "zwei". I guess the program is not waiting for the first function "playTtsString1" to be finished and already carries out the second function "playTtsString2". I would really appreciate any help on this matter!

  • WeAreThePeople
    WeAreThePeople almost 5 years
    Hey, thanks for the answer man! Unfortunately, it still does not work as expected. I edited the code as you have suggested, however, I can only hear the second text and not the first text when pressing the button.
  • Kenneth Li
    Kenneth Li almost 5 years
    that means flutterTts.speak() returns immediately after call, without waiting the speaking to complete
  • Kenneth Li
    Kenneth Li almost 5 years
    ok, I remember, there is a setCompletionHandler, I'll update the answe
  • Kenneth Li
    Kenneth Li almost 5 years
    Pls. check the updated answer. I may have made mistakes in the code, i.e. the [position] of some codes may not be correct. In my original codes, I did not [NEW] a [flutterTts] every time I want to speak a string, instead, my flutterTts is a static variable in another dart file, and only initialized ONCE in my whole app. So I'm not sure if this variable is to be initialized many times, will the flutterTts.setCompletionHandler still work or not!
  • WeAreThePeople
    WeAreThePeople almost 5 years
    Really thanks for your help! Somehow the code still doesn't work. It seems like there are quite a few mistakes inside of it.
  • Augustine Joseph
    Augustine Joseph over 2 years
    This answer works perfectly. Adding awaitSpeakCompletion works