<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Surendra Kumar</title>
    <description>The latest articles on Forem by Surendra Kumar (@surendra_kumar_f2f7e31559).</description>
    <link>https://forem.com/surendra_kumar_f2f7e31559</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3851581%2F7a418d1b-66b4-4ec1-a12a-4d17d8033b92.jpg</url>
      <title>Forem: Surendra Kumar</title>
      <link>https://forem.com/surendra_kumar_f2f7e31559</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/surendra_kumar_f2f7e31559"/>
    <language>en</language>
    <item>
      <title>Building a Zomato-Like Multi-City Food Delivery App with Flutter &amp; Firebase</title>
      <dc:creator>Surendra Kumar</dc:creator>
      <pubDate>Tue, 28 Apr 2026 05:37:47 +0000</pubDate>
      <link>https://forem.com/surendra_kumar_f2f7e31559/building-a-zomato-like-multi-city-food-delivery-app-with-flutter-firebase-52bd</link>
      <guid>https://forem.com/surendra_kumar_f2f7e31559/building-a-zomato-like-multi-city-food-delivery-app-with-flutter-firebase-52bd</guid>
      <description>&lt;p&gt;Hey everyone, Surendra Kumar here —Flutter/Firebase developer from Banda, India. I've been building production apps under Gfood Delivery Private Limited, and today I want to share the architecture behind my most ambitious project yet: Zesto, a multi-city food delivery platform built entirely with Flutter and Firebase.&lt;br&gt;
Zesto isn't a tutorial project. It's a real, working platform — 4 Flutter apps, a complete Firebase backend, and architecture decisions modeled after how production platforms like Zomato actually work.&lt;br&gt;
Want to try it before reading? Download the customer app APK and test it yourself:&lt;br&gt;
Download Zesto Customer App &lt;a href="https://github.com/ssurekumar01111-hue/zesto-demo/releases/download/v1.0-beta/app-release.apk" rel="noopener noreferrer"&gt;https://github.com/ssurekumar01111-hue/zesto-demo/releases/download/v1.0-beta/app-release.apk&lt;/a&gt;&lt;br&gt;
I'd love your feedback and improvement ideas after reading.&lt;/p&gt;

&lt;p&gt;The 4-App Ecosystem&lt;br&gt;
Zesto consists of four interconnected Flutter applications:&lt;/p&gt;

&lt;p&gt;Customer App — Multi-city restaurant discovery, cart, Razorpay + wallet payments, live GPS tracking, reviews&lt;br&gt;
Restaurant Partner App — Order management, menu CRUD, earnings dashboard, FCM order alerts&lt;br&gt;
Driver App — Order accept/reject, live delivery tracking, earnings, payout requests&lt;br&gt;
Flutter Web Admin Panel — 14 screens covering cities, zones, restaurants, drivers, orders, analytics, reviews, ads, config, and audit logs&lt;/p&gt;

&lt;p&gt;The Core Architecture Decision: City + Zone Scoping&lt;br&gt;
This is the most important architectural decision in Zesto — and the one that separates it from basic food delivery tutorials.&lt;br&gt;
Every single Firestore document has two fields:&lt;br&gt;
dart{&lt;br&gt;
"city_id": "banda_up",&lt;br&gt;
"zone_id": "banda_zone_1",&lt;br&gt;
// ... rest of document&lt;br&gt;
}&lt;br&gt;
Every query filters by city_id first:&lt;br&gt;
dartFirebaseFirestore.instance&lt;br&gt;
.collection('restaurants')&lt;br&gt;
.where('city_id', isEqualTo: currentCityId)&lt;br&gt;
.where('is_active', isEqualTo: true)&lt;br&gt;
.snapshots();&lt;br&gt;
Why does this matter? Without city scoping, as you add cities your queries return restaurants from everywhere. Firestore has no built-in geographic filtering — you have to design it in from day one. Retrofitting this later would require rewriting every query and migrating every document.&lt;br&gt;
Zones sit inside cities and control driver assignment. When an order is placed, the Cloud Function assigns it to available drivers in the same zone first, then expands to the full city if no zone drivers are available.&lt;/p&gt;

&lt;p&gt;Firebase Custom Claims for RBAC&lt;br&gt;
Zesto has 5 user roles: customer, restaurant_owner, driver, admin, super_admin. Managing this with Firestore documents alone creates security holes — a user could theoretically write to their own document and escalate their role.&lt;br&gt;
The solution is Firebase custom claims set via Cloud Functions:&lt;br&gt;
javascript// functions/src/admin/setUserRole.js&lt;br&gt;
exports.setUserRole = functions.https.onCall(async (data, context) =&amp;gt; {&lt;br&gt;
if (!context.auth.token.super_admin) {&lt;/p&gt;

&lt;p&gt;throw new functions.https.HttpsError('permission-denied', 'Not authorized');&lt;br&gt;
}&lt;br&gt;
await admin.auth().setCustomUserClaims(data.uid, {&lt;/p&gt;

&lt;p&gt;role: data.role,&lt;br&gt;
city_id: data.cityId,&lt;br&gt;
});&lt;br&gt;
});&lt;br&gt;
On the Flutter side, the role is read from the ID token directly:&lt;br&gt;
dartfinal token = await FirebaseAuth.instance.currentUser?.getIdTokenResult();&lt;br&gt;
final role = token?.claims?['role'] as String?;&lt;br&gt;
This means even if someone modifies their Firestore document, the custom claim on the token is what gates access. Firestore rules enforce this:&lt;br&gt;
javascript// firestore.rules&lt;br&gt;
match /orders/{orderId} {&lt;br&gt;
allow read: if request.auth.token.role == 'admin'&lt;/p&gt;

&lt;p&gt;|| request.auth.token.role == 'restaurant_owner'&lt;br&gt;
|| request.auth.uid == resource.data.customer_id;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;MobX for State Management&lt;br&gt;
Zesto uses MobX across all 4 apps. Here's why I chose it over Riverpod or GetX for this project:&lt;br&gt;
MobX's reactive observables map perfectly to Firestore streams. When a Firestore stream emits a new value, MobX automatically rebuilds only the widgets that consume that observable — no manual setState, no context.watch boilerplate.&lt;br&gt;
dart// core/stores/order_store.dart&lt;br&gt;
class OrderStore = OrderStore with $OrderStore;&lt;/p&gt;

&lt;p&gt;abstract class _OrderStore with Store {&lt;br&gt;
&lt;a class="mentioned-user" href="https://dev.to/observable"&gt;@observable&lt;/a&gt;&lt;br&gt;
ObservableList activeOrders = ObservableList();&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/observable"&gt;@observable&lt;/a&gt;&lt;br&gt;
OrderStatus currentStatus = OrderStatus.placed;&lt;/p&gt;

&lt;p&gt;@action&lt;br&gt;
void updateOrderStatus(String orderId, OrderStatus status) {&lt;/p&gt;

&lt;p&gt;final idx = activeOrders.indexWhere((o) =&amp;gt; o.id == orderId);&lt;br&gt;
if (idx != -1) {&lt;br&gt;
  activeOrders[idx] = activeOrders[idx].copyWith(status: status);&lt;br&gt;
}&lt;br&gt;
currentStatus = status;&lt;br&gt;
}&lt;br&gt;
}&lt;br&gt;
The store subscribes to a Firestore stream once and updates observables. Every widget that reads activeOrders rebuilds automatically. Clean, testable, no rebuild storms.&lt;/p&gt;

&lt;p&gt;Dual Payment Path: Razorpay + In-App Wallet&lt;br&gt;
Zesto supports two payment methods and they interact with each other in interesting ways.&lt;br&gt;
Razorpay handles card/UPI payments. The flow is standard — create an order via Cloud Functions, open Razorpay checkout, handle the webhook callback.&lt;br&gt;
The wallet is more interesting. Each customer has a wallet_balance field on their user document. When they pay via wallet:&lt;br&gt;
javascript// functions/src/payments/walletPayment.js&lt;br&gt;
exports.processWalletPayment = functions.https.onCall(async (data, context) =&amp;gt; {&lt;br&gt;
const uid = context.auth.uid;&lt;/p&gt;

&lt;p&gt;return admin.firestore().runTransaction(async (transaction) =&amp;gt; {&lt;/p&gt;

&lt;p&gt;const userRef = admin.firestore().collection('users').doc(uid);&lt;br&gt;
const userDoc = await transaction.get(userRef);&lt;/p&gt;

&lt;p&gt;const currentBalance = userDoc.data().wallet_balance;&lt;br&gt;
if (currentBalance &amp;lt; data.amount) {&lt;br&gt;
  throw new functions.https.HttpsError('failed-precondition', 'Insufficient balance');&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;transaction.update(userRef, {&lt;br&gt;
  wallet_balance: admin.firestore.FieldValue.increment(-data.amount)&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;transaction.set(admin.firestore().collection('wallet_txns').doc(), {&lt;br&gt;
  uid,&lt;br&gt;
  amount: -data.amount,&lt;br&gt;
  type: 'debit',&lt;br&gt;
  order_id: data.orderId,&lt;br&gt;
  created_at: admin.firestore.FieldValue.serverTimestamp(),&lt;br&gt;
});&lt;br&gt;
});&lt;br&gt;
});&lt;br&gt;
The Firestore transaction ensures the balance check and deduction happen atomically — no race condition where two simultaneous orders drain the wallet below zero.&lt;/p&gt;

&lt;p&gt;Cloud Functions: Domain-Split Architecture&lt;br&gt;
Instead of one massive index.js, Zesto's Cloud Functions are split by domain:&lt;br&gt;
functions/src/&lt;br&gt;
├── orders/&lt;br&gt;
│ ├── placeOrder.js # Create order, notify restaurant&lt;br&gt;
│ ├── assignDriver.js # Zone-based driver assignment&lt;br&gt;
│ └── onOrderDelivered.js # Earnings calculation, wallet credit&lt;br&gt;
├── payments/&lt;br&gt;
│ ├── razorpayWebhook.js # Payment confirmation&lt;br&gt;
│ └── walletPayment.js # Wallet debit/credit&lt;br&gt;
└── admin/&lt;/p&gt;

&lt;p&gt;├── approveRestaurant.js&lt;br&gt;
└── setUserRole.js&lt;br&gt;
Each function is independently deployable. When I fix a bug in assignDriver.js, I deploy only that function — not the entire backend.&lt;/p&gt;

&lt;p&gt;The Driver Assignment Problem&lt;br&gt;
When an order is placed, the platform needs to assign a driver. Here's the simplified logic:&lt;br&gt;
javascriptexports.assignDriver = functions.firestore&lt;br&gt;
.document('orders/{orderId}')&lt;br&gt;
.onCreate(async (snap, context) =&amp;gt; {&lt;/p&gt;

&lt;p&gt;const order = snap.data();&lt;/p&gt;

&lt;p&gt;// 1. Find online drivers in the same zone&lt;br&gt;
const driversSnap = await admin.firestore()&lt;br&gt;
  .collection('drivers')&lt;br&gt;
  .where('city_id', isEqualTo: order.city_id)&lt;br&gt;
  .where('zone_id', isEqualTo: order.zone_id)&lt;br&gt;
  .where('is_online', isEqualTo: true)&lt;br&gt;
  .where('current_order_id', isEqualTo: null)&lt;br&gt;
  .limit(5)&lt;br&gt;
  .get();&lt;/p&gt;

&lt;p&gt;if (driversSnap.empty) {&lt;br&gt;
  // Expand to full city&lt;br&gt;
  // ... same query without zone_id filter&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// 2. Send FCM notification to available drivers&lt;br&gt;
// First driver to accept wins (handled by transaction in Driver app)&lt;br&gt;
const tokens = driversSnap.docs.map(d =&amp;gt; d.data().fcm_token);&lt;br&gt;
await admin.messaging().sendMulticast({&lt;br&gt;
  tokens,&lt;br&gt;
  data: { type: 'NEW_ORDER', order_id: context.params.orderId }&lt;br&gt;
});&lt;br&gt;
});&lt;br&gt;
On the driver side, accepting an order uses a Firestore transaction with three guards to prevent race conditions — only one driver can claim the order even if multiple tap Accept simultaneously.&lt;/p&gt;

&lt;p&gt;What's Coming&lt;br&gt;
Zesto is launching soon on Gumroad and Codester as commercial source code — complete, ready-to-customize. It includes all 4 Flutter apps, the complete Cloud Functions backend, Firestore security rules, and developer documentation.&lt;br&gt;
Try the customer app right now:&lt;br&gt;
&lt;a href="https://github.com/ssurekumar01111-hue/zesto-demo/releases/download/v1.0-beta/app-release.apk" rel="noopener noreferrer"&gt;https://github.com/ssurekumar01111-hue/zesto-demo/releases/download/v1.0-beta/app-release.apk&lt;/a&gt;&lt;br&gt;
I'd genuinely love your feedback — what works well, what feels off, what you'd build differently. Drop a comment or reach me at Emails are not allowed.&lt;/p&gt;

&lt;p&gt;Surendra Kumar — Flutter/Firebase developer, Banda, India&lt;br&gt;
Portfolio: portfolio.gfood.in | GitHub: ssurekumar01111-hue&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>firebase</category>
      <category>dart</category>
      <category>mobile</category>
    </item>
    <item>
      <title>I Built a Complete On-Demand Home Services Platform in Flutter — 4 Apps, Live Tracking, Real Payments. Here's Everything Inside.</title>
      <dc:creator>Surendra Kumar</dc:creator>
      <pubDate>Fri, 24 Apr 2026 07:20:03 +0000</pubDate>
      <link>https://forem.com/surendra_kumar_f2f7e31559/how-i-built-a-complete-on-demand-home-services-app-servenow-with-flutter-firebase-dj</link>
      <guid>https://forem.com/surendra_kumar_f2f7e31559/how-i-built-a-complete-on-demand-home-services-app-servenow-with-flutter-firebase-dj</guid>
      <description>&lt;p&gt;Most "marketplace app" tutorials stop at a login screen.&lt;br&gt;
I built the whole thing.&lt;br&gt;
Customer app. Provider app. Handyman app. Full Flutter Web admin panel. Real Razorpay payments. Real-time Google Maps tracking. Firebase Cloud Functions handling all the backend logic. Push notifications. Earnings tracking. Payout system. Chat.&lt;br&gt;
It took months of grinding as a self-taught developer from Banda, India.&lt;br&gt;
You can have it today for $55.&lt;br&gt;
👉 Get ServeNow Source Code on Gumroad&lt;/p&gt;

&lt;p&gt;What Is ServeNow?&lt;br&gt;
ServeNow is a production-ready, on-demand home services marketplace — think Urban Company or Thumbtack — built entirely with Flutter and Firebase.&lt;br&gt;
It's not a demo. It's not a tutorial project. It's a fully integrated system with signed APKs, complete documentation, and demo credentials — ready to deploy or hand off to a client.&lt;br&gt;
If you're a developer who wants to:&lt;/p&gt;

&lt;p&gt;Launch your own home services business in your city&lt;br&gt;
Sell a ready-made solution to a local client&lt;br&gt;
Skip 4–6 months of building and go straight to customizing&lt;/p&gt;

&lt;p&gt;...this is built for you.&lt;/p&gt;

&lt;p&gt;The 4 Apps — What Each One Does&lt;br&gt;
🧑‍💼 Customer App&lt;br&gt;
The end-user experience. Customers can:&lt;/p&gt;

&lt;p&gt;Browse services (beauty, cleaning, plumbing, electrical, and more)&lt;br&gt;
Book appointments with real-time slot selection&lt;br&gt;
Track their service provider live on Google Maps&lt;br&gt;
Chat directly with their provider&lt;br&gt;
Pay securely via Razorpay&lt;br&gt;
Rate and review after the job&lt;/p&gt;

&lt;p&gt;🏢 Provider App&lt;br&gt;
For the businesses listing their services:&lt;/p&gt;

&lt;p&gt;Accept and manage incoming bookings&lt;br&gt;
Update service offerings and pricing&lt;br&gt;
Track real-time earnings dashboard&lt;br&gt;
Request payouts directly from the app&lt;/p&gt;

&lt;p&gt;🔧 Handyman App&lt;br&gt;
For the on-ground service professionals:&lt;/p&gt;

&lt;p&gt;Broadcast job matching system — new jobs are pushed to nearby available handymen&lt;br&gt;
First to accept gets the job (race conditions handled via Firestore transactions)&lt;br&gt;
Onboarding and admin approval flow built in&lt;br&gt;
Earnings and job history tracking&lt;/p&gt;

&lt;p&gt;🖥️ Flutter Web Admin Panel&lt;br&gt;
The control center for the marketplace owner:&lt;/p&gt;

&lt;p&gt;Full user management (customers, providers, handymen)&lt;br&gt;
Booking oversight and status management&lt;br&gt;
Category and service configuration&lt;br&gt;
Payout request processing&lt;br&gt;
Analytics dashboard&lt;/p&gt;

&lt;p&gt;Tech Stack — Why I Chose Each One&lt;br&gt;
Flutter + Dart&lt;br&gt;
Four apps. One codebase. That's the core advantage.&lt;br&gt;
Flutter's native compilation keeps performance smooth across all four apps. Hot reload kept my development cycles fast. The widget library handled UI consistency without extra effort.&lt;br&gt;
Firebase (My backend of choice for indie development)&lt;br&gt;
Firestore is the heart of ServeNow's data layer. I went with denormalized data — embedding critical booking details directly in booking documents to minimize cross-collection reads. Subcollections handle nested data like chat messages.&lt;br&gt;
Cloud Functions (JavaScript) handle everything that shouldn't happen on the client:&lt;/p&gt;

&lt;p&gt;Razorpay webhook processing — payment callbacks update booking status server-side&lt;br&gt;
FCM push notification triggers — booking updates, new job broadcasts, chat alerts&lt;br&gt;
Earnings calculation and aggregation&lt;br&gt;
New provider/handyman approval flows&lt;/p&gt;

&lt;p&gt;Firebase Auth handles secure user management across all four apps.&lt;br&gt;
Firebase Storage manages profile images, service photos, and media.&lt;br&gt;
FCM (Firebase Cloud Messaging) powers real-time communication — from notifying handymen about new jobs to alerting customers when their pro is on the way.&lt;br&gt;
Razorpay&lt;br&gt;
Integrated as the primary payment gateway. Cloud Functions handle the success/failure webhook callbacks securely — no payment logic sits on the client.&lt;br&gt;
Google Maps&lt;br&gt;
Live handyman tracking, service area definition, job location visualization.&lt;br&gt;
RxDart&lt;br&gt;
Used for reactive state management — handling complex data streams cleanly, especially for real-time booking status updates across apps.&lt;/p&gt;

&lt;p&gt;The Hardest Problems I Solved&lt;br&gt;
Real-time synchronization across 4 apps&lt;br&gt;
When a handyman accepts a job, the customer sees it instantly. The provider app reflects the status change simultaneously. This required careful Firestore listener management and Cloud Function triggers firing in the right sequence.&lt;br&gt;
Job broadcasting without race conditions&lt;br&gt;
When a new booking arrives, eligible handymen in the area all get notified at once. Only one can accept. I used Firestore transactions inside Cloud Functions to handle this atomically — first write wins, rest get rejected cleanly.&lt;br&gt;
Secure payout flow&lt;br&gt;
Earnings are tracked in real-time in Firestore. Providers and handymen request payouts through the app. The admin panel surfaces these requests for processing. All balance updates go through Cloud Functions to prevent client-side manipulation.&lt;br&gt;
Building for production, not demos&lt;br&gt;
The architecture was designed to scale — Firestore's horizontal scaling and serverless Cloud Functions mean you're not re-engineering the backend when you get real users.&lt;/p&gt;

&lt;p&gt;What's Included&lt;br&gt;
✅ Full source code for all 4 Flutter apps (Customer, Provider, Handyman, Admin Web)&lt;br&gt;
✅ Firebase Cloud Functions (Node.js) — production-ready&lt;br&gt;
✅ 16-page developer documentation (v2.0) — setup to deployment&lt;br&gt;
✅ Signed APKs for immediate testing&lt;br&gt;
✅ Demo credentials for all user roles&lt;br&gt;
✅ Razorpay integration (test + live mode ready)&lt;br&gt;
✅ Google Maps integration&lt;br&gt;
✅ Complete Firebase security rules&lt;/p&gt;

&lt;p&gt;Who This Is For&lt;/p&gt;

&lt;p&gt;Flutter freelancers — sell this to a local business client, charge ₹50K–₹2L for customization&lt;br&gt;
Entrepreneurs — launch a home services marketplace in your city without building from scratch&lt;br&gt;
Developers — study a real production-grade multi-app Firebase architecture&lt;br&gt;
Agencies — white-label and deploy for clients&lt;/p&gt;

&lt;p&gt;Pricing&lt;br&gt;
$55 one-time — no subscriptions, no royalties, full source code ownership.&lt;br&gt;
Compare that to 4–6 months of development time at any reasonable hourly rate.&lt;br&gt;
👉 Get ServeNow on Gumroad — $55&lt;br&gt;
Also available on Codester.&lt;/p&gt;

&lt;p&gt;About Me&lt;br&gt;
I'm Surendra Kumar — a self-taught Flutter and Firebase developer from Banda, Uttar Pradesh. I build production-grade multi-app ecosystems and sell the source code for other developers and entrepreneurs to launch with.&lt;br&gt;
This is real work, built by a real indie developer who ships.&lt;/p&gt;

&lt;p&gt;GitHub: ssurekumar01111-hue&lt;br&gt;
LinkedIn: surendra-kumar-58320145&lt;br&gt;
Email: &lt;a href="mailto:ssure.kumar01111@gmail.com"&gt;ssure.kumar01111@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Questions about the code, architecture, or customization? Drop them in the comments — I reply to everything.&lt;br&gt;
👉 Get ServeNow Source Code — $55&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>firebase</category>
      <category>dart</category>
      <category>mobiledev</category>
    </item>
  </channel>
</rss>
