import 'package:flutter/material.dart'; import '../services/ai_recommendation_service.dart'; class AiChatScreen extends StatefulWidget { const AiChatScreen({super.key}); @override State createState() => _AiChatScreenState(); } class _AiChatScreenState extends State { final TextEditingController _controller = TextEditingController(); final ScrollController _scrollController = ScrollController(); final AiRecommendationService _service = AiRecommendationService(); final List<_ChatMessage> _messages = [ const _ChatMessage( text: 'Olá! Diz-me uma ocasião, destino ou plano e eu recomendo o que deves levar. Ex: "uma viagem para Itália de 5 dias".', isUser: false, ), ]; bool _isLoading = false; Future _sendMessage([String? suggestion]) async { final text = (suggestion ?? _controller.text).trim(); if (text.isEmpty || _isLoading) return; setState(() { _messages.add(_ChatMessage(text: text, isUser: true)); _isLoading = true; }); _controller.clear(); _scrollToBottom(); try { final response = await _service.recommendForOccasion(text); if (!mounted) return; setState(() { _messages.add(_ChatMessage(text: response, isUser: false)); }); } catch (e) { if (!mounted) return; setState(() { _messages.add( _ChatMessage( text: 'Não consegui gerar uma recomendação agora. Tenta novamente.', isUser: false, ), ); }); } finally { if (mounted) { setState(() => _isLoading = false); _scrollToBottom(); } } } void _scrollToBottom() { WidgetsBinding.instance.addPostFrameCallback((_) { if (!_scrollController.hasClients) return; _scrollController.animateTo( _scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 250), curve: Curves.easeOut, ); }); } @override void dispose() { _controller.dispose(); _scrollController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFFFE5CC), appBar: AppBar( backgroundColor: const Color(0xFF0066CC), elevation: 0, title: const Text( 'DayMaker IA', style: TextStyle(color: Colors.white, fontSize: 20), ), centerTitle: true, ), body: SafeArea( child: Column( children: [ _buildSuggestions(), Expanded( child: ListView.builder( controller: _scrollController, padding: const EdgeInsets.all(16), itemCount: _messages.length + (_isLoading ? 1 : 0), itemBuilder: (context, index) { if (_isLoading && index == _messages.length) { return const _TypingBubble(); } return _MessageBubble(message: _messages[index]); }, ), ), _buildInput(), ], ), ), ); } Widget _buildSuggestions() { final suggestions = [ 'Viagem para Itália', 'Fim de semana na praia', 'Reunião de trabalho', ]; return Container( width: double.infinity, padding: const EdgeInsets.fromLTRB(16, 12, 16, 8), color: const Color(0xFFFFE5CC), child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: suggestions.map((suggestion) { return Padding( padding: const EdgeInsets.only(right: 8), child: ActionChip( backgroundColor: Colors.white, side: const BorderSide(color: Color(0xFFE0E0E0)), label: Text(suggestion), onPressed: () => _sendMessage(suggestion), ), ); }).toList(), ), ), ); } Widget _buildInput() { return Container( padding: const EdgeInsets.fromLTRB(12, 8, 12, 12), decoration: const BoxDecoration(color: Colors.white), child: Row( children: [ Expanded( child: Container( decoration: BoxDecoration( color: const Color(0xFFF5F5F5), borderRadius: BorderRadius.circular(24), ), child: TextField( controller: _controller, minLines: 1, maxLines: 4, textInputAction: TextInputAction.send, onSubmitted: (_) => _sendMessage(), decoration: const InputDecoration( hintText: 'Ex: viagem para Itália de 5 dias...', border: InputBorder.none, contentPadding: EdgeInsets.symmetric( horizontal: 16, vertical: 12, ), ), ), ), ), const SizedBox(width: 8), SizedBox( width: 48, height: 48, child: ElevatedButton( onPressed: _isLoading ? null : () => _sendMessage(), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF0066CC), shape: const CircleBorder(), padding: EdgeInsets.zero, ), child: const Icon(Icons.send, color: Colors.white), ), ), ], ), ); } } class _ChatMessage { final String text; final bool isUser; const _ChatMessage({required this.text, required this.isUser}); } class _MessageBubble extends StatelessWidget { final _ChatMessage message; const _MessageBubble({required this.message}); @override Widget build(BuildContext context) { final alignment = message.isUser ? Alignment.centerRight : Alignment.centerLeft; final backgroundColor = message.isUser ? const Color(0xFF0066CC) : Colors.white; final textColor = message.isUser ? Colors.white : const Color(0xFF333333); return Align( alignment: alignment, child: Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width * 0.78, ), margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: backgroundColor, borderRadius: BorderRadius.circular(18), ), child: Text( message.text, style: TextStyle(color: textColor, fontSize: 15, height: 1.35), ), ), ); } } class _TypingBubble extends StatelessWidget { const _TypingBubble(); @override Widget build(BuildContext context) { return Align( alignment: Alignment.centerLeft, child: Container( margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(18), ), child: const SizedBox( width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2), ), ), ); } }