QOL: Mzansi AI Chat Look and Feel & Startup question pt4

This commit is contained in:
2025-11-10 13:35:04 +02:00
parent 01220144c3
commit f8de726959
2 changed files with 79 additions and 93 deletions

View File

@@ -8,12 +8,14 @@ import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_env.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_env.dart';
class MzansiAiProvider extends ChangeNotifier { class MzansiAiProvider extends ChangeNotifier {
bool ttsOn;
int toolIndex; int toolIndex;
String? startUpQuestion; String? startUpQuestion;
late OllamaProvider ollamaProvider; late OllamaProvider ollamaProvider;
MzansiAiProvider({ MzansiAiProvider({
this.toolIndex = 0, this.toolIndex = 0,
this.ttsOn = false,
}) { }) {
ollamaProvider = OllamaProvider( ollamaProvider = OllamaProvider(
baseUrl: "${AppEnviroment.baseAiUrl}/api", baseUrl: "${AppEnviroment.baseAiUrl}/api",
@@ -35,6 +37,7 @@ class MzansiAiProvider extends ChangeNotifier {
"- Mzansi Directory: A place to search and find out more about the people and businesses across Mzansi.\n" + "- Mzansi Directory: A place to search and find out more about the people and businesses across Mzansi.\n" +
"- Calendar: Integrated calendar for managing personal and business appointments.\n" + "- Calendar: Integrated calendar for managing personal and business appointments.\n" +
"- Calculator: Simple calculator with tip and forex calculation functionality.\n" + "- Calculator: Simple calculator with tip and forex calculation functionality.\n" +
"- MIH Minesweeper: The first game from MIH! It's the classic brain-teaser ready to entertain you no matter where you are.\n" +
"- MIH Access: Manage and view profile access security.\n" + "- MIH Access: Manage and view profile access security.\n" +
"**Core Rules and Guidelines:**\n" + "**Core Rules and Guidelines:**\n" +
"- **Accuracy First:** Always prioritize providing correct information.\n" + "- **Accuracy First:** Always prioritize providing correct information.\n" +
@@ -59,6 +62,11 @@ class MzansiAiProvider extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void setTTSstate(bool ttsOn) {
this.ttsOn = ttsOn;
notifyListeners();
}
void setStartUpQuestion(String? question) { void setStartUpQuestion(String? question) {
startUpQuestion = question; startUpQuestion = question;
notifyListeners(); notifyListeners();
@@ -244,6 +252,7 @@ class MzansiAiProvider extends ChangeNotifier {
], ],
), ),
textStyle: TextStyle( textStyle: TextStyle(
fontSize: 16,
color: MihColors.getPrimaryColor( color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"), MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
), ),

View File

@@ -4,6 +4,7 @@ import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:flutter_tts/flutter_tts.dart'; import 'package:flutter_tts/flutter_tts.dart';
import 'package:ken_logger/ken_logger.dart'; import 'package:ken_logger/ken_logger.dart';
import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_button.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_floating_menu.dart';
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_icons.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_components/mih_providers/mzansi_ai_provider.dart';
@@ -18,7 +19,6 @@ class MihAiChat extends StatefulWidget {
} }
class _MihAiChatState extends State<MihAiChat> { class _MihAiChatState extends State<MihAiChat> {
bool ttsOn = false;
final FlutterTts _flutterTts = FlutterTts(); final FlutterTts _flutterTts = FlutterTts();
Widget noMessagescDisplay() { Widget noMessagescDisplay() {
@@ -123,9 +123,9 @@ class _MihAiChatState extends State<MihAiChat> {
_flutterTts.stop(); _flutterTts.stop();
} }
Future<void> initTts() async { Future<void> initTts(MzansiAiProvider aiProvider) async {
try { try {
await _flutterTts.setSpeechRate(1); await _flutterTts.setSpeechRate(0.55);
// await _flutterTts.setLanguage("en-US"); // await _flutterTts.setLanguage("en-US");
// Safer voice selection with error handling // Safer voice selection with error handling
@@ -153,25 +153,19 @@ class _MihAiChatState extends State<MihAiChat> {
_flutterTts.setStartHandler(() { _flutterTts.setStartHandler(() {
if (mounted) { if (mounted) {
setState(() { aiProvider.setTTSstate(true);
ttsOn = true;
});
} }
}); });
_flutterTts.setCompletionHandler(() { _flutterTts.setCompletionHandler(() {
if (mounted) { if (mounted) {
setState(() { aiProvider.setTTSstate(false);
ttsOn = false;
});
} }
}); });
_flutterTts.setErrorHandler((message) { _flutterTts.setErrorHandler((message) {
if (mounted) { if (mounted) {
setState(() { aiProvider.setTTSstate(false);
ttsOn = false;
});
} }
}); });
} }
@@ -192,7 +186,8 @@ class _MihAiChatState extends State<MihAiChat> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
initTts(); MzansiAiProvider aiProvider = context.read<MzansiAiProvider>();
initTts(aiProvider);
initStartQuestion(); initStartQuestion();
} }
@@ -205,102 +200,84 @@ class _MihAiChatState extends State<MihAiChat> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Consumer<MzansiAiProvider>( return Consumer<MzansiAiProvider>(
builder: (BuildContext context, MzansiAiProvider mzansiAiProvider, builder:
Widget? child) { (BuildContext context, MzansiAiProvider aiProvider, Widget? child) {
// final startupQuestion = mzansiAiProvider.startUpQuestion; bool hasHistory = aiProvider.ollamaProvider.history.isNotEmpty;
// if (startupQuestion != null) {
// WidgetsBinding.instance.addPostFrameCallback((_) {
// mzansiAiProvider.ollamaProvider.sendMessageStream(startupQuestion);
// mzansiAiProvider.setStartUpQuestion(null);
// });
// }
bool hasHistory = mzansiAiProvider.ollamaProvider.history.isNotEmpty;
KenLogger.success("has history: $hasHistory");
KenLogger.success(
"length: ${mzansiAiProvider.ollamaProvider.history.length}");
return Stack( return Stack(
children: [ children: [
LlmChatView( LlmChatView(
provider: mzansiAiProvider.ollamaProvider, provider: aiProvider.ollamaProvider,
messageSender: mzansiAiProvider.ollamaProvider.sendMessageStream, messageSender: aiProvider.ollamaProvider.sendMessageStream,
// welcomeMessage: // welcomeMessage:
// "Mzansi AI is here to help. Send us a messahe and we'll try our best to assist you.", // "Mzansi AI is here to help. Send us a messahe and we'll try our best to assist you.",
autofocus: false, autofocus: false,
enableAttachments: false, enableAttachments: false,
enableVoiceNotes: false, enableVoiceNotes: false,
style: mzansiAiProvider.getChatStyle(context), style: aiProvider.getChatStyle(context),
// suggestions: [ suggestions: [
// "What is mih all about?", "What is mih all about?",
// "What are the features of MIH?" "What are the features of MIH?"
// ], ],
), ),
if (hasHistory) if (hasHistory)
Positioned( Positioned(
right: 10,
bottom: 80, bottom: 80,
child: MihFloatingMenu( left: 10,
animatedIcon: AnimatedIcons.menu_close, child: MihButton(
children: [ width: 35,
SpeedDialChild( height: 35,
child: Icon( onPressed: () {
Icons.refresh, if (!aiProvider.ttsOn) {
color: MihColors.getPrimaryColor( speakLastMessage(aiProvider);
MzansiInnovationHub.of(context)!.theme.mode == } else {
"Dark"), stopTTS();
), }
label: "New Chat", },
labelBackgroundColor: MihColors.getGreenColor( buttonColor: !aiProvider.ttsOn
? MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark")
: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode == MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"), "Dark"),
labelStyle: TextStyle( child: Icon(
color: MihColors.getPrimaryColor( !aiProvider.ttsOn ? Icons.volume_up : Icons.volume_off,
MzansiInnovationHub.of(context)!.theme.mode == color: MihColors.getPrimaryColor(
"Dark"), 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();
}
},
),
],
), ),
), ),
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(aiProvider);
},
),
],
),
),
if (!hasHistory) noMessagescDisplay(), if (!hasHistory) noMessagescDisplay(),
], ],
); );