flutter_baidu_speech_tts 1.0.4
flutter_baidu_speech_tts: ^1.0.4 copied to clipboard
Baidu TTS plugin for Flutter: online, offline and mixed speech synthesis on Android, iOS and HarmonyOS.
example/lib/main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_baidu_speech_tts/flutter_baidu_speech_tts.dart';
import 'utils/tts_config.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final FlutterBaiduTts _tts = FlutterBaiduTts();
final BaiduTtsHighlightController _highlight = BaiduTtsHighlightController();
/// 高光直接画在这个输入框里,没有额外的展示区。
late final BaiduTtsHighlightTextEditingController _textController =
BaiduTtsHighlightTextEditingController(
highlight: _highlight,
text: 'Hello, this is Flutter Baidu TTS.',
);
StreamSubscription<BaiduTtsEvent>? _eventSubscription;
final List<String> _logs = <String>[];
String _cuid = ''; // 指纹信息
@override
void initState() {
super.initState();
_highlight.addListener(_onHighlightChanged);
_refreshCuid();
}
@override
void dispose() {
_highlight.removeListener(_onHighlightChanged);
// 输入框监听着 _highlight,先拆掉它再释放 controller。
_textController.dispose();
_highlight.dispose();
_eventSubscription?.cancel();
super.dispose();
}
void _onHighlightChanged() {
if (mounted) {
setState(() {});
}
}
Future<void> _appendLog(String message) async {
if (!mounted) {
return;
}
setState(() {
_logs.insert(
0,
'${DateTime.now().toIso8601String().substring(11, 19)} $message',
);
if (_logs.length > 50) {
_logs.removeLast();
}
});
}
Future<void> _refreshCuid() async {
try {
final String? cuid = await _tts.getCuid();
if (!mounted) return;
setState(() {
_cuid = cuid ?? '';
});
} catch (e) {
if (!mounted) return;
setState(() {
_cuid = '';
});
await _appendLog('getCuid error => $e');
}
}
Future<void> _ensureEventSubscription() async {
if (_eventSubscription != null) {
return;
}
_eventSubscription = _tts.typedEvents.listen(
(BaiduTtsEvent event) {
_appendLog('event => ${event.event ?? event.message} | ${event.raw}');
},
onError: (Object error) {
_appendLog('event error => $error');
},
);
}
Future<void> _runAction(
String label,
Future<BaiduTtsResult> Function() action,
) async {
final result = await action();
await _appendLog('$label => $result');
if (!mounted) {
return;
}
}
Future<void> _initialize() async {
await _ensureEventSubscription();
await _runAction(
'initialize',
() => _tts.initializeWithConfig(TtsConfig.buildInitConfig()),
);
// cuid 通常在 SDK 初始化后才可用,初始化完成后再刷新一次。
await _refreshCuid();
}
Future<void> _speak() async {
await _runAction(
'speak',
() => _tts.speakText(_textController.text.trim(),
mode: BaiduTtsMode.offline),
);
}
Future<void> _synthesize() async {
await _runAction(
'synthesize',
() => _tts.synthesizeText(
_textController.text.trim(),
),
);
}
Future<void> _speakWithHighlight() async {
await _ensureEventSubscription();
// 不 trim:高光下标要和输入框里的内容严格对齐。
await _runAction(
'speak(highlight)',
// 和上面的 speak 按钮用同一个 mode,否则一个走离线引擎、一个走在线引擎,
// 发音人不同,听起来就像两个人在念。
() => _highlight.speak(_textController.text, mode: BaiduTtsMode.online),
);
}
Future<void> _pause() async {
// 高光在播时走 controller,它需要同步暂停自己的时钟。
if (_highlight.isSpeaking) {
await _runAction('pause', _highlight.pause);
return;
}
await _runAction('pause', _tts.pauseTts);
}
Future<void> _resume() async {
if (_highlight.isSpeaking) {
await _runAction('resume', _highlight.resume);
return;
}
await _runAction('resume', _tts.resumeTts);
}
Future<void> _stop() async {
if (_highlight.isSpeaking) {
await _runAction('stop', _highlight.stop);
return;
}
await _runAction('stop', _tts.stopTts);
}
Future<void> _release() async {
await _highlight.stop();
await _runAction('release', _tts.releaseTts);
}
Widget _button(IconData icon, String label, VoidCallback onPressed) {
return SizedBox(
width: 160,
child: ElevatedButton.icon(
onPressed: onPressed,
icon: Icon(icon, size: 18),
label: Text(label),
),
);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: true,
home: Scaffold(
appBar: AppBar(
title: const Text('Baidu TTS Flutter Demo'),
),
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Text(
'设备cuid:${_cuid.isEmpty ? '(未获取,请先 Initialize)' : _cuid}',
),
),
const SizedBox(width: 8),
IconButton(
tooltip: '刷新 cuid',
icon: const Icon(Icons.refresh, size: 18),
onPressed: _refreshCuid,
),
],
),
TextField(
controller: _textController,
maxLines: 4,
decoration: InputDecoration(
labelText: 'Text',
border: const OutlineInputBorder(),
helperText: _highlight.text.isEmpty
? '点击 Speak+Highlight,高光会直接跟在这个输入框里'
: '高光进度 ${(_highlight.progress * 100).toStringAsFixed(0)}%'
'${_highlight.hasRealPlaybackPosition ? ' · 真实播放位置' : ' · 估算'}',
),
),
const SizedBox(height: 16),
Wrap(
spacing: 8,
runSpacing: 8,
children: <Widget>[
_button(Icons.tune, 'initialize', _initialize),
_button(Icons.volume_up, 'speak', _speak),
_button(Icons.format_color_text, 'speak高光',
_speakWithHighlight),
_button(Icons.graphic_eq, 'Synthesize', _synthesize),
_button(Icons.pause, 'pause', _pause),
_button(Icons.refresh, 'resume', _resume),
_button(Icons.stop, 'stop', _stop),
_button(Icons.delete_outline, 'release', _release),
],
),
const SizedBox(height: 16),
Row(
children: <Widget>[
Text('Event log',
style: Theme.of(context).textTheme.titleMedium),
const Spacer(),
IconButton(
tooltip: '清空日志',
icon: const Icon(Icons.delete_sweep, size: 20),
onPressed: () => setState(() => _logs.clear()),
),
],
),
const SizedBox(height: 8),
Container(
constraints: const BoxConstraints(minHeight: 180),
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
border: Border.all(color: Colors.black26),
borderRadius: BorderRadius.circular(6),
),
child: SelectableText(
_logs.isEmpty ? 'No events yet.' : _logs.join('\n'),
),
),
],
),
),
),
),
);
}
}