update md

This commit is contained in:
Phuoc Nguyen
2025-11-28 15:16:40 +07:00
parent 440b474504
commit 65f6f825a6
17 changed files with 73 additions and 0 deletions

View File

@@ -0,0 +1,238 @@
# Cart Initialization & Keep Alive Implementation
## Overview
The cart is now initialized when the app starts (on HomePage mount) and kept alive throughout the entire app session. This ensures:
- Cart data is loaded from API once on startup
- Cart state persists across all navigation
- No unnecessary re-fetching when navigating between pages
- Real-time cart badge updates across all screens
## Implementation Details
### 1. Cart Provider with Keep Alive
**File**: `lib/features/cart/presentation/providers/cart_provider.dart`
```dart
@Riverpod(keepAlive: true) // ✅ Keep alive throughout app session
class Cart extends _$Cart {
@override
CartState build() {
return CartState.initial().copyWith(
memberTier: 'Diamond',
memberDiscountPercent: 15.0,
);
}
Future<void> initialize() async {
// Load cart from API with Hive fallback
// ...
}
}
// Dependent providers also need keepAlive
@Riverpod(keepAlive: true)
int cartItemCount(Ref ref) {
final cartState = ref.watch(cartProvider);
return cartState.items.length;
}
@Riverpod(keepAlive: true)
double cartTotal(Ref ref) {
final cartState = ref.watch(cartProvider);
return cartState.total;
}
```
### 1.1 Cart Data Providers with Keep Alive
**File**: `lib/features/cart/data/providers/cart_data_providers.dart`
**CRITICAL**: All cart data layer providers must also use `keepAlive: true` to prevent disposal errors:
```dart
@Riverpod(keepAlive: true)
CartLocalDataSource cartLocalDataSource(Ref ref) {
final hiveService = HiveService();
return CartLocalDataSourceImpl(hiveService);
}
@Riverpod(keepAlive: true)
Future<CartRemoteDataSource> cartRemoteDataSource(Ref ref) async {
final dioClient = await ref.watch(dioClientProvider.future);
return CartRemoteDataSourceImpl(dioClient);
}
@Riverpod(keepAlive: true)
Future<CartRepository> cartRepository(Ref ref) async {
final remoteDataSource = await ref.watch(cartRemoteDataSourceProvider.future);
final localDataSource = ref.watch(cartLocalDataSourceProvider);
return CartRepositoryImpl(
remoteDataSource: remoteDataSource,
localDataSource: localDataSource,
);
}
```
**Why all providers need keepAlive:**
- Cart provider depends on cartRepository
- If repository is disposed, cart operations fail with "Ref disposed" error
- All dependencies in the chain must persist together
- Ensures consistent lifecycle management
**Benefits of `keepAlive: true`:**
- Provider state is never disposed
- Cart data persists when navigating away and back
- No re-initialization needed on subsequent visits
- Consistent cart count across all app screens
- No "Ref disposed" errors during async operations
### 2. HomePage Initialization
**File**: `lib/features/home/presentation/pages/home_page.dart`
```dart
class HomePage extends ConsumerStatefulWidget {
const HomePage({super.key});
@override
ConsumerState<HomePage> createState() => _HomePageState();
}
class _HomePageState extends ConsumerState<HomePage> {
@override
void initState() {
super.initState();
// Initialize cart from API on app startup
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(cartProvider.notifier).initialize();
});
}
@override
Widget build(BuildContext context) {
// Watch cart item count for badge
final cartItemCount = ref.watch(cartItemCountProvider);
// ...
}
}
```
**Why in HomePage?**
- HomePage is the first screen after login
- Ensures cart is loaded early in app lifecycle
- Provides immediate cart count for navigation badge
### 3. Cart Badge Integration
**Location**: All pages with cart icon/badge
```dart
// Any page can watch cart count - it's always available
final cartItemCount = ref.watch(cartItemCountProvider);
// Display badge
if (cartItemCount > 0)
Badge(
label: Text('$cartItemCount'),
child: Icon(Icons.shopping_cart),
)
```
## Data Flow
```
App Start
HomePage mounts
initState() calls cart.initialize()
Cart loads from API → Syncs to Hive
Cart state updates with items
cartItemCountProvider updates
All badges across app update reactively
[keepAlive ensures state persists during navigation]
```
## API & Local Storage Integration
### Initialize Flow
1. **API First**: Fetch cart items from ERPNext API
2. **Product Details**: For each cart item, fetch full product data
3. **Calculate Conversions**: Apply business rules (boxes, m², etc.)
4. **Update State**: Set cart items with full product info
5. **Local Sync**: Automatically synced to Hive by repository
### Offline Fallback
- If API fails, cart loads from Hive cache
- All mutations queue for sync when online
- See `cart_repository_impl.dart` for sync logic
## Cart Operations
All cart operations work seamlessly after initialization:
```dart
// Add to cart (from any page)
await ref.read(cartProvider.notifier).addToCart(product, quantity: 2.0);
// Remove from cart
await ref.read(cartProvider.notifier).removeFromCart(productId);
// Update quantity
await ref.read(cartProvider.notifier).updateQuantity(productId, 5.0);
// Clear cart
await ref.read(cartProvider.notifier).clearCart();
```
All operations:
- Sync to API first
- Fallback to local on failure
- Queue for sync when offline
- Update UI reactively
## Testing Keep Alive
To verify keepAlive works:
1. **Navigate to HomePage** → Cart initializes
2. **Add items to cart** → Badge shows count
3. **Navigate to Products page** → Badge still shows count
4. **Navigate back to HomePage** → Cart state preserved, no re-fetch
5. **Navigate to Cart page** → Same items, no loading
6. **Hot restart app** → Cart reloads from API
## Performance Benefits
- **One-time API call**: Cart loads once on startup
- **No re-fetching**: Navigation doesn't trigger reloads
- **Instant updates**: All cart operations update state immediately
- **Offline support**: Hive cache provides instant fallback
- **Memory efficient**: Single provider instance for entire app
## Error Handling
If cart initialization fails:
- Error stored in `cartState.errorMessage`
- Can retry via `ref.read(cartProvider.notifier).initialize()`
- Cart page shows error state with retry button
- Local Hive cache used if available
## Related Files
- **Cart Provider**: `lib/features/cart/presentation/providers/cart_provider.dart`
- **Cart State**: `lib/features/cart/presentation/providers/cart_state.dart`
- **Data Providers**: `lib/features/cart/data/providers/cart_data_providers.dart`
- **Repository**: `lib/features/cart/data/repositories/cart_repository_impl.dart`
- **HomePage**: `lib/features/home/presentation/pages/home_page.dart`
## Future Enhancements
Potential improvements:
- Add periodic background sync (every 5 minutes)
- Implement optimistic updates for faster UI
- Add cart merge logic when switching accounts
- Implement cart expiry (clear after 30 days)
- Add analytics tracking for cart events