QOL: Mzansi AI Chat Look and Feel pt1

This commit is contained in:
2025-11-07 12:03:44 +02:00
parent 6a5b4f7f4b
commit d75da5389a
15 changed files with 1198 additions and 61 deletions

View File

@@ -598,9 +598,6 @@ class _AiChatState extends State<AiChat> {
_voicesString =
_voices.map((_voice) => _voice["name"] as String).toList();
_voicesString.sort();
// print(
// "=================== Voices ===================\n$_voicesString");
setTtsVoice(_voicesString.first);
});
} catch (e) {

View File

@@ -0,0 +1,286 @@
import 'package:flutter/material.dart';
import 'package:flutter_ai_toolkit/flutter_ai_toolkit.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:flutter_tts/flutter_tts.dart';
import 'package:ken_logger/ken_logger.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_floating_menu.dart';
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_icons.dart';
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mzansi_ai_provider.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import 'package:provider/provider.dart';
class MihAiChat extends StatefulWidget {
const MihAiChat({super.key});
@override
State<MihAiChat> createState() => _MihAiChatState();
}
class _MihAiChatState extends State<MihAiChat> {
bool ttsOn = false;
final FlutterTts _flutterTts = FlutterTts();
Widget noMessagescDisplay() {
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 50),
Icon(
MihIcons.mzansiAi,
size: 165,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
const SizedBox(height: 10),
Text(
"Mzansi AI is here to help",
textAlign: TextAlign.center,
overflow: TextOverflow.visible,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
),
const SizedBox(height: 25),
Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.normal,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
children: [
TextSpan(
text:
"Send us a message and we'll try our best to assist you"),
],
),
),
),
Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.normal,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
children: [
TextSpan(text: "Press "),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(
Icons.menu,
size: 20,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
),
TextSpan(text: " to start a new chat or read last message"),
],
),
),
),
],
),
),
);
}
void resetChat(MzansiAiProvider aiProvider) {
aiProvider.ollamaProvider.resetChat();
}
void speakLastMessage(MzansiAiProvider aiProvider) {
final history = aiProvider.ollamaProvider.history;
if (history.isNotEmpty) {
final historyList = history.toList();
for (int i = historyList.length - 1; i >= 0; i--) {
if (historyList[i].origin == MessageOrigin.llm &&
historyList[i].text != null &&
historyList[i].text!.isNotEmpty) {
_flutterTts.speak(historyList[i].text!);
return;
}
}
}
}
void stopTTS() {
_flutterTts.stop();
}
Future<void> initTts() async {
List<Map> _voices = [];
List<String> _voicesString = [];
// await _flutterTts.setLanguage("en-US");
await _flutterTts.setSpeechRate(1);
_flutterTts.getVoices.then(
(data) {
try {
_voices = List<Map>.from(data);
setState(() {
_voices = _voices
.where(
(_voice) => _voice["name"].toLowerCase().contains("en-us"))
.toList();
_voicesString =
_voices.map((_voice) => _voice["name"] as String).toList();
_voicesString.sort();
_flutterTts.setVoice(
{
"name": _voicesString.first,
"locale": _voices
.where((_voice) =>
_voice["name"].contains(_voicesString.first))
.first["locale"]
},
);
});
} catch (e) {
print(e);
}
},
);
_flutterTts.setStartHandler(() {
setState(() {
ttsOn = true;
});
});
_flutterTts.setCompletionHandler(() {
setState(() {
ttsOn = false;
});
});
_flutterTts.setErrorHandler((message) {
setState(() {
ttsOn = false;
});
});
}
@override
void initState() {
super.initState();
initTts();
}
@override
void dispose() {
_flutterTts.stop();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Consumer<MzansiAiProvider>(
builder: (BuildContext context, MzansiAiProvider mzansiAiProvider,
Widget? child) {
bool hasHistory = mzansiAiProvider.ollamaProvider.history.isNotEmpty;
KenLogger.success("has history: $hasHistory");
KenLogger.success(
"length: ${mzansiAiProvider.ollamaProvider.history.length}");
return Stack(
children: [
LlmChatView(
provider: mzansiAiProvider.ollamaProvider,
// welcomeMessage:
// "Mzansi AI is here to help. Send us a messahe and we'll try our best to assist you.",
enableAttachments: false,
enableVoiceNotes: true,
style: mzansiAiProvider.getChatStyle(context),
// suggestions: [
// "What is mih all about?",
// "What are the features of MIH?"
// ],
),
if (hasHistory)
Positioned(
right: 10,
bottom: 80,
child: MihFloatingMenu(
animatedIcon: AnimatedIcons.menu_close,
children: [
SpeedDialChild(
child: Icon(
Icons.refresh,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
label: "New Chat",
labelBackgroundColor: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
labelStyle: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
),
backgroundColor: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
onTap: () {
resetChat(mzansiAiProvider);
},
),
SpeedDialChild(
child: Icon(
!ttsOn ? Icons.volume_up : Icons.volume_off,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
label: !ttsOn ? "Read last Message" : "Stop Reading",
labelBackgroundColor: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
labelStyle: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
),
backgroundColor: !ttsOn
? MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark")
: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
onTap: () {
KenLogger.success("Button pressed: TtsOn - $ttsOn");
if (!ttsOn) {
speakLastMessage(mzansiAiProvider);
} else {
stopTTS();
}
},
),
],
),
),
if (!hasHistory) noMessagescDisplay(),
],
);
},
);
}
}