import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../providers/auth_provider.dart'; import '../providers/chat_provider.dart'; import '../services/chat_service.dart'; import '../widgets/message_bubble.dart'; import '../widgets/chat_input.dart'; class ChatScreen extends StatefulWidget { const ChatScreen({super.key}); @override State createState() => _ChatScreenState(); } class _ChatScreenState extends State with WidgetsBindingObserver { final _scrollController = ScrollController(); @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addPostFrameCallback((_) { final auth = context.read(); final chat = context.read(); chat.updateAuth(auth); chat.addListener(_onChatUpdate); }); } String? _lastError; void _onChatUpdate() { final chat = context.read(); if (chat.error != null && chat.error != _lastError) { _lastError = chat.error; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(chat.error!), backgroundColor: Colors.red.shade700, action: SnackBarAction( label: 'OK', textColor: Colors.white, onPressed: () => chat.clearError(), ), ), ); } } @override void dispose() { WidgetsBinding.instance.removeObserver(this); _scrollController.dispose(); try { final chat = context.read(); chat.removeListener(_onChatUpdate); } catch (_) {} super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { final chat = context.read(); if (!chat.isConnected) { chat.connect(); } } } void _scrollToBottom() { if (_scrollController.hasClients) { _scrollController.animateTo( _scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 300), curve: Curves.easeOut, ); } } void _sendMessage(String message) { final chat = context.read(); chat.sendMessage(message); Future.delayed(const Duration(milliseconds: 100), _scrollToBottom); } void _newChat() { final chat = context.read(); final name = 'Chat ${DateTime.now().hour}:${DateTime.now().minute.toString().padLeft(2, '0')}'; chat.createSession(name); } void _logout() { final auth = context.read(); final chat = context.read(); chat.disconnect(); auth.logout(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFF1A1A1A), appBar: AppBar( backgroundColor: const Color(0xFF2D2D2D), elevation: 0, title: Consumer( builder: (context, chat, _) { return Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.auto_awesome, color: Colors.orange.shade400, size: 24), const SizedBox(width: 8), Text( chat.currentSession?.name ?? 'Captain Claude', style: const TextStyle(fontSize: 18), ), ], ); }, ), actions: [ Consumer( builder: (context, chat, _) { return IconButton( icon: const Icon(Icons.add), onPressed: _newChat, tooltip: 'Nuevo Chat', ); }, ), IconButton( icon: const Icon(Icons.logout), onPressed: _logout, tooltip: 'Salir', ), ], ), body: Column( children: [ // Connection status bar Consumer( builder: (context, chat, _) { return _buildConnectionBar(chat); }, ), // Messages list Expanded( child: Consumer( builder: (context, chat, _) { WidgetsBinding.instance.addPostFrameCallback((_) { if (chat.isProcessing) _scrollToBottom(); }); if (!chat.isSessionConnected) { return _buildStartState(); } if (chat.messages.isEmpty) { return _buildEmptyState(); } return ListView.builder( controller: _scrollController, padding: const EdgeInsets.only(top: 8, bottom: 8), itemCount: chat.messages.length, itemBuilder: (context, index) { return MessageBubble(message: chat.messages[index]); }, ); }, ), ), // Input area Consumer( builder: (context, chat, _) { return ChatInput( onSend: _sendMessage, isConnected: chat.isConnected && chat.isSessionConnected, isLoading: chat.isProcessing, ); }, ), ], ), ); } Widget _buildConnectionBar(ChatProvider chat) { if (chat.connectionState == ChatConnectionState.connected) { return const SizedBox.shrink(); } Color backgroundColor; String text; IconData icon; switch (chat.connectionState) { case ChatConnectionState.connecting: backgroundColor = Colors.blue.shade700; text = 'Conectando...'; icon = Icons.sync; break; case ChatConnectionState.reconnecting: backgroundColor = Colors.orange.shade700; text = 'Reconectando...'; icon = Icons.sync; break; case ChatConnectionState.error: backgroundColor = Colors.red.shade700; text = 'Error de conexión'; icon = Icons.error_outline; break; default: backgroundColor = Colors.grey.shade700; text = 'Desconectado'; icon = Icons.cloud_off; } final showRetry = chat.connectionState == ChatConnectionState.error || chat.connectionState == ChatConnectionState.disconnected; return GestureDetector( onTap: showRetry ? () => chat.connect() : null, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), color: backgroundColor, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, size: 16, color: Colors.white), const SizedBox(width: 8), Text( showRetry ? '$text - Toca para reintentar' : text, style: const TextStyle(color: Colors.white, fontSize: 13), ), if (chat.connectionState == ChatConnectionState.connecting || chat.connectionState == ChatConnectionState.reconnecting) ...[ const SizedBox(width: 8), const SizedBox( width: 12, height: 12, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ), ], ], ), ), ); } Widget _buildStartState() { return Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.chat_bubble_outline, size: 80, color: Colors.orange.shade400.withOpacity(0.5), ), const SizedBox(height: 24), Text( 'Captain Claude', style: TextStyle( color: Colors.grey.shade300, fontSize: 24, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), Text( 'Toca el botón + para iniciar un chat', textAlign: TextAlign.center, style: TextStyle( color: Colors.grey.shade500, fontSize: 15, ), ), const SizedBox(height: 32), ElevatedButton.icon( onPressed: _newChat, icon: const Icon(Icons.add), label: const Text('Nuevo Chat'), style: ElevatedButton.styleFrom( backgroundColor: Colors.orange.shade700, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), ), ), ], ), ), ); } Widget _buildEmptyState() { return Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.auto_awesome, size: 80, color: Colors.orange.shade400.withOpacity(0.5), ), const SizedBox(height: 24), Text( 'Chat Conectado', style: TextStyle( color: Colors.grey.shade300, fontSize: 24, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), Text( 'Escribe un mensaje para hablar con Claude', textAlign: TextAlign.center, style: TextStyle( color: Colors.grey.shade500, fontSize: 15, ), ), const SizedBox(height: 32), Wrap( spacing: 8, runSpacing: 8, alignment: WrapAlignment.center, children: [ _buildSuggestionChip('Hola'), _buildSuggestionChip('Estado del sistema'), _buildSuggestionChip('Ayuda'), ], ), ], ), ), ); } Widget _buildSuggestionChip(String text) { return ActionChip( label: Text(text), backgroundColor: const Color(0xFF2D2D2D), labelStyle: TextStyle(color: Colors.grey.shade300, fontSize: 13), side: BorderSide(color: Colors.grey.shade700), onPressed: () => _sendMessage(text), ); } }