class TextToSpeechDemo extends StatefulWidget {
@override
_TextToSpeechDemoState createState() => _TextToSpeechDemoState();
}
class _TextToSpeechDemoState extends State<TextToSpeechDemo> {
final _controller = TextEditingController();
final _player = AudioPlayer();
bool _isSpeaking = false;
bool _isLoaded = false;
@override
void initState() {
super.initState();
_initTTS();
}
Future<void> _initTTS() async {
// Download if needed
final models = await RunAnywhere.availableModels();
final ttsModel = models.firstWhere(
(m) => m.id == 'vits-piper-en_US-lessac-medium',
);
if (!ttsModel.isDownloaded) {
await for (final p in RunAnywhere.downloadModel(ttsModel.id)) {
if (p.state.isCompleted) break;
}
}
// Load voice
await RunAnywhere.loadTTSVoice('vits-piper-en_US-lessac-medium');
setState(() => _isLoaded = true);
}
Future<void> _speak() async {
if (_controller.text.isEmpty) return;
setState(() => _isSpeaking = true);
try {
final result = await RunAnywhere.synthesize(_controller.text);
// Play audio (implementation depends on your audio setup)
await _playAudio(result);
} finally {
setState(() => _isSpeaking = false);
}
}
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
controller: _controller,
decoration: InputDecoration(
hintText: 'Enter text to speak...',
),
maxLines: 3,
),
SizedBox(height: 16),
ElevatedButton.icon(
onPressed: _isLoaded && !_isSpeaking ? _speak : null,
icon: Icon(_isSpeaking ? Icons.stop : Icons.volume_up),
label: Text(_isSpeaking ? 'Speaking...' : 'Speak'),
),
],
);
}
@override
void dispose() {
_controller.dispose();
_player.dispose();
super.dispose();
}
}