Chat Bot Completed

a5_chatbot
aamir-csol 2 weeks ago
parent 234dce65e5
commit f253a5e99e

@ -31,6 +31,7 @@ class _ChatBotScreenState extends State<ChatBotScreen> {
final TextEditingController _messageController = TextEditingController(); final TextEditingController _messageController = TextEditingController();
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
AtlasInitiateChatResponse? atlasChatTokenResponse; AtlasInitiateChatResponse? atlasChatTokenResponse;
bool _isThinking = false;
// Speech to text // Speech to text
final SpeechToText _speechToText = SpeechToText(); final SpeechToText _speechToText = SpeechToText();
@ -40,7 +41,6 @@ class _ChatBotScreenState extends State<ChatBotScreen> {
void initState() { void initState() {
// TODO: implement initState // TODO: implement initState
super.initState(); super.initState();
print("Chat Bot Launched");
getChatSession(); getChatSession();
_initSpeech(); _initSpeech();
} }
@ -76,7 +76,9 @@ class _ChatBotScreenState extends State<ChatBotScreen> {
try { try {
GenericResponseModel? res = await DashboardApiClient().getChatBotSession(); GenericResponseModel? res = await DashboardApiClient().getChatBotSession();
if (res != null) { if (res != null) {
atlasChatTokenResponse = res.atlasInitiateChatResponse; setState(() {
atlasChatTokenResponse = res.atlasInitiateChatResponse;
});
} }
} catch (ex) { } catch (ex) {
Utils.handleException(ex, context, null); Utils.handleException(ex, context, null);
@ -92,37 +94,23 @@ class _ChatBotScreenState extends State<ChatBotScreen> {
super.dispose(); super.dispose();
} }
void _sendMessage() async { void _sendMessage({String? suggestionText}) async {
String text = _messageController.text.trim(); String text = suggestionText ?? _messageController.text.trim();
if (text.isEmpty || atlasChatTokenResponse == null) return; if (text.isEmpty || atlasChatTokenResponse == null) return;
// setState(() {
// _messages.add(ChatMessage(text: text, isUser: true, timestamp: DateTime.now()));
// _currentMode = ChatMode.chatActive;
// _messageController.clear();
// });
//
// // Simulate bot response
// Future.delayed(const Duration(milliseconds: 500), () {
// if (mounted) {
// setState(() {
// _messages.add(ChatMessage(text: "Hello there! How may I assist you today?", isUser: false, timestamp: DateTime.now()));
// });
// _scrollToBottom();
// }
// });
try { try {
setState(() { setState(() {
_messages.add(ChatMessage(text: text, isUser: true, timestamp: DateTime.now())); _messages.add(ChatMessage(text: text, isUser: true, timestamp: DateTime.now()));
_currentMode = ChatMode.chatActive; _currentMode = ChatMode.chatActive;
_messageController.clear(); _messageController.clear();
_isThinking = true;
}); });
_scrollToBottom();
GenericResponseModel? res = await DashboardApiClient().sendChatBotMessage(atlasChatTokenResponse: atlasChatTokenResponse!, atlasChatText: text); GenericResponseModel? res = await DashboardApiClient().sendChatBotMessage(atlasChatTokenResponse: atlasChatTokenResponse!, atlasChatText: text);
if (res != null) { if (res != null) {
res.atlasContinueChatResponseDetails; res.atlasContinueChatResponseDetails;
if (mounted) { if (mounted) {
setState(() { setState(() {
_isThinking = false;
_messages.add( _messages.add(
ChatMessage( ChatMessage(
text: res.atlasContinueChatResponseDetails!.aiResponse!.structuredData!.answer ?? "", text: res.atlasContinueChatResponseDetails!.aiResponse!.structuredData!.answer ?? "",
@ -135,6 +123,9 @@ class _ChatBotScreenState extends State<ChatBotScreen> {
_scrollToBottom(); _scrollToBottom();
} }
} catch (ex) { } catch (ex) {
setState(() {
_isThinking = false;
});
Utils.handleException(ex, context, null); Utils.handleException(ex, context, null);
} }
@ -150,8 +141,11 @@ class _ChatBotScreenState extends State<ChatBotScreen> {
} }
void _sendSuggestion(String text) { void _sendSuggestion(String text) {
_messageController.text = text; if (atlasChatTokenResponse == null) {
_sendMessage(); Utils.showToast("Please wait, initializing chat...");
return;
}
_sendMessage(suggestionText: text);
} }
@override @override
@ -199,8 +193,11 @@ class _ChatBotScreenState extends State<ChatBotScreen> {
return ListView.separated( return ListView.separated(
controller: _scrollController, controller: _scrollController,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
itemCount: _messages.length, itemCount: _messages.length + (_isThinking ? 1 : 0),
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
if (index == _messages.length && _isThinking) {
return _buildThinkingIndicator();
}
ChatMessage message = _messages[index]; ChatMessage message = _messages[index];
return _buildMessageBubble(message); return _buildMessageBubble(message);
}, },
@ -210,9 +207,28 @@ class _ChatBotScreenState extends State<ChatBotScreen> {
); );
} }
Widget _buildThinkingIndicator() {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
gradient: const LinearGradient(colors: <Color>[Color(0xFF32D892), Color(0xFF259CB8)], begin: Alignment.centerLeft, end: Alignment.centerRight),
borderRadius: BorderRadius.circular(15),
boxShadow: <BoxShadow>[BoxShadow(color: const Color(0xFF000000).withValues(alpha: 0.0784), offset: const Offset(0, 0), blurRadius: 13, spreadRadius: 0)],
),
child: const ThinkingDotsAnimation(),
),
const Spacer(flex: 2),
],
);
}
Widget _buildMessageBubble(ChatMessage message) { Widget _buildMessageBubble(ChatMessage message) {
bool isUser = message.isUser; bool isUser = message.isUser;
return Row( Widget bubbleContent = Row(
mainAxisAlignment: isUser ? MainAxisAlignment.end : MainAxisAlignment.start, mainAxisAlignment: isUser ? MainAxisAlignment.end : MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
@ -251,6 +267,11 @@ class _ChatBotScreenState extends State<ChatBotScreen> {
if (!isUser) const Spacer(flex: 2), if (!isUser) const Spacer(flex: 2),
], ],
); );
if (!isUser) {
return AnimatedMessageBubble(child: bubbleContent);
}
return bubbleContent;
} }
String _formatTimestamp(DateTime timestamp) { String _formatTimestamp(DateTime timestamp) {
@ -300,21 +321,25 @@ class _ChatBotScreenState extends State<ChatBotScreen> {
Expanded( Expanded(
child: TextField( child: TextField(
controller: _messageController, controller: _messageController,
onSubmitted: (_) => _sendMessage(), onSubmitted: _isThinking ? null : (_) => _sendMessage(),
enabled: !_isThinking,
maxLines: 1, maxLines: 1,
textAlignVertical: TextAlignVertical.center, textAlignVertical: TextAlignVertical.center,
decoration: const InputDecoration( decoration: InputDecoration(
hintText: 'Type a message..', hintText: _isThinking ? 'Waiting for response...' : 'Type a message..',
hintStyle: TextStyle(color: MyColors.hintTextColor, fontSize: 14, fontWeight: FontWeight.normal), hintStyle: const TextStyle(color: MyColors.hintTextColor, fontSize: 14, fontWeight: FontWeight.normal),
border: InputBorder.none, border: InputBorder.none,
isDense: true, isDense: true,
contentPadding: EdgeInsets.symmetric(vertical: 10), contentPadding: const EdgeInsets.symmetric(vertical: 10),
), ),
), ),
), ),
GestureDetector( GestureDetector(
onTap: _speechEnabled ? (_speechToText.isNotListening ? _startListening : _stopListening) : null, onTap: _isThinking ? null : (_speechEnabled ? (_speechToText.isNotListening ? _startListening : _stopListening) : null),
child: SvgPicture.asset("assets/icons/microphone.svg", colorFilter: ColorFilter.mode(_speechToText.isListening ? Colors.red : MyColors.darkTextColor, BlendMode.srcIn)), child: SvgPicture.asset(
"assets/icons/microphone.svg",
colorFilter: ColorFilter.mode(_isThinking ? MyColors.hintTextColor : (_speechToText.isListening ? Colors.red : MyColors.darkTextColor), BlendMode.srcIn),
),
), ),
], ],
), ),
@ -323,8 +348,13 @@ class _ChatBotScreenState extends State<ChatBotScreen> {
const SizedBox(width: 6), const SizedBox(width: 6),
// Send button // Send button
GestureDetector( GestureDetector(
onTap: _sendMessage, onTap: _isThinking ? null : _sendMessage,
child: Container(width: 48, height: 48, decoration: const BoxDecoration(color: MyColors.white, shape: BoxShape.circle), child: Center(child: SvgPicture.asset("assets/icons/send.svg"))), child: Container(
width: 48,
height: 48,
decoration: BoxDecoration(color: _isThinking ? MyColors.hintTextColor.withValues(alpha: 0.3) : MyColors.white, shape: BoxShape.circle),
child: Center(child: SvgPicture.asset("assets/icons/send.svg", colorFilter: _isThinking ? const ColorFilter.mode(MyColors.hintTextColor, BlendMode.srcIn) : null)),
),
), ),
], ],
), ),
@ -332,3 +362,89 @@ class _ChatBotScreenState extends State<ChatBotScreen> {
).paddingOnly(left: 16, right: 16, bottom: 16); ).paddingOnly(left: 16, right: 16, bottom: 16);
} }
} }
class ThinkingDotsAnimation extends StatefulWidget {
const ThinkingDotsAnimation({Key? key}) : super(key: key);
@override
State<ThinkingDotsAnimation> createState() => _ThinkingDotsAnimationState();
}
class _ThinkingDotsAnimationState extends State<ThinkingDotsAnimation> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 1200))..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text("Thinking", style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Colors.white)),
const SizedBox(width: 2),
AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget? child) {
int dotCount = (_controller.value * 4).floor() % 4;
String dots = '.' * dotCount;
return SizedBox(width: 18, child: Text(dots, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Colors.white)));
},
),
],
);
}
}
class AnimatedMessageBubble extends StatefulWidget {
final Widget child;
const AnimatedMessageBubble({Key? key, required this.child}) : super(key: key);
@override
State<AnimatedMessageBubble> createState() => _AnimatedMessageBubbleState();
}
class _AnimatedMessageBubbleState extends State<AnimatedMessageBubble> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _fadeAnimation;
late Animation<Offset> _slideAnimation;
late Animation<double> _scaleAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 400));
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut));
_slideAnimation = Tween<Offset>(begin: const Offset(-0.3, 0.0), end: Offset.zero).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic));
_scaleAnimation = Tween<double>(begin: 0.8, end: 1.0).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOutBack));
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SlideTransition(
position: _slideAnimation,
child: FadeTransition(opacity: _fadeAnimation, child: ScaleTransition(scale: _scaleAnimation, alignment: Alignment.centerLeft, child: widget.child)),
);
}
}

Loading…
Cancel
Save