Files
worker/docs/CART_INITIALIZATION.md
Phuoc Nguyen 192c322816 a
2025-11-17 17:56:34 +07:00

6.8 KiB

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

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

@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

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

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

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