<?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>DEV Community: Surendra Kumar</title>
    <description>The latest articles on DEV Community by Surendra Kumar (@surendra_kumar_f2f7e31559).</description>
    <link>https://dev.to/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>DEV Community: Surendra Kumar</title>
      <link>https://dev.to/surendra_kumar_f2f7e31559</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/surendra_kumar_f2f7e31559"/>
    <language>en</language>
    <item>
      <title>I built a free AI writing SaaS with Flutter Web + Gemini — would love feedback from this community</title>
      <dc:creator>Surendra Kumar</dc:creator>
      <pubDate>Sun, 17 May 2026 08:17:55 +0000</pubDate>
      <link>https://dev.to/surendra_kumar_f2f7e31559/i-built-a-free-ai-writing-saas-with-flutter-web-gemini-would-love-feedback-from-this-community-1c0l</link>
      <guid>https://dev.to/surendra_kumar_f2f7e31559/i-built-a-free-ai-writing-saas-with-flutter-web-gemini-would-love-feedback-from-this-community-1c0l</guid>
      <description>&lt;p&gt;Hey Dev.to Community&lt;/p&gt;

&lt;p&gt;I've been a Flutter/Firebase developer for the past&lt;br&gt;
couple of years, and I recently shipped something I'm genuinely&lt;br&gt;
proud of — ScribeAI, a free AI-powered writing platform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://scribeai-f7645.web.app" rel="noopener noreferrer"&gt;https://scribeai-f7645.web.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's what it does — 8 tools in one platform:&lt;/p&gt;

&lt;p&gt;ATS Resume Scorer — paste your resume + job description,&lt;br&gt;
get an instant compatibility score, missing keywords, and&lt;br&gt;
improvement suggestions&lt;/p&gt;

&lt;p&gt;✉️ Cover Letter Generator — tailored cover letters matched&lt;br&gt;
to any job description&lt;/p&gt;

&lt;p&gt;Document Summarizer — turn long contracts and reports into&lt;br&gt;
clean structured summaries&lt;/p&gt;

&lt;p&gt;Blog &amp;amp; SEO Writer — full SEO-optimized blog posts from&lt;br&gt;
just a topic and keywords&lt;/p&gt;

&lt;p&gt;✏️ Grammar &amp;amp; Tone Fixer — fix errors and adjust tone&lt;br&gt;
(professional, casual, formal)&lt;/p&gt;

&lt;p&gt;Paraphrasing Tool — rewrite text in multiple styles&lt;br&gt;
and variations&lt;/p&gt;

&lt;p&gt;LinkedIn Bio Writer — optimized About section that&lt;br&gt;
attracts recruiters&lt;/p&gt;

&lt;p&gt;Interview Q&amp;amp;A Generator — role-specific questions and&lt;br&gt;
model answers&lt;/p&gt;

&lt;p&gt;️ Tech stack for those curious:&lt;/p&gt;

&lt;p&gt;Flutter Web (single codebase, runs in any browser)&lt;br&gt;
Google Gemini AI API for all 8 tools&lt;br&gt;
Firebase Auth + Firestore for backend&lt;br&gt;
BLoC state management&lt;br&gt;
Razorpay for payments&lt;br&gt;
Deployed on Firebase Hosting&lt;br&gt;
Monetization: Free tier (3 uses/day) → Credits (₹99 = 20)&lt;br&gt;
→ Pro (₹199/month unlimited)&lt;/p&gt;

&lt;p&gt;Other features: PDF download, copy to clipboard, share&lt;br&gt;
results as public link, usage history, (signup needed to try)&lt;/p&gt;

&lt;p&gt;This started as a side project to prove that a Indie-developer can build and ship a real SaaS product without&lt;br&gt;
a team or funding.&lt;/p&gt;

&lt;p&gt;I'd genuinely love feedback from this community on:&lt;/p&gt;

&lt;p&gt;Is the UI/UX intuitive? Anything confusing?&lt;br&gt;
Which tool did you find most useful?&lt;br&gt;
What feature would make you actually use this regularly?&lt;br&gt;
Any bugs you spotted?&lt;br&gt;
Would you pay for the Pro plan? If not, why?&lt;br&gt;
Be as brutal as you want — I can handle it and I'll&lt;br&gt;
actually fix things based on your feedback&lt;/p&gt;

&lt;p&gt;Thanks for checking it out!&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>firebase</category>
      <category>gemini</category>
    </item>
    <item>
      <title>How I Built a Complete Food Delivery Platform with Flutter and Firebase</title>
      <dc:creator>Surendra Kumar</dc:creator>
      <pubDate>Sun, 10 May 2026 10:30:28 +0000</pubDate>
      <link>https://dev.to/surendra_kumar_f2f7e31559/how-i-built-a-complete-food-delivery-platform-with-flutter-and-firebase-39o</link>
      <guid>https://dev.to/surendra_kumar_f2f7e31559/how-i-built-a-complete-food-delivery-platform-with-flutter-and-firebase-39o</guid>
      <description>&lt;p&gt;After months of building, I just released Zesto — a production-ready,&lt;br&gt;
multi-city food delivery platform inspired by Zomato and Swiggy.&lt;br&gt;
It consists of 4 Flutter apps backed by a Firebase backend with 23&lt;br&gt;
Cloud Functions. In this article I'll walk through the key architecture&lt;br&gt;
decisions I made along the way.&lt;/p&gt;

&lt;p&gt;What I Built&lt;/p&gt;

&lt;p&gt;Customer App — OTP login, restaurant discovery, cart, Razorpay payments, live driver tracking&lt;br&gt;
Driver App — automatic job assignment, step-by-step delivery slider, background GPS&lt;br&gt;
Restaurant App — live order dashboard, menu management, earnings&lt;br&gt;
Admin Panel — Flutter Web, multi-city, restaurant and driver management&lt;/p&gt;

&lt;p&gt;Demo: &lt;a href="https://youtube.com/shorts/rL2M5O5Tk50" rel="noopener noreferrer"&gt;https://youtube.com/shorts/rL2M5O5Tk50&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Architecture Overview&lt;br&gt;
All 4 apps share a single Firebase project. Each app is independent&lt;br&gt;
with its own package name and Firebase registration, but they all&lt;br&gt;
read and write to the same Firestore collections.&lt;br&gt;
Customer App  ─────┐&lt;br&gt;
Driver App    ─────┤──── Firebase (Firestore + Auth + FCM + Functions)&lt;br&gt;
Restaurant App ────┤&lt;br&gt;
Admin Panel   ─────┘&lt;br&gt;
This approach kept things simple — no separate backends, no API&lt;br&gt;
servers to manage. Just Firestore listeners and Cloud Functions.&lt;/p&gt;

&lt;p&gt;State Management: MobX&lt;br&gt;
I chose MobX over BLoC or Riverpod for one reason — the reaction&lt;br&gt;
system. When the driver's order status changes in Firestore, I need&lt;br&gt;
the UI to react at multiple levels simultaneously:&lt;/p&gt;

&lt;p&gt;The map needs to update polylines&lt;br&gt;
The slider needs to show the next step&lt;br&gt;
The navigation stack needs to know whether to push a new screen&lt;/p&gt;

&lt;p&gt;MobX reactions handle this cleanly:&lt;br&gt;
dart_navigationReaction = reaction(&lt;br&gt;
  (_) =&amp;gt; _driverStore.activeJob,&lt;br&gt;
  (order) {&lt;br&gt;
    if (order != null &amp;amp;&amp;amp; !_isOnActiveDelivery &amp;amp;&amp;amp; mounted) {&lt;br&gt;
      _isOnActiveDelivery = true;&lt;br&gt;
      Navigator.push(context, _deliveryRoute());&lt;br&gt;
    }&lt;br&gt;
  },&lt;br&gt;
);&lt;br&gt;
One thing I learned the hard way — reactions fire on every observable&lt;br&gt;
change, not just null → non-null transitions. This caused the driver&lt;br&gt;
app to push a new ActiveDeliveryScreen on every status update, stacking&lt;br&gt;
screens on top of each other. The fix was a simple _isOnActiveDelivery&lt;br&gt;
guard flag.&lt;/p&gt;

&lt;p&gt;Order Status Flow&lt;br&gt;
The biggest consistency challenge was keeping order statuses in sync&lt;br&gt;
across 4 apps and 23 Cloud Functions. Early in development I had&lt;br&gt;
camelCase statuses in Flutter (arrivedAtRestaurant) and snake_case&lt;br&gt;
in Firestore (arrived_at_restaurant). This caused Firestore queries&lt;br&gt;
to silently return empty results.&lt;br&gt;
The fix was standardizing everything to snake_case and adding a safe&lt;br&gt;
parser that never throws:&lt;br&gt;
dartenum OrderStatus {&lt;br&gt;
  placed, confirmed, preparing, ready, accepted,&lt;br&gt;
  arrived_at_restaurant, picked_up, on_the_way,&lt;br&gt;
  arrived_at_customer, delivered, cancelled, refunded;&lt;/p&gt;

&lt;p&gt;static OrderStatus fromString(String? value) {&lt;br&gt;
    return OrderStatus.values.firstWhere(&lt;br&gt;
      (e) =&amp;gt; e.name == value,&lt;br&gt;
      orElse: () =&amp;gt; OrderStatus.placed,&lt;br&gt;
    );&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
The full status progression looks like this:&lt;br&gt;
placed → confirmed → preparing → ready → accepted&lt;br&gt;
→ arrived_at_restaurant → picked_up → on_the_way&lt;br&gt;
→ arrived_at_customer → delivered&lt;/p&gt;

&lt;p&gt;Driver Assignment Algorithm&lt;br&gt;
When a restaurant marks an order ready, the app calls the assignDriver&lt;br&gt;
Cloud Function. Here's how it finds the nearest available driver:&lt;/p&gt;

&lt;p&gt;Query zone_drivers/{cityId}/active/ — drivers who are online&lt;br&gt;
Filter out drivers with updated_at older than 10 minutes (stale)&lt;br&gt;
Calculate distance from each driver to the restaurant using&lt;br&gt;
Haversine formula on GeoPoint coordinates&lt;br&gt;
Assign the nearest driver&lt;br&gt;
Delete the driver from the active pool (prevents double assignment)&lt;br&gt;
Return success: false if no drivers are available&lt;/p&gt;

&lt;p&gt;The trickiest bug here was a race condition — the driver app was calling&lt;br&gt;
goOnline() before init() had finished reading the driver's real&lt;br&gt;
cityId from Firestore. So the driver registered under a hardcoded&lt;br&gt;
default city and was invisible to the assignment function.&lt;br&gt;
The fix was a simple initialization guard:&lt;br&gt;
dart@action&lt;br&gt;
Future goOnline(String driverUid) async {&lt;br&gt;
  if (!_isInitialized) {&lt;br&gt;
    int retries = 0;&lt;br&gt;
    while (!_isInitialized &amp;amp;&amp;amp; retries &amp;lt; 20) {&lt;br&gt;
      await Future.delayed(const Duration(milliseconds: 250));&lt;br&gt;
      retries++;&lt;br&gt;
    }&lt;br&gt;
  }&lt;br&gt;
  // now safe to use real cityId&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Security: Why Client-Side Validation Is Not Enough&lt;br&gt;
The most important backend fix was server-side price validation.&lt;br&gt;
The original placeOrder function trusted the grand_total sent&lt;br&gt;
by the client — meaning anyone could intercept the API call and&lt;br&gt;
order ₹1000 worth of food for ₹1.&lt;br&gt;
The fix was recalculating everything server-side:&lt;br&gt;
js// Never trust client prices&lt;br&gt;
for (const item of items) {&lt;br&gt;
  const menuItemSnap = await db&lt;br&gt;
    .collection('restaurants').doc(restaurant_id)&lt;br&gt;
    .collection('categories').doc(item.category_id)&lt;br&gt;
    .collection('items').doc(item.item_id)&lt;br&gt;
    .get();&lt;/p&gt;

&lt;p&gt;const menuItem = menuItemSnap.data();&lt;br&gt;
  calculatedSubtotal += menuItem.price * item.quantity;&lt;br&gt;
}&lt;br&gt;
// Use calculatedSubtotal — never data.grand_total&lt;br&gt;
I also locked down Firestore rules to prevent drivers from updating&lt;br&gt;
their own wallet_balance and customers from setting&lt;br&gt;
payment_status: paid without a real transaction.&lt;/p&gt;

&lt;p&gt;The Toughest Bug&lt;br&gt;
The driver delivery screen freezing after accepting an order took the&lt;br&gt;
longest to debug. The symptoms:&lt;/p&gt;

&lt;p&gt;Driver accepts order ✅&lt;br&gt;
Screen appears frozen on "Processing..." ❌&lt;br&gt;
Only fixed by clearing the app from memory and reopening&lt;/p&gt;

&lt;p&gt;The root cause was actually three separate bugs compounding:&lt;/p&gt;

&lt;p&gt;_updatePolylines() — an async method calling setState — was&lt;br&gt;
being called directly inside an Observer build method. Flutter&lt;br&gt;
suppresses secondary rebuilds to prevent infinite loops, causing&lt;br&gt;
the UI to freeze.&lt;br&gt;
The accepted status had no case in the delivery screen's switch&lt;br&gt;
statement, so it defaulted to "Processing..."&lt;br&gt;
The NewOrderOverlay dialog was never popped after acceptance,&lt;br&gt;
leaving an invisible backdrop blocking all touches.&lt;/p&gt;

&lt;p&gt;Each fix was simple in isolation — moving polylines to a MobX&lt;br&gt;
reaction, adding the accepted case, and calling&lt;br&gt;
Navigator.pop() on acceptance. But finding all three together&lt;br&gt;
took a full investigation pass.&lt;/p&gt;

&lt;p&gt;What I Would Do Differently&lt;br&gt;
Shared package for models — each app has its own copy of&lt;br&gt;
OrderModel and OrderStatus. A shared packages/core library&lt;br&gt;
would have prevented the status mismatch bugs entirely.&lt;br&gt;
Webhook-based payment confirmation — currently Razorpay payment&lt;br&gt;
success triggers order creation from the client. If the app crashes&lt;br&gt;
between payment and order creation, the customer loses money with no&lt;br&gt;
order. A proper webhook reconciliation flow would be more robust.&lt;br&gt;
iOS from day one — the driver app's foreground service needs&lt;br&gt;
separate configuration for iOS. Building Android-first and adding&lt;br&gt;
iOS later meant reworking the background location architecture.&lt;/p&gt;

&lt;p&gt;The Result&lt;br&gt;
A fully working food delivery platform that I've listed as source&lt;br&gt;
code for other developers to use as a starting point.&lt;br&gt;
🛒 Source code: &lt;a href="https://morningstar47jb.gumroad.com/l/tnevss" rel="noopener noreferrer"&gt;https://morningstar47jb.gumroad.com/l/tnevss&lt;/a&gt;&lt;br&gt;
Includes full SETUP.md, demo seeder, AppConfig for rebranding,&lt;br&gt;
and all 23 Cloud Functions.&lt;br&gt;
Happy to answer any questions about the architecture in the comments.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>firebase</category>
      <category>mobx</category>
    </item>
    <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://dev.to/surendra_kumar_f2f7e31559/building-a-zomato-like-multi-city-food-delivery-app-with-flutter-firebase-52bd</link>
      <guid>https://dev.to/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://dev.to/surendra_kumar_f2f7e31559/how-i-built-a-complete-on-demand-home-services-app-servenow-with-flutter-firebase-dj</link>
      <guid>https://dev.to/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>
