Código completo de DatabaseService, WhatsAppService, CartProvider, ChatProvider y otros servicios esenciales del sistema.
Arquitectura de Servicios
Servicios modulares y reutilizables que manejan la lógica de negocio y la comunicación con APIs externas.
DatabaseService
import 'package:cloud_firestore/cloud_firestore.dart';
import '../models/user.dart';
import '../models/product.dart';
import '../models/order.dart';
class DatabaseService {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
// Users
Future createUser(User user) async {
await _firestore.collection('users').doc(user.uid).set(user.toMap());
}
Future getUser(String uid) async {
DocumentSnapshot doc = await _firestore.collection('users').doc(uid).get();
if (doc.exists) {
return User.fromFirestore(doc);
}
return null;
}
Future updateUser(String uid, Map data) async {
await _firestore.collection('users').doc(uid).update(data);
}
// Products
Stream> getProducts() {
return _firestore
.collection('products')
.where('isActive', isEqualTo: true)
.orderBy('createdAt', descending: true)
.snapshots()
.map((snapshot) => snapshot.docs
.map((doc) => Product.fromFirestore(doc))
.toList());
}
Future addProduct(Product product) async {
await _firestore.collection('products').add(product.toMap());
}
Future updateProduct(String id, Map data) async {
await _firestore.collection('products').doc(id).update(data);
}
Future deleteProduct(String id) async {
await _firestore.collection('products').doc(id).update({'isActive': false});
}
// Orders
Future createOrder(Order order) async {
await _firestore.collection('orders').add(order.toMap());
}
Stream> getUserOrders(String userId) {
return _firestore
.collection('orders')
.where('userId', isEqualTo: userId)
.orderBy('createdAt', descending: true)
.snapshots()
.map((snapshot) => snapshot.docs
.map((doc) => Order.fromFirestore(doc))
.toList());
}
Future updateOrderStatus(String orderId, String status) async {
await _firestore.collection('orders').doc(orderId).update({'status': status});
}
}
WhatsAppService
import 'dart:convert';
import 'package:http/http.dart' as http;
class WhatsAppService {
static const String _baseUrl = 'https://graph.facebook.com/v17.0';
static const String _phoneNumberId = 'YOUR_PHONE_NUMBER_ID';
static const String _accessToken = 'YOUR_ACCESS_TOKEN';
static Future sendMessage({
required String to,
required String message,
}) async {
try {
final response = await http.post(
Uri.parse('$_baseUrl/$_phoneNumberId/messages'),
headers: {
'Authorization': 'Bearer $_accessToken',
'Content-Type': 'application/json',
},
body: jsonEncode({
'messaging_product': 'whatsapp',
'to': to,
'type': 'text',
'text': {'body': message},
}),
);
if (response.statusCode == 200) {
print('WhatsApp message sent successfully');
return true;
} else {
print('Failed to send WhatsApp message: ${response.body}');
return false;
}
} catch (e) {
print('Error sending WhatsApp message: $e');
return false;
}
}
static Future sendOrderNotification({
required String phone,
required String orderId,
required double total,
}) async {
final message = '''
¡Hola! Tu pedido #$orderId ha sido confirmado.
Total: \$${total.toStringAsFixed(2)}
Gracias por elegir Panadería Antonella.
Te notificaremos cuando tu pedido esté listo.
''';
return await sendMessage(to: phone, message: message);
}
static Future sendOrderReadyNotification({
required String phone,
required String orderId,
}) async {
final message = '''
¡Tu pedido #$orderId está listo!
Puedes pasar a recogerlo en Panadería Antonella.
¡Gracias por tu paciencia!
''';
return await sendMessage(to: phone, message: message);
}
}
CartProvider
import 'package:flutter/foundation.dart';
import '../models/product.dart';
import '../models/cart_item.dart';
class CartProvider with ChangeNotifier {
List _items = [];
double _total = 0.0;
List get items => _items;
double get total => _total;
int get itemCount => _items.length;
void addItem(Product product, {int quantity = 1}) {
final existingIndex = _items.indexWhere((item) => item.product.id == product.id);
if (existingIndex >= 0) {
_items[existingIndex].quantity += quantity;
} else {
_items.add(CartItem(product: product, quantity: quantity));
}
_calculateTotal();
notifyListeners();
}
void removeItem(String productId) {
_items.removeWhere((item) => item.product.id == productId);
_calculateTotal();
notifyListeners();
}
void updateQuantity(String productId, int quantity) {
final index = _items.indexWhere((item) => item.product.id == productId);
if (index >= 0) {
if (quantity <= 0) {
_items.removeAt(index);
} else {
_items[index].quantity = quantity;
}
_calculateTotal();
notifyListeners();
}
}
void clearCart() {
_items.clear();
_total = 0.0;
notifyListeners();
}
void _calculateTotal() {
_total = _items.fold(0.0, (sum, item) => sum + (item.product.price * item.quantity));
}
bool get isEmpty => _items.isEmpty;
bool get isNotEmpty => _items.isNotEmpty;
}
ChatProvider
import 'package:flutter/foundation.dart';
import '../models/chat_message.dart';
import '../core/services/database_service.dart';
class ChatProvider with ChangeNotifier {
final DatabaseService _databaseService = DatabaseService();
List _messages = [];
bool _isLoading = false;
List get messages => _messages;
bool get isLoading => _isLoading;
Future loadMessages() async {
_isLoading = true;
notifyListeners();
try {
// Cargar mensajes desde Firestore
// Implementar según la estructura de datos
_isLoading = false;
notifyListeners();
} catch (e) {
_isLoading = false;
notifyListeners();
print('Error loading messages: $e');
}
}
Future sendMessage(String text, String userId) async {
final message = ChatMessage(
id: DateTime.now().millisecondsSinceEpoch.toString(),
text: text,
userId: userId,
timestamp: DateTime.now(),
isUser: true,
);
_messages.add(message);
notifyListeners();
try {
// Guardar mensaje en Firestore
// await _databaseService.saveMessage(message);
// Simular respuesta del bot
await Future.delayed(Duration(seconds: 2));
final botResponse = ChatMessage(
id: DateTime.now().millisecondsSinceEpoch.toString(),
text: _generateBotResponse(text),
userId: 'bot',
timestamp: DateTime.now(),
isUser: false,
);
_messages.add(botResponse);
notifyListeners();
} catch (e) {
print('Error sending message: $e');
}
}
String _generateBotResponse(String userMessage) {
final message = userMessage.toLowerCase();
if (message.contains('hola') || message.contains('buenos días')) {
return '¡Hola! Bienvenido a Panadería Antonella. ¿En qué puedo ayudarte?';
} else if (message.contains('producto') || message.contains('pan')) {
return 'Tenemos una gran variedad de productos frescos. ¿Te gustaría ver nuestro catálogo?';
} else if (message.contains('precio') || message.contains('costo')) {
return 'Los precios varían según el producto. ¿Qué te interesa específicamente?';
} else if (message.contains('horario') || message.contains('abierto')) {
return 'Estamos abiertos de lunes a domingo de 6:00 AM a 8:00 PM.';
} else {
return 'Gracias por tu mensaje. Un representante te contactará pronto.';
}
}
void clearMessages() {
_messages.clear();
notifyListeners();
}
}
Patrones de Diseño
Utiliza el patrón Provider para state management y el patrón Repository para abstraer el acceso a datos. Esto facilita el testing y mantenimiento.