import 'package:flutter/material.dart'; import 'package:flutter_tts/flutter_tts.dart'; import '../theme/app_theme.dart'; const _ttsLangMap = { 'en': 'en-US', 'es': 'es-ES', 'fr': 'fr-FR', 'de': 'de-DE', 'it': 'it-IT', 'pt': 'pt-BR', 'ja': 'ja-JP', 'ko': 'ko-KR', 'zh': 'zh-CN', 'yue': 'zh-HK', 'ar': 'ar-SA', 'hi': 'hi-IN', 'kn': 'kn-IN', 'ml': 'ml-IN', 'vi': 'vi-VN', 'ru': 'ru-RU', }; String ttsLang(String code) => _ttsLangMap[code] ?? code; class TtsButton extends StatefulWidget { const TtsButton({super.key, required this.text, required this.lang, this.size = 36}); final String text; final String lang; final double size; @override State createState() => _TtsButtonState(); } class _TtsButtonState extends State { late final FlutterTts _tts; bool _speaking = false; bool _pressed = false; @override void initState() { super.initState(); _tts = FlutterTts(); _tts.setCompletionHandler(() { if (mounted) setState(() => _speaking = false); }); _tts.setCancelHandler(() { if (mounted) setState(() => _speaking = false); }); _tts.setErrorHandler((_) { if (mounted) setState(() => _speaking = false); }); } @override void dispose() { _tts.stop(); super.dispose(); } Future _toggle() async { if (_speaking) { await _tts.stop(); if (mounted) setState(() => _speaking = false); return; } await _tts.stop(); await _tts.setLanguage(ttsLang(widget.lang)); await _tts.setSpeechRate(0.45); setState(() => _speaking = true); final result = await _tts.speak(widget.text); if (result != 1 && mounted) setState(() => _speaking = false); } @override Widget build(BuildContext context) { final offset = _pressed ? 1.0 : 2.0; final fill = _speaking ? const Color(0xFFFF8080) : kBrutalYellow; return GestureDetector( onTapDown: (_) => setState(() => _pressed = true), onTapUp: (_) => setState(() => _pressed = false), onTapCancel: () => setState(() => _pressed = false), onTap: _toggle, child: AnimatedContainer( duration: const Duration(milliseconds: 80), width: widget.size, height: widget.size, transform: Matrix4.translationValues(_pressed ? 1 : 0, _pressed ? 1 : 0, 0), decoration: BoxDecoration( color: fill, borderRadius: BorderRadius.circular(8), border: Border.all(color: kBrutalBlack, width: 2), boxShadow: [ BoxShadow(color: kBrutalBlack, offset: Offset(offset, offset), blurRadius: 0), ], ), child: Icon( _speaking ? Icons.stop_rounded : Icons.volume_up_rounded, color: kBrutalBlack, size: widget.size * 0.55, ), ), ); } }