diff --git a/Frontend/lib/mih_packages/mzansi_ai/ai_chat.dart b/Frontend/lib/mih_packages/mzansi_ai/ai_chat.dart index 190eb7ee..5f964760 100644 --- a/Frontend/lib/mih_packages/mzansi_ai/ai_chat.dart +++ b/Frontend/lib/mih_packages/mzansi_ai/ai_chat.dart @@ -31,9 +31,11 @@ class AiChat extends StatefulWidget { class _AiChatState extends State { final TextEditingController _modelController = TextEditingController(); final TextEditingController _fontSizeController = TextEditingController(); - final TextEditingController _ttsController = TextEditingController(); + final TextEditingController _ttsVoiceController = TextEditingController(); final ValueNotifier _showModelOptions = ValueNotifier(false); FlutterTts _flutterTts = FlutterTts(); + final ValueNotifier _ttsVoiceName = ValueNotifier(""); + // bool _ttsOn = false; String? textStream; List _voices = []; Map? _currentVoice; @@ -138,8 +140,9 @@ class _AiChatState extends State { child: IconButton( color: MzanziInnovationHub.of(context)!.theme.primaryColor(), - onPressed: () { + onPressed: () async { print("Start TTS now"); + _speakText(snapshot.requireData); }, icon: const Icon(Icons.volume_up), @@ -351,7 +354,7 @@ class _AiChatState extends State { alignment: Alignment.centerLeft, child: FittedBox( child: Container( - padding: const EdgeInsets.only(top: 5.0), + padding: const EdgeInsets.all(10.0), decoration: BoxDecoration( color: MzanziInnovationHub.of(context)!.theme.primaryColor(), @@ -385,21 +388,18 @@ class _AiChatState extends State { Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 25), - child: SizedBox( - width: 300, - child: MIHDropdownField( - controller: _modelController, - hintText: "AI Model", - dropdownOptions: const [ - 'deepseek-r1:1.5b', - 'gemma2:2b' - ], - required: true, - editable: true, - enableSearch: false, - ), + SizedBox( + width: 300, + child: MIHDropdownField( + controller: _modelController, + hintText: "AI Model", + dropdownOptions: const [ + 'deepseek-r1:1.5b', + 'gemma2:2b' + ], + required: true, + editable: true, + enableSearch: false, ), ), ], @@ -445,29 +445,53 @@ class _AiChatState extends State { ), ], ), - const SizedBox(height: 10), + const SizedBox(height: 15), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ + SizedBox( + width: 230, + child: MIHDropdownField( + controller: _ttsVoiceController, + hintText: "AI Voice", + dropdownOptions: _voices + .map((_voice) => _voice["name"] as String) + .toList(), + required: true, + editable: true, + enableSearch: false, + ), + ), + const SizedBox(width: 10), Padding( - padding: const EdgeInsets.symmetric(horizontal: 25), - child: SizedBox( - width: 300, - child: MIHDropdownField( - controller: _ttsController, - hintText: "AI Voice", - dropdownOptions: _voices - .map((_voice) => _voice["name"] as String) - .toList(), - required: true, - editable: true, - enableSearch: false, + padding: const EdgeInsets.all(5.0), + child: Container( + //color: MzanziInnovationHub.of(context)!.theme.successColor(), + decoration: BoxDecoration( + color: MzanziInnovationHub.of(context)! + .theme + .successColor(), + borderRadius: const BorderRadius.all( + Radius.circular(100), + ), + ), + child: IconButton( + color: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + onPressed: () { + print("Start TTS now"); + + _speakText( + "This is the sample of the Mzansi A.I Voice."); + }, + icon: const Icon(Icons.volume_up), ), ), ), ], ), - const SizedBox(height: 10), + const SizedBox(height: 15), ], ), ), @@ -488,7 +512,7 @@ class _AiChatState extends State { .first["locale"] }, ); - _ttsController.text = _currentVoice!["name"]; + _ttsVoiceController.text = _currentVoice!["name"]; } void _speakText(String text) async { @@ -500,13 +524,25 @@ class _AiChatState extends State { } } + void voiceSelected() { + if (_ttsVoiceController.text.isNotEmpty) { + _ttsVoiceName.value = _ttsVoiceController.text; + print( + "======================================== Voice Set ========================================"); + setTtsVoice(_ttsVoiceController.text); + } else { + _ttsVoiceName.value = ""; + } + } + @override void dispose() { // TODO: implement dispose super.dispose(); _modelController.dispose(); _fontSizeController.dispose(); - _ttsController.dispose(); + _ttsVoiceController.dispose(); + _ttsVoiceController.removeListener(voiceSelected); client.endSession(); _flutterTts.stop(); } @@ -521,7 +557,7 @@ class _AiChatState extends State { print("=================== Voices ===================\n$_voices"); setState(() { _voices = _voices - .where((_voice) => _voice["name"].contains("en")) + .where((_voice) => _voice["name"].contains("en-us")) .toList(); _currentVoice = _voices.first; setTtsVoice(_currentVoice!["name"]); @@ -546,6 +582,7 @@ class _AiChatState extends State { ); _modelController.text = 'gemma2:2b'; _fontSizeController.text = _chatFrontSize.ceil().toString(); + _ttsVoiceController.addListener(voiceSelected); _chatHistory.add( ollama.Message( role: ollama.MessageRole.system,