/* Right Vote - A web app for election prediction and manifesto comparison with machine learning and NLP. Nilakna Warushavithana, September 2024 */ import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; class ChatBotPanelGenerator extends StatefulWidget { const ChatBotPanelGenerator({super.key}); @override _ChatBotPanelState createState() => _ChatBotPanelState(); } class _ChatBotPanelState extends State { final TextEditingController _chatController = TextEditingController(); final ScrollController _scrollController = ScrollController(); List> _chatHistory = []; void getAnswer() async { final url = "https://85a9-35-196-250-8.ngrok-free.app/ask"; final uri = Uri.parse(url); try { final response = await http.post( uri, headers: {"Content-Type": "application/json"}, body: jsonEncode({ "question": _chatController.text }), //send the question to the server ); //for debugging the http request and reponse // print("Request Body: ${jsonEncode({ // 'question': _chatController.text // })}"); // print("Response Body: ${response.body}"); if (response.statusCode == 200) { final data = json.decode(response.body); setState(() { _chatHistory.add({ "time": DateTime.now(), "message": data["answer"], "isSender": false, }); //update chat history with the answer }); // Ensure scrolling happens after the state has been updated WidgetsBinding.instance.addPostFrameCallback((_) { if (_scrollController.hasClients) { _scrollController.animateTo( _scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 300), curve: Curves.easeOut, ); } }); } else { // print("Error: ${response.statusCode}"); //debugging // print(response.body); //debugging setState(() { _chatHistory.add({ "time": DateTime.now(), "message": "Error: ${response.statusCode}", "isSender": false, }); }); } } catch (e) { // print("Exception: $e"); //debugging setState(() { _chatHistory.add({ "time": DateTime.now(), "message": "Exception: $e", "isSender": false, }); }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text( "Chat", style: TextStyle(fontWeight: FontWeight.bold), ), ), body: Stack( children: [ Container( //get max height height: MediaQuery.of(context).size.height - 160, child: ListView.builder( itemCount: _chatHistory.length, // shrinkWrap: false, controller: _scrollController, padding: const EdgeInsets.only(top: 10, bottom: 10), physics: const BouncingScrollPhysics(), itemBuilder: (context, index) { return Container( padding: EdgeInsets.only(left: 14, right: 14, top: 10, bottom: 10), child: Align( alignment: (_chatHistory[index]["isSender"] ? Alignment.topRight : Alignment.topLeft), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.5), spreadRadius: 2, blurRadius: 5, offset: const Offset(0, 3), ), ], color: (_chatHistory[index]["isSender"] ? Colors.blue[100] : Colors.white), ), padding: EdgeInsets.all(16), child: Text(_chatHistory[index]["message"], style: TextStyle( fontSize: 15, color: _chatHistory[index]["isSender"] ? Colors.black : Colors.black)), ), ), ); }, ), ), Align( alignment: Alignment.bottomCenter, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), height: 60, width: double.infinity, color: Colors.white, child: Row( children: [ Expanded( child: Container( decoration: const BoxDecoration(), child: Padding( padding: const EdgeInsets.all(4.0), child: TextField( decoration: const InputDecoration( hintText: "Type a message", border: InputBorder.none, contentPadding: EdgeInsets.all(8.0), ), controller: _chatController, ), ), ), ), const SizedBox( width: 4.0, ), MaterialButton( onPressed: () { setState(() { if (_chatController.text.isNotEmpty) { _chatHistory.add({ "time": DateTime.now(), "message": _chatController.text, "isSender": true, }); getAnswer(); _chatController.clear(); } }); _scrollController.jumpTo( _scrollController.position.maxScrollExtent, ); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(80.0)), padding: const EdgeInsets.all(0.0), child: Ink( decoration: const BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.all(Radius.circular(50.0)), ), child: Container( constraints: const BoxConstraints( minWidth: 88.0, minHeight: 36.0), // min sizes for Material buttons alignment: Alignment.center, child: const Icon( Icons.send, color: Colors.white, )), ), ) ], ), ), ) ], ), ); } }