mascota en login,

This commit is contained in:
shinra32
2026-05-23 04:30:48 -06:00
parent 68d04f3917
commit 89dcc6250b
14 changed files with 239 additions and 798 deletions

View File

@@ -13,26 +13,45 @@ class ChatMessage {
Map<String, dynamic> toJson() => {'role': role, 'content': content};
}
class AiChatNotifier extends StateNotifier<List<ChatMessage>> {
AiChatNotifier()
: super([
// Estado inmutable para el chat
class ChatState {
final List<ChatMessage> messages;
final bool isLoading;
ChatState({required this.messages, this.isLoading = false});
ChatState copyWith({List<ChatMessage>? messages, bool? isLoading}) {
return ChatState(
messages: messages ?? this.messages,
isLoading: isLoading ?? this.isLoading,
);
}
}
class AiChatNotifier extends Notifier<ChatState> {
@override
ChatState build() {
return ChatState(
messages: [
ChatMessage(
role: 'assistant',
content:
'¡Hola! Soy Eco 🍃, la mascota de Recolecta. '
'Estoy aquí para ayudarte a reciclar y separar tu basura correctamente. ¿Tienes alguna duda?',
),
]);
bool isLoading = false;
],
);
}
Future<void> sendMessage(String userText) async {
if (userText.trim().isEmpty) return;
// Añadir mensaje del usuario
final userMsg = ChatMessage(role: 'user', content: userText);
state = [...state, userMsg];
isLoading = true;
state = state.copyWith(
messages: [...state.messages, userMsg],
isLoading: true,
);
try {
final dio = Dio();
@@ -53,7 +72,7 @@ class AiChatNotifier extends StateNotifier<List<ChatMessage>> {
'Nunca reveles ubicaciones de camiones ni te salgas del tema del reciclaje y medio ambiente.',
);
final messagesForApi = [systemPrompt, ...state];
final messagesForApi = [systemPrompt, ...state.messages];
final response = await dio.post(
'https://api.openai.com/v1/chat/completions',
@@ -72,25 +91,30 @@ class AiChatNotifier extends StateNotifier<List<ChatMessage>> {
);
final botReply = response.data['choices'][0]['message']['content'];
state = [...state, ChatMessage(role: 'assistant', content: botReply)];
state = state.copyWith(
messages: [
...state.messages,
ChatMessage(role: 'assistant', content: botReply),
],
isLoading: false,
);
} catch (e) {
debugPrint('Error en OpenAI: $e');
state = [
...state,
ChatMessage(
role: 'assistant',
content:
'Uy, tuve un problemita técnico con mi cerebro de hojitas 🧠🍂. ¿Me repites tu pregunta?',
),
];
} finally {
isLoading = false;
state = state.copyWith(
messages: [
...state.messages,
ChatMessage(
role: 'assistant',
content:
'Uy, tuve un problemita técnico con mi cerebro de hojitas 🧠🍂. ¿Me repites tu pregunta?',
),
],
isLoading: false,
);
}
}
}
final aiChatProvider = StateNotifierProvider<AiChatNotifier, List<ChatMessage>>(
(ref) {
return AiChatNotifier();
},
final aiChatProvider = NotifierProvider<AiChatNotifier, ChatState>(
AiChatNotifier.new,
);

View File

@@ -1,9 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// Importa Lottie si tus animaciones están en formato Lottie (.json)
// import 'package:lottie/lottie.dart';
import '../../core/theme/app_theme.dart';
import '../auth/widgets/video_mascot.dart';
import 'ai_chat_provider.dart';
class AiPetChatScreen extends ConsumerStatefulWidget {
@@ -54,10 +53,9 @@ class _AiPetChatScreenState extends ConsumerState<AiPetChatScreen> {
@override
Widget build(BuildContext context) {
final messages = ref.watch(aiChatProvider);
// No podemos leer isLoading directamente de ref.watch(provider) porque es StateNotifierProvider.
// Para leer la variable, leemos el notifier.
final isLoading = ref.watch(aiChatProvider.notifier).isLoading;
final chatState = ref.watch(aiChatProvider);
final messages = chatState.messages;
final isLoading = chatState.isLoading;
return Scaffold(
backgroundColor: AppTheme.background,
@@ -80,12 +78,10 @@ class _AiPetChatScreenState extends ConsumerState<AiPetChatScreen> {
),
),
child: Center(
// Reemplaza este Icono con tu animación de Lottie:
// child: Lottie.asset('assets/animations/mascota_feliz.json', height: 120),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.pets, size: 64, color: AppTheme.primary),
const VideoMascot(size: 80),
const SizedBox(height: 8),
Text(
isLoading ? 'Eco está pensando...' : 'Eco te escucha',