Flutter文本转语音插件flutter_tts的使用
Flutter文本转语音插件flutter_tts的使用
Flutter文本转语音(Text To Speech,TTS)是一个强大的工具,可以帮助开发者为应用程序添加语音功能。flutter_tts
是一个流行的插件,支持多个平台,如Android、iOS、Web、Windows和macOS。它提供了丰富的API来控制语音合成的行为。
特性
- 多平台支持:适用于Android、iOS、Web、Windows和macOS。
- 核心功能:
- ✅ 发音 (
speak
) - ✅ 停止发音 (
stop
) - ✅ 获取语言列表 (
getLanguages
) - ✅ 设置语言 (
setLanguage
) - ✅ 设置语速 (
setSpeechRate
) - ✅ 设置音量 (
setVolume
) - ✅ 设置音调 (
setPitch
) - ✅ 获取可用声音 (
getVoices
) - ✅ 设置声音 (
setVoice
) - ✅ 检查语言是否可用 (
isLanguageAvailable
) - ✅ 暂停发音 (
pause
) 和继续发音 (continue
) - ✅ 将语音合成到文件 (
synthesizeToFile
)
- ✅ 发音 (
使用方法
安装依赖
在 pubspec.yaml
文件中添加 flutter_tts
依赖:
dependencies:
flutter:
sdk: flutter
flutter_tts: ^3.6.0 # 请确保使用最新版本
然后运行 flutter pub get
来安装依赖。
初始化和配置
Android 配置
确保你的 minSdkVersion
不低于21,并且更新 Kotlin Gradle 插件版本至 1.9.10
或更高版本。
// android/app/build.gradle
minSdkVersion 21
// android/build.gradle
ext.kotlin_version = '1.9.10'
// 或者在 android/settings.gradle 中
id "org.jetbrains.kotlin.android" version "1.9.10" apply false
对于目标为Android 11的应用,需要在 AndroidManifest.xml
中声明 TTS 服务:
<queries>
<intent>
<action android:name="android.intent.action.TTS_SERVICE" />
</intent>
</queries>
iOS 配置
如果你的项目是用Objective-C模板创建的,可能会遇到一些问题。建议查看 Flutter#16049 获取更多信息。
示例代码
下面是一个完整的示例应用,展示了如何使用 flutter_tts
插件:
import 'dart:async';
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter_tts/flutter_tts.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
enum TtsState { playing, stopped, paused, continued }
class _MyAppState extends State<MyApp> {
late FlutterTts flutterTts;
String? language;
String? engine;
double volume = 0.5;
double pitch = 1.0;
double rate = 0.5;
bool isCurrentLanguageInstalled = false;
String? _newVoiceText;
int? _inputLength;
TtsState ttsState = TtsState.stopped;
bool get isPlaying => ttsState == TtsState.playing;
bool get isStopped => ttsState == TtsState.stopped;
bool get isPaused => ttsState == TtsState.paused;
bool get isContinued => ttsState == TtsState.continued;
bool get isIOS => !kIsWeb && Platform.isIOS;
bool get isAndroid => !kIsWeb && Platform.isAndroid;
bool get isWindows => !kIsWeb && Platform.isWindows;
bool get isWeb => kIsWeb;
@override
void initState() {
super.initState();
initTts();
}
dynamic initTts() async {
flutterTts = FlutterTts();
await flutterTts.awaitSpeakCompletion(true);
if (isAndroid) {
await flutterTts.getDefaultEngine;
await flutterTts.getDefaultVoice;
}
flutterTts.setStartHandler(() {
setState(() {
print("Playing");
ttsState = TtsState.playing;
});
});
flutterTts.setCompletionHandler(() {
setState(() {
print("Complete");
ttsState = TtsState.stopped;
});
});
flutterTts.setCancelHandler(() {
setState(() {
print("Cancel");
ttsState = TtsState.stopped;
});
});
flutterTts.setPauseHandler(() {
setState(() {
print("Paused");
ttsState = TtsState.paused;
});
});
flutterTts.setContinueHandler(() {
setState(() {
print("Continued");
ttsState = TtsState.continued;
});
});
flutterTts.setErrorHandler((msg) {
setState(() {
print("error: $msg");
ttsState = TtsState.stopped;
});
});
}
Future<List<dynamic>> _getLanguages() async => await flutterTts.getLanguages;
Future<List<dynamic>> _getEngines() async => await flutterTts.getEngines;
Future<void> _getDefaultEngine() async {
var engine = await flutterTts.getDefaultEngine;
if (engine != null) {
print(engine);
}
}
Future<void> _getDefaultVoice() async {
var voice = await flutterTts.getDefaultVoice;
if (voice != null) {
print(voice);
}
}
Future<void> _speak() async {
await flutterTts.setVolume(volume);
await flutterTts.setSpeechRate(rate);
await flutterTts.setPitch(pitch);
if (_newVoiceText != null && _newVoiceText!.isNotEmpty) {
await flutterTts.speak(_newVoiceText!);
}
}
Future<void> _stop() async {
var result = await flutterTts.stop();
if (result == 1) setState(() => ttsState = TtsState.stopped);
}
Future<void> _pause() async {
var result = await flutterTts.pause();
if (result == 1) setState(() => ttsState = TtsState.paused);
}
@override
void dispose() {
super.dispose();
flutterTts.stop();
}
List<DropdownMenuItem<String>> getEnginesDropDownMenuItems(List<dynamic> engines) {
var items = <DropdownMenuItem<String>>[];
for (dynamic type in engines) {
items.add(DropdownMenuItem(
value: type as String?, child: Text(type as String)));
}
return items;
}
void changedEnginesDropDownItem(String? selectedEngine) async {
await flutterTts.setEngine(selectedEngine!);
language = null;
setState(() {
engine = selectedEngine;
});
}
List<DropdownMenuItem<String>> getLanguageDropDownMenuItems(List<dynamic> languages) {
var items = <DropdownMenuItem<String>>[];
for (dynamic type in languages) {
items.add(DropdownMenuItem(
value: type as String?, child: Text(type as String)));
}
return items;
}
void changedLanguageDropDownItem(String? selectedType) {
setState(() {
language = selectedType;
flutterTts.setLanguage(language!);
if (isAndroid) {
flutterTts.isLanguageInstalled(language!).then((value) => isCurrentLanguageInstalled = (value as bool));
}
});
}
void _onChange(String text) {
setState(() {
_newVoiceText = text;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter TTS'),
),
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
_inputSection(),
_btnSection(),
_engineSection(),
_futureBuilder(),
_buildSliders(),
if (isAndroid) _getMaxSpeechInputLengthSection(),
],
),
),
),
);
}
Widget _engineSection() {
if (isAndroid) {
return FutureBuilder<dynamic>(
future: _getEngines(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
return _enginesDropDownSection(snapshot.data as List<dynamic>);
} else if (snapshot.hasError) {
return Text('Error loading engines...');
} else {
return Text('Loading engines...');
}
});
} else {
return Container(width: 0, height: 0);
}
}
Widget _futureBuilder() => FutureBuilder<dynamic>(
future: _getLanguages(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
return _languageDropDownSection(snapshot.data as List<dynamic>);
} else if (snapshot.hasError) {
return Text('Error loading languages...');
} else
return Text('Loading Languages...');
});
Widget _inputSection() => Container(
alignment: Alignment.topCenter,
padding: EdgeInsets.only(top: 25.0, left: 25.0, right: 25.0),
child: TextField(
maxLines: 11,
minLines: 6,
onChanged: (String value) {
_onChange(value);
},
));
Widget _btnSection() {
return Container(
padding: EdgeInsets.only(top: 50.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildButtonColumn(Colors.green, Colors.greenAccent, Icons.play_arrow,
'PLAY', _speak),
_buildButtonColumn(
Colors.red, Colors.redAccent, Icons.stop, 'STOP', _stop),
_buildButtonColumn(
Colors.blue, Colors.blueAccent, Icons.pause, 'PAUSE', _pause),
],
),
);
}
Widget _enginesDropDownSection(List<dynamic> engines) => Container(
padding: EdgeInsets.only(top: 50.0),
child: DropdownButton(
value: engine,
items: getEnginesDropDownMenuItems(engines),
onChanged: changedEnginesDropDownItem,
),
);
Widget _languageDropDownSection(List<dynamic> languages) => Container(
padding: EdgeInsets.only(top: 10.0),
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
DropdownButton(
value: language,
items: getLanguageDropDownMenuItems(languages),
onChanged: changedLanguageDropDownItem,
),
Visibility(
visible: isAndroid,
child: Text("Is installed: $isCurrentLanguageInstalled"),
),
]));
Column _buildButtonColumn(Color color, Color splashColor, IconData icon,
String label, Function func) {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(icon),
color: color,
splashColor: splashColor,
onPressed: () => func()),
Container(
margin: const EdgeInsets.only(top: 8.0),
child: Text(label,
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w400,
color: color)))
]);
}
Widget _getMaxSpeechInputLengthSection() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
child: Text('Get max speech input length'),
onPressed: () async {
_inputLength = await flutterTts.getMaxSpeechInputLength;
setState(() {});
},
),
Text("$_inputLength characters"),
],
);
}
Widget _buildSliders() {
return Column(
children: [_volume(), _pitch(), _rate()],
);
}
Widget _volume() {
return Slider(
value: volume,
onChanged: (newVolume) {
setState(() => volume = newVolume);
},
min: 0.0,
max: 1.0,
divisions: 10,
label: "Volume: ${volume.toStringAsFixed(1)}");
}
Widget _pitch() {
return Slider(
value: pitch,
onChanged: (newPitch) {
setState(() => pitch = newPitch);
},
min: 0.5,
max: 2.0,
divisions: 15,
label: "Pitch: ${pitch.toStringAsFixed(1)}",
activeColor: Colors.red,
);
}
Widget _rate() {
return Slider(
value: rate,
onChanged: (newRate) {
setState(() => rate = newRate);
},
min: 0.0,
max: 1.0,
divisions: 10,
label: "Rate: ${rate.toStringAsFixed(1)}",
activeColor: Colors.green,
);
}
}
这个示例展示了如何初始化 FlutterTts
实例,配置语言、引擎、音量、音调和语速等参数,并通过按钮控制发音、暂停和停止等功能。此外,还展示了如何获取最大输入长度以及监听发音状态的变化。
希望这些信息对你有所帮助!如果你有任何其他问题或需要进一步的帮助,请随时提问。
更多关于Flutter文本转语音插件flutter_tts的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter文本转语音插件flutter_tts的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter项目中使用flutter_tts
插件来实现文本转语音功能的代码示例。这个示例将展示如何初始化TTS引擎、设置语言、播放文本以及处理播放完成事件。
首先,确保你已经在pubspec.yaml
文件中添加了flutter_tts
依赖:
dependencies:
flutter:
sdk: flutter
flutter_tts: ^3.3.3 # 请检查最新版本号
然后,运行flutter pub get
来安装依赖。
接下来,在你的Flutter项目中创建一个新的Dart文件(例如tts_screen.dart
),并添加以下代码:
import 'package:flutter/material.dart';
import 'package:flutter_tts/flutter_tts.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: TtsScreen(),
);
}
}
class TtsScreen extends StatefulWidget {
@override
_TtsScreenState createState() => _TtsScreenState();
}
class _TtsScreenState extends State<TtsScreen> {
final FlutterTts flutterTts = FlutterTts();
bool isPlaying = false;
String languageCode = "en-US"; // 默认语言代码
@override
void initState() {
super.initState();
_initTts();
}
Future<void> _initTts() async {
bool isAvailable = await flutterTts.isLanguageAvailable(languageCode);
setState(() {
if (isAvailable) {
flutterTts.setLanguage(languageCode);
flutterTts.setPitch(1.0);
flutterTts.setRate(0.5);
flutterTts.setVolume(1.0);
} else {
languageCode = "en-US"; // 如果指定语言不可用,则使用英语
flutterTts.setLanguage(languageCode);
}
});
flutterTts.setCompletionHandler(() {
setState(() {
isPlaying = false;
});
});
flutterTts.setErrorHandler((msg) {
print("Error: $msg");
});
}
Future<void> _speak(String text) async {
if (!isPlaying) {
setState(() {
isPlaying = true;
});
await flutterTts.speak(text);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Flutter TTS Example"),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
decoration: InputDecoration(
labelText: "Enter text to speak",
),
onSubmitted: (text) {
_speak(text);
},
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
_speak("Hello, this is a text to speech example.");
},
child: Text("Speak"),
),
SizedBox(height: 20),
Text(
"Is Playing: $isPlaying",
style: TextStyle(fontSize: 18),
),
],
),
),
);
}
}
这个示例代码创建了一个简单的Flutter应用,其中包含一个文本输入框和一个按钮。用户可以输入文本并点击按钮来播放该文本,或者通过提交文本输入框中的文本来直接播放。
代码解释
- 依赖导入:导入
flutter/material.dart
和flutter_tts/flutter_tts.dart
。 - 主应用:
MyApp
是一个无状态组件,它设置了应用的根。 - TtsScreen:这是一个有状态组件,包含TTS逻辑。
- 初始化TTS:在
initState
方法中,检查并设置默认语言,并设置完成和错误处理回调。 - 播放文本:
_speak
方法用于播放传入的文本字符串。
- 初始化TTS:在
- UI:包含一个文本输入框、一个按钮和一个显示播放状态的文本。
运行这个应用,你应该能够输入文本并点击按钮来听到文本转语音的输出。