285 lines
6.9 KiB
Markdown
285 lines
6.9 KiB
Markdown
# 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<Set<String>>` | Main favorites state |
|
|
| `isFavoriteProvider(productId)` | `Provider<bool>` | Check if product is favorited |
|
|
| `favoriteCountProvider` | `Provider<int>` | Total count of favorites |
|
|
| `favoriteProductIdsProvider` | `Provider<List<String>>` | All favorite product IDs |
|
|
| `favoritesLocalDataSourceProvider` | `Provider<DataSource>` | Database access |
|
|
| `currentUserIdProvider` | `Provider<String>` | 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: () => CircularProgressIndicator(),
|
|
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<FavoriteModel>(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<String>`
|
|
- **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
|