Files
worker/lib/features/favorites/README.md
Phuoc Nguyen b27c5d7742 favorite
2025-10-24 16:20:48 +07:00

6.9 KiB

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

import 'package:worker/features/favorites/presentation/providers/favorites_provider.dart';

2. Check if Product is Favorited

final isFavorited = ref.watch(isFavoriteProvider(productId));

3. Toggle Favorite

ref.read(favoritesProvider.notifier).toggleFavorite(productId);

4. Get Favorites Count

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

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

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

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:

// 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:

await Hive.openBox<FavoriteModel>(HiveBoxNames.favoriteBox);

📚 Documentation


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

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 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:


Status: Ready for integration Version: 1.0.0 Last Updated: 2024-10-24