import 'package:flutter/material.dart'; import '../services/ai_recommendation_service.dart'; import '../theme/app_theme.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, bool silent = false, bool hideUserMessage = false, }) async { final text = (suggestion ?? _controller.text).trim(); if (text.isEmpty || _isLoading) return; setState(() { if (!hideUserMessage) { _messages.add(_ChatMessage(text: text, isUser: true)); } _isLoading = true; }); _controller.clear(); _scrollToBottom(); try { final response = await _service.sendMessage(text, silent: silent); if (!mounted) return; setState(() { _messages.add(_ChatMessage(text: response, isUser: false)); }); } catch (e) { if (!mounted) return; setState(() { _messages.add( _ChatMessage( text: 'Nao consegui gerar uma recomendacao 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: AppColors.background, body: SafeArea( child: Column( children: [ _buildHeader(), _buildSuggestions(), Expanded( child: ListView.builder( controller: _scrollController, padding: const EdgeInsets.fromLTRB(16, 8, 16, 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 _buildHeader() { return Container( padding: const EdgeInsets.fromLTRB(20, 16, 20, 16), decoration: BoxDecoration( gradient: AppColors.brandGradient, boxShadow: AppShadows.brand, ), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.22), borderRadius: BorderRadius.circular(AppRadius.md), ), child: const Icon( Icons.auto_awesome_rounded, color: Colors.white, size: 24, ), ), const SizedBox(width: 12), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'DayMaker IA', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w700, ), ), SizedBox(height: 2), Text( 'Pergunta-me sobre o teu dia ou viagem', style: TextStyle(color: Colors.white70, fontSize: 12), ), ], ), ), ], ), ); } 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, 14, 16, 8), color: AppColors.background, child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: suggestions.map((suggestion) { return Padding( padding: const EdgeInsets.only(right: 8), child: AppChip( label: suggestion, icon: Icons.bolt_rounded, onTap: () => _sendMessage(suggestion: suggestion), ), ); }).toList(), ), ), ); } Widget _buildInput() { return Container( padding: const EdgeInsets.fromLTRB(12, 10, 12, 14), decoration: BoxDecoration( color: AppColors.surface, boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.04), blurRadius: 16, offset: const Offset(0, -4), ), ], ), child: Row( children: [ Expanded( child: Container( decoration: BoxDecoration( color: AppColors.surfaceAlt, borderRadius: BorderRadius.circular(AppRadius.pill), border: Border.all(color: AppColors.border), ), child: TextField( controller: _controller, minLines: 1, maxLines: 4, textInputAction: TextInputAction.send, onSubmitted: (_) => _sendMessage(), style: AppText.body, decoration: const InputDecoration( hintText: 'Escreve uma mensagem...', hintStyle: TextStyle(color: AppColors.textTertiary), border: InputBorder.none, contentPadding: EdgeInsets.symmetric( horizontal: 16, vertical: 12, ), ), ), ), ), const SizedBox(width: 10), Container( width: 48, height: 48, decoration: BoxDecoration( gradient: AppColors.brandGradient, shape: BoxShape.circle, boxShadow: AppShadows.brand, ), child: Material( color: Colors.transparent, shape: const CircleBorder(), child: InkWell( customBorder: const CircleBorder(), onTap: _isLoading ? null : () => _sendMessage(), child: const Icon( Icons.send_rounded, color: Colors.white, size: 22, ), ), ), ), ], ), ); } } 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 isUser = message.isUser; final alignment = isUser ? Alignment.centerRight : Alignment.centerLeft; 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.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( gradient: isUser ? AppColors.brandGradient : null, color: isUser ? null : AppColors.surface, borderRadius: BorderRadius.only( topLeft: const Radius.circular(AppRadius.lg), topRight: const Radius.circular(AppRadius.lg), bottomLeft: Radius.circular(isUser ? AppRadius.lg : 4), bottomRight: Radius.circular(isUser ? 4 : AppRadius.lg), ), boxShadow: isUser ? AppShadows.brand : AppShadows.soft, ), child: Text( message.text, style: TextStyle( color: isUser ? Colors.white : AppColors.textPrimary, fontSize: 15, height: 1.4, ), ), ), ); } } 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: 16, vertical: 14), decoration: BoxDecoration( color: AppColors.surface, borderRadius: const BorderRadius.only( topLeft: Radius.circular(AppRadius.lg), topRight: Radius.circular(AppRadius.lg), bottomLeft: Radius.circular(4), bottomRight: Radius.circular(AppRadius.lg), ), boxShadow: AppShadows.soft, ), child: const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2.2, valueColor: AlwaysStoppedAnimation(AppColors.primary), ), ), ), ); } }