Bilingual enterprise inventory suite—field reps and admins on one Supabase backend
In product terms: a Flutter client where workers (مندوب) browse live stock, build a local draft order, and confirm so the backend decrements inventory and records orders + line items. Admins run the catalog, see reservations and orders (including via an Edge Function + Dio), per-rep analytics, batch confirmations, and shared messaging. Auth is role-aware; a splash session check routes into separate navigation shells per role.
My role: End-to-end Flutter development, Supabase (schema, realtime, auth), Edge Function integration for aggregated admin reads, and Arabic-first UI.
Foundational client project for a meat wholesale company—one of the first production-style systems on my path—built to replace a fragmented internal reservation process with a single, auditable workflow.
Live demo instead of a gallery
One recorded walkthrough covers the problem, the UI, BLoC + Supabase architecture, and how the app replaced manual inventory chaos. Opens in a new tab.
Operations teams juggle stock truth, who sold what, and management visibility across phones and desks. The company’s internal reservation workflow was informal—easy to misread, hard to reconcile, and weak for auditability.
The product idea is to separate duties: people in the field should not need full database access, while leadership needs aggregation, auditability, and broadcast tools. One shared backend (inventory, orders, order_items, clients, messaging) powers two deliberate experiences—not two unrelated apps.
A single codebase encodes fast, guided flows for workers and control plus insight for admins, with a deliberately simple surface over strict data rules: one app entry, role-based routing, live inventory, and a confirm step that ties quantity changes to persisted orders.
Fragmented reservations, oversell risk, and no single place to see every rep’s activity.
One operational system: realtime catalog truth, draft-to-confirm sales, and admin oversight including analytics and messaging.
Replaced manual coordination with a deployed internal app—streamlined ordering without constant accountant mediation.
The “final product” is not a generic inventory screen—it is a role matrix mapped to navigation and data emphasis.
| Dimension | Worker (مندوب, user) |
Admin (admin) |
|---|---|---|
| Primary job | Take orders against live inventory; draft cart locally; submit so stock and orders stay consistent. | CRUD catalog; see everyone’s reservations and orders; analytics; confirm rep summaries; messaging. |
| Home hub | Shortcuts: stock, reservation history, messages. | Shortcuts: stock, reservations/orders, broadcast message, reps’ order board. |
| Navigation | Bottom GNav + PageView: reservations, cart/orders, home, inventory, messages. |
Same pattern: analysis, reservations, home, inventory, messages. |
| Data emphasis | Realtime inventory stream; SharedPreferences for pending lines until confirm. | Streams for inventory; HTTP to Edge for enriched order lists; order summary APIs. |
Authentication and onboarding — Supabase initialized from .env (URL + anon key). Splash checks session and routes to auth or the correct shell. Login uses tabs for admin vs مندوب with RTL layout.
Worker — Live inventory via inventory stream and fetches. Draft pipeline: add lines → SharedPreferences → confirm runs read qty → validate → update rows → insert orders and order_items (Arabic errors for missing SKU or oversell). Orders screen with grid-style sections, snackbars, inventory refresh on success. User reservations/history tied to user_id. Messages page reused for company-wide comms.
Admin — Inventory add/update/delete with realtime stream (DB triggers in the pipeline). Reservations and orders via Dio to an Edge Function URL from env, with defensive parsing of nested items. Analysis: orders summary per user, cards per rep, drill-down, confirm user orders with feedback. Messaging via MessageBloc and entities. Logout returns to auth.
Cross-cutting — Clean architecture per feature, repository implementations, use cases, Either + Failure from dartz. Root MultiBlocProvider for Auth, Admin (inventory, analysis, messages, reservations, orders summary), and User (inventory, orders, reservations).
Visual system: Centralized palette—deep bluish nav, teal/green accents, warm off-whites, red for errors—cohesive across admin and worker shells.
Typography: Google Fonts Almarai through a custom TextTheme for readable Arabic at scale.
Responsiveness: flutter_screenutil with a 390×844 design baseline, SafeArea, padding via .w / .h.
Motion: flutter_animate on splash (e.g. pulsing logo) for a polished first load.
Navigation: google_nav_bar plus a non-swipeable PageView for predictable tabs; home cards jump to the correct tab via ancestor state (WrapperNavigationState / UserWrapperNavigationState)—hub-and-spoke for non-technical users.
Density: Syncfusion Flutter DataGrid for inventory and orders where tabular accuracy matters; snackbars and BlocListener patterns keep async errors visible.
Stack: Flutter (Dart 3.6+), Supabase Flutter, flutter_bloc, dio, shared_preferences, flutter_dotenv, equatable, syncfusion_flutter_datagrid, intl, google_nav_bar, flutter_screenutil, google_fonts, flutter_animate. fl_chart is in pubspec as a declared dependency for future analytics visuals—not yet referenced in lib/ at documentation time.
Role model: Session carries admin vs user. Splash resolves auth and routes into two different PageView + Google Nav Bar shells so permission boundaries stay obvious in the UI, not only in queries.
Worker path: Subscribes to inventory realtime streams; builds a local draft order; on confirm, the flow reads current quantity, validates against requested amount, rejects oversell with Arabic messaging, then updates rows and inserts orders + order_items in a coordinated sequence—integrity enforced at the application layer with room to harden further via DB transactions and constraints.
Admin path: CRUD on catalog with realtime refresh; reservations and order lists enriched via a Supabase Edge Function consumed through Dio (defensive JSON parsing for nested items, fallbacks like unknown user labels); separate BLoCs for inventory, reservations, order summaries, analysis drill-down, and messaging.
Configuration and targets: Secrets and function URLs in .env, not hardcoded. Mobile-first UI; Flutter web/ assets support a multi-platform story where relevant.
newQty, reject if negative, then update—documented as guarding oversell at the app layer; DB constraints and transactions are the natural next hardening step.PageView apps—reduces wrong-permission UI and keeps user mental models simple.Directionality, Almarai, and Arabic copy in analysis empty states and errors—built for local-market operations.Either-based failuresorders + order_items with quantity validationfl_chart only when charts are wired; today it is a declared dependency, not used in lib/—analytics remain card- and grid-driven until then.UserLocalDataSource paths may still carry TODOs or stubs (e.g. line edit/remove)—fair to call “planned iteration” in conversation.Flutter · Dart · Supabase · PostgreSQL · Edge Functions · BLoC · Clean Architecture · Dio · REST · Realtime · Syncfusion DataGrid · RTL / i18n · Material Design · Git