import 'dart:async'; import 'dart:html' as html; import 'dart:js'; import 'package:flutter/services.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; enum TtsState { playing, stopped, paused, continued } class FlutterTtsPlugin { static const String PLATFORM_CHANNEL = "flutter_tts"; static MethodChannel channel; bool awaitSpeakCompletion = false; TtsState ttsState = TtsState.stopped; get isPlaying => ttsState == TtsState.playing; get isStopped => ttsState == TtsState.stopped; get isPaused => ttsState == TtsState.paused; get isContinued => ttsState == TtsState.continued; static void registerWith(Registrar registrar) { channel = MethodChannel( PLATFORM_CHANNEL, const StandardMethodCodec(), registrar.messenger); final instance = FlutterTtsPlugin(); channel.setMethodCallHandler(instance.handleMethodCall); } html.SpeechSynthesis synth; html.SpeechSynthesisUtterance utterance; List voices; List languages; FlutterTtsPlugin() { utterance = html.SpeechSynthesisUtterance(); synth = html.window.speechSynthesis; _listeners(); } void _listeners() { utterance.onStart.listen((e) { ttsState = TtsState.playing; channel.invokeMethod("speak.onStart", null); }); utterance.onEnd.listen((e) { ttsState = TtsState.stopped; channel.invokeMethod("speak.onComplete", null); if (awaitSpeakCompletion) { return 1; } }); utterance.onPause.listen((e) { ttsState = TtsState.paused; channel.invokeMethod("speak.onPause", null); }); utterance.onResume.listen((e) { ttsState = TtsState.continued; channel.invokeMethod("speak.onContinue", null); }); utterance.onError.listen((e) { channel.invokeMethod("speak.onError", e); if (awaitSpeakCompletion) { return 0; } }); } Future handleMethodCall(MethodCall call) async { switch (call.method) { case 'speak': final text = call.arguments as String; _speak(text); if (!awaitSpeakCompletion) { return 1; } break; case 'awaitSpeakCompletion': awaitSpeakCompletion = call.arguments as bool; return 1; break; case 'stop': _stop(); return 1; break; case 'pause': _pause(); return 1; break; case 'setLanguage': final language = call.arguments as String; _setLanguage(language); return 1; break; case 'getLanguages': return _getLanguages(); break; case 'setSpeechRate': final rate = call.arguments as num; _setRate(rate); return 1; break; case 'setVolume': final volume = call.arguments as num; _setVolume(volume); return 1; break; case 'setPitch': final pitch = call.arguments as num; _setPitch(pitch); return 1; break; case 'isLanguageAvailable': final lang = call.arguments as String; return _isLanguageAvailable(lang); break; default: throw PlatformException( code: 'Unimplemented', details: "The flutter_tts plugin for web doesn't implement " "the method '${call.method}'"); } } void _speak(String text) { if (ttsState == TtsState.stopped || ttsState == TtsState.paused) { utterance.text = text; if (ttsState == TtsState.paused) { synth.resume(); } else { synth.speak(utterance); } } } void _stop() { if (ttsState != TtsState.stopped) { synth.cancel(); } } void _pause() { if (ttsState == TtsState.playing || ttsState == TtsState.continued) { synth.pause(); } } void _setRate(num rate) => utterance.rate = rate * 2.0; void _setVolume(num volume) => utterance.volume = volume; void _setPitch(num pitch) => utterance.pitch = pitch; void _setLanguage(String language) => utterance.lang = language; bool _isLanguageAvailable(String language) { if (voices?.isEmpty ?? true) _setVoices(); if (languages?.isEmpty ?? true) _setLanguages(); for (var lang in languages) { if (lang.toLowerCase() == language.toLowerCase()) return true; } return false; } List _getLanguages() { if (voices?.isEmpty ?? true) _setVoices(); if (languages?.isEmpty ?? true) _setLanguages(); return languages; } void _setVoices() { voices = context['speechSynthesis'].callMethod('getVoices') as JsArray; } void _setLanguages() { var langs = Set(); for (var v in voices) { langs.add(v['lang'] as String); } languages = langs.toList(); } }