Skip to content

Architecture

Layered Architecture

Presentation (Pages / Widgets)
State Management (Riverpod Providers)
Data Access (API Classes → Supabase Client)
Backend (Supabase PostgreSQL)

State Management: Riverpod

The app uses Riverpod ^3.2.1 with the following provider types:

Provider Type Usage Count
Provider Singleton services, computed state ~8
FutureProvider One-shot data fetches (categories, promotions) ~14
FutureProvider.family Parameterized fetches (product by ID, offers by product) ~6
StreamProvider Real-time streams (orders, chat rooms, messages) ~5
NotifierProvider Mutable state (cart, locale, user role) ~5
StateNotifierProvider Complex state with async operations (profile, orders) ~8
AsyncNotifierProvider Async mutable state (addresses, wishlist) ~3

Provider pattern

// 1. Create API class
final productApiProvider = Provider<ProductApi>((ref) {
  return ProductApi(Supabase.instance.client);
});

// 2. Create provider using API
final productsProvider = FutureProvider.family<Map<String, dynamic>, ProductParams>(
  (ref, params) async {
    final api = ref.read(productApiProvider);
    return api.fetchProducts(
      page: params.page,
      limit: params.limit,
      categoryId: params.categoryId,
    );
  },
);

// 3. Consume in widget
@override
Widget build(BuildContext context, WidgetRef ref) {
  final productsAsync = ref.watch(productsProvider(defaultParams));
  return productsAsync.when(
    data: (data) => ProductGrid(products: data['products']),
    loading: () => const SkeletonLoader(),
    error: (e, _) => ErrorWidget(e.toString()),
  );
}

Routing

File: lib/core/routes/app_routes.dart

Route Group Path Prefix Pages
Auth /login, /register, /otp-verification 4
Customer /customer/home, /customer/products, etc. 10
Seller /seller/dashboard, /seller/products, etc. 7
Payment /payment/ 4
Shared /profile, /settings, /notifications, etc. 17

Total: 42 named routes

Data Layer: API Classes

20 API classes, each wrapping Supabase.instance.client:

lib/core/api/
├── auth_api.dart              # Auth, OTP, OAuth
├── product_api.dart           # Products, search, featured
├── category_api.dart          # Categories, tree
├── brand_api.dart             # Brands
├── pharmacy_api.dart          # Pharmacies, nearby, registration
├── order_api.dart             # Orders, status
├── cart_api.dart              # Cart CRUD
├── address_api.dart           # Address CRUD
├── notification_api.dart      # Notifications, unread count
├── promotion_api.dart         # Active promotions
├── offer_api.dart             # Active offers
├── review_api.dart            # Product reviews
├── wishlist_api.dart          # Wishlist CRUD
├── chat_api.dart              # Real-time chat, messages
├── chatbot_api.dart           # AI chatbot
├── cms_api.dart               # Banners, static pages, settings
├── profile_api.dart           # User profile, FCM token
├── security_settings_api.dart # Security preferences
├── featured_product_api.dart  # Featured product placements
├── public_user_api.dart       # (unused/commented)

Service Layer: 12 Services

lib/core/services/
├── api_client.dart            # HTTP client wrapper (unused, Supabase preferred)
├── device_service.dart        # Cache management, app updates
├── firebase_notification_service.dart  # FCM initialization
├── locale_service.dart        # Language persistence
├── location_service.dart      # GPS, geocoding, distance
├── notification_service.dart  # Notification creation + notifySellers/Admins
├── onboarding_service.dart    # Onboarding seen state
├── payment_service.dart       # Chapa payment
├── pharmacist_service.dart    # Seller dashboard ops
├── secure_storage_service.dart# Token/session persistence
├── subscription_service.dart  # Pharmacy subscription CRUD

Error Handling

  • API layer: All API methods catch Supabase errors and return Map<String, dynamic> with structured error info
  • Provider layer: Riverpod's AsyncValue handles loading/error/data states natively
  • UI layer: when(data:, loading:, error:) pattern on all async providers
  • Toast notifications: app_toast.dart for user-facing errors
  • Logging: logger.dart with configurable levels via FLUTTER_LOG_LEVEL define