# Favorites Feature A complete favorites state management system for the Worker app using **Riverpod 3.0** with code generation and **Hive** for local persistence. --- ## ๐Ÿ“ File Structure ``` lib/features/favorites/ โ”œโ”€โ”€ data/ โ”‚ โ”œโ”€โ”€ datasources/ โ”‚ โ”‚ โ””โ”€โ”€ favorites_local_datasource.dart # Hive operations โ”‚ โ””โ”€โ”€ models/ โ”‚ โ”œโ”€โ”€ favorite_model.dart # Hive model (TypeId: 28) โ”‚ โ””โ”€โ”€ favorite_model.g.dart # Generated adapter โ”œโ”€โ”€ domain/ โ”‚ โ””โ”€โ”€ entities/ โ”‚ โ””โ”€โ”€ favorite.dart # Business entity โ”œโ”€โ”€ presentation/ โ”‚ โ””โ”€โ”€ providers/ โ”‚ โ”œโ”€โ”€ favorites_provider.dart # Riverpod providers โ”‚ โ””โ”€โ”€ favorites_provider.g.dart # Generated providers โ”œโ”€โ”€ INTEGRATION_SUMMARY.md # Complete documentation โ”œโ”€โ”€ USAGE_EXAMPLES.md # Code examples โ””โ”€โ”€ README.md # This file ``` --- ## ๐Ÿš€ Quick Start ### 1. Import Provider ```dart import 'package:worker/features/favorites/presentation/providers/favorites_provider.dart'; ``` ### 2. Check if Product is Favorited ```dart final isFavorited = ref.watch(isFavoriteProvider(productId)); ``` ### 3. Toggle Favorite ```dart ref.read(favoritesProvider.notifier).toggleFavorite(productId); ``` ### 4. Get Favorites Count ```dart final count = ref.watch(favoriteCountProvider); ``` --- ## ๐Ÿ“ฆ Available Providers | Provider | Type | Description | |----------|------|-------------| | `favoritesProvider` | `AsyncNotifier>` | Main favorites state | | `isFavoriteProvider(productId)` | `Provider` | Check if product is favorited | | `favoriteCountProvider` | `Provider` | Total count of favorites | | `favoriteProductIdsProvider` | `Provider>` | All favorite product IDs | | `favoritesLocalDataSourceProvider` | `Provider` | Database access | | `currentUserIdProvider` | `Provider` | Current user ID (TODO: auth) | --- ## ๐ŸŽฏ Common Use Cases ### Favorite Button in Product Card ```dart class ProductCard extends ConsumerWidget { final String productId; @override Widget build(BuildContext context, WidgetRef ref) { final isFavorited = ref.watch(isFavoriteProvider(productId)); return IconButton( icon: Icon( isFavorited ? Icons.favorite : Icons.favorite_border, color: isFavorited ? Colors.red : Colors.grey, ), onPressed: () { ref.read(favoritesProvider.notifier).toggleFavorite(productId); }, ); } } ``` ### Favorites Count Badge ```dart class FavoriteBadge extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final count = ref.watch(favoriteCountProvider); return Badge( label: Text('$count'), child: Icon(Icons.favorite), ); } } ``` ### Display All Favorites ```dart class FavoritesPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final favoritesAsync = ref.watch(favoritesProvider); return favoritesAsync.when( data: (favorites) => ListView.builder( itemCount: favorites.length, itemBuilder: (context, index) { final productId = favorites.elementAt(index); return ProductTile(productId: productId); }, ), loading: () => const CustomLoadingIndicator(), error: (error, stack) => ErrorWidget(error), ); } } ``` --- ## ๐Ÿ”ง Provider Methods ### Main Provider (`favoritesProvider.notifier`) | Method | Description | |--------|-------------| | `addFavorite(productId)` | Add product to favorites | | `removeFavorite(productId)` | Remove product from favorites | | `toggleFavorite(productId)` | Toggle favorite status | | `refresh()` | Reload favorites from database | | `clearAll()` | Remove all favorites | --- ## ๐Ÿ’พ Data Persistence - **Storage**: Hive local database - **Box**: `favorite_box` (HiveBoxNames.favoriteBox) - **Model**: `FavoriteModel` (TypeId: 28) - **Fields**: - `favoriteId`: Unique ID (userId_productId_timestamp) - `productId`: Product reference - `userId`: User reference - `createdAt`: Timestamp --- ## โš ๏ธ Important Notes ### TODO: Auth Integration Currently using hardcoded userId (`'user_001'`). Replace when auth is ready: ```dart // In favorites_provider.dart @riverpod String currentUserId(Ref ref) { // TODO: Replace with actual auth provider final user = ref.watch(authProvider).user; return user?.id ?? 'guest'; } ``` ### Hive Box Initialization Ensure the favorites box is opened in `main.dart`: ```dart await Hive.openBox(HiveBoxNames.favoriteBox); ``` --- ## ๐Ÿ“š Documentation - **[INTEGRATION_SUMMARY.md](./INTEGRATION_SUMMARY.md)** - Complete technical documentation - **[USAGE_EXAMPLES.md](./USAGE_EXAMPLES.md)** - Comprehensive code examples --- ## โœ… Features - โœ… Add/remove favorites - โœ… Toggle favorite status - โœ… Persistent storage (Hive) - โœ… Multi-user support - โœ… Optimistic updates - โœ… Error handling - โœ… Loading states - โœ… Debug logging - โœ… Auto-dispose providers --- ## ๐Ÿงช Testing ```dart test('should add favorite', () async { final container = ProviderContainer(); addTearDown(container.dispose); await container.read(favoritesProvider.notifier) .addFavorite('product_1'); final favorites = await container.read(favoritesProvider.future); expect(favorites, contains('product_1')); }); ``` --- ## ๐ŸŽจ UI Integration Examples See **[USAGE_EXAMPLES.md](./USAGE_EXAMPLES.md)** for: - Favorite buttons with loading states - Favorites page with pull-to-refresh - Empty state handling - Error state handling - App lifecycle management - Performance optimization --- ## ๐Ÿ› Debugging All operations log to console: ``` [FavoritesProvider] Added favorite: product_123 [FavoritesLocalDataSource] Loaded 5 favorites for user: user_001 [FavoritesProvider] Error adding favorite: ... ``` --- ## ๐Ÿ“ˆ Performance - **O(1)** favorite lookup using `Set` - **Auto-dispose** providers when not in use - **Selective rebuilds** with `isFavoriteProvider` - **Database compaction** available --- ## ๐Ÿ”ฎ Future Enhancements - [ ] Remote API sync - [ ] Offline queue - [ ] Cross-device sync - [ ] Batch operations - [ ] Favorites collections - [ ] Share favorites - [ ] Analytics tracking --- ## ๐Ÿ†˜ Troubleshooting | Issue | Solution | |-------|----------| | Favorites not persisting | Check Hive box initialization in `main.dart` | | State not updating | Use `ref.read()` for mutations, `ref.watch()` for listening | | Performance issues | Use `isFavoriteProvider` instead of watching full `favoritesProvider` | --- ## ๐Ÿ“ž Support For detailed examples and advanced use cases, see: - **[USAGE_EXAMPLES.md](./USAGE_EXAMPLES.md)** - **[INTEGRATION_SUMMARY.md](./INTEGRATION_SUMMARY.md)** --- **Status**: โœ… Ready for integration **Version**: 1.0.0 **Last Updated**: 2024-10-24