# How to Build an Auction or Bidding System in FlutterFlow

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 25-35 min
- Compatibility: FlutterFlow Free+ (Cloud Functions required for bid validation)
- Last updated: March 2026

## TL;DR

Build a live auction system using a Firestore auctions collection with currentBid, currentBidderId, endTime, and a bids subcollection logging every bid. Display auctions with real-time countdown timers, validate bids server-side in a Cloud Function using Firestore transactions to prevent race conditions, and end auctions automatically with a scheduled Cloud Function that sets the status to ended and notifies the winner.

## Live auction platform with real-time bidding and countdown timers

An auction system needs real-time updates, countdown pressure, and bulletproof bid validation. This tutorial builds one in FlutterFlow: a Firestore collection stores auction items with current bid and end time, a bids subcollection logs every bid, countdown timers create urgency, a Cloud Function validates bids atomically to prevent two simultaneous bids from overwriting each other, and a scheduled function ends auctions and notifies winners. The result is a functional live auction platform.

## Before you start

- A FlutterFlow project with Firebase/Firestore connected
- Firebase Authentication enabled with user sign-in working
- Cloud Functions enabled for bid validation and auction ending
- Understanding of Custom Functions, Action Flows, and real-time Backend Queries

## Step-by-step guide

### 1. Create the Firestore data model for auctions and bids

Create an auctions collection with fields: title (String), description (String), imageUrl (String), startingPrice (Double), currentBid (Double, initially same as startingPrice), currentBidderId (String, initially empty), minIncrement (Double: minimum amount above current bid, e.g., 5.00), endTime (Timestamp), status (String: 'active', 'ended'), bidCount (Integer, default 0), and sellerId (String). Under each auction, create a bids subcollection with fields: userId (String), displayName (String), amount (Double), and timestamp (Timestamp). Set Firestore rules: any authenticated user can read auctions and bids, but only Cloud Functions can write to the currentBid, currentBidderId, and status fields (to prevent client manipulation). Users can read but not directly write auction documents.

**Expected result:** Firestore has auctions and bids subcollection with proper fields and security rules restricting writes to Cloud Functions.

### 2. Build the auction listing page with countdown timers

Create an AuctionsPage with a ListView bound to a Backend Query on auctions where status equals 'active', ordered by endTime ascending (ending soonest first). Set Single Time Query to OFF for real-time updates so new bids update the currentBid display live. Each auction card Component shows: Image (imageUrl), title Text, current bid Text (bold, large, prefixed with currency symbol), bidCount Text ('12 bids'), and a countdown timer. For the countdown, create a Custom Function named getTimeRemaining that takes endTime Timestamp and returns a formatted string like '2h 15m 30s' or 'Ending soon!' when under 5 minutes. To update every second, use a Timer Custom Action that re-calculates the countdown every 1000ms and updates a Component State variable. Style items ending within 5 minutes with a red border for urgency. Tap navigates to AuctionDetailPage.

**Expected result:** Active auctions display with real-time current bids and countdown timers that tick every second. Items ending soon are highlighted.

### 3. Build the auction detail page with bid history and place bid form

Create an AuctionDetailPage receiving the auction document reference. Display the item image as a large hero, title, description, seller info, and prominent current bid display. Set the auction doc Backend Query to real-time (Single Time Query OFF) so the current bid updates live when other users bid. Below, add a bid history section: ListView bound to auctions/{auctionId}/bids ordered by timestamp descending, limit 10. Each row shows the bidder's displayName, bid amount, and relative timestamp. At the bottom, add a sticky Container with: a TextField for the bid amount (number keyboard, hint showing minimum bid: currentBid + minIncrement), and a Place Bid button. The button is disabled (Conditional Enabling) when: the auction has ended (status != 'active'), the user is the seller, or the entered amount is less than currentBid + minIncrement.

**Expected result:** The auction detail page shows real-time bid updates, scrollable bid history, and a validated bid input form.

### 4. Validate and place bids through a Cloud Function with Firestore transaction

Create a callable Cloud Function named placeBid that receives auctionId and bidAmount. The function uses a Firestore transaction to: (1) read the current auction document; (2) verify status is 'active'; (3) verify endTime is in the future; (4) verify bidAmount > currentBid + minIncrement; (5) verify the bidder is not the seller; (6) atomically update currentBid, currentBidderId, and increment bidCount; (7) create a bid document in the bids subcollection. The transaction ensures that if two users bid simultaneously, only the valid higher bid succeeds and the other gets an error. In FlutterFlow, wire the Place Bid button to call this Cloud Function via Backend Call. On success, show a SnackBar confirming the bid. On error, show the error message (e.g., 'Someone outbid you' or 'Auction has ended').

```
// Cloud Function: placeBid
const functions = require('firebase-functions');
const admin = require('firebase-admin');

exports.placeBid = functions.https.onCall(async (data, context) => {
  if (!context.auth) throw new functions.https.HttpsError('unauthenticated', 'Login required');
  const { auctionId, bidAmount } = data;
  const uid = context.auth.uid;

  const auctionRef = admin.firestore().collection('auctions').doc(auctionId);
  const userDoc = await admin.firestore().collection('users').doc(uid).get();
  const displayName = userDoc.data()?.displayName || 'Anonymous';

  await admin.firestore().runTransaction(async (txn) => {
    const auction = await txn.get(auctionRef);
    if (!auction.exists) throw new functions.https.HttpsError('not-found', 'Auction not found');
    const d = auction.data();
    if (d.status !== 'active') throw new functions.https.HttpsError('failed-precondition', 'Auction has ended');
    if (d.endTime.toDate() < new Date()) throw new functions.https.HttpsError('failed-precondition', 'Auction has ended');
    if (uid === d.sellerId) throw new functions.https.HttpsError('failed-precondition', 'Sellers cannot bid on own items');
    if (bidAmount <= d.currentBid + d.minIncrement) {
      throw new functions.https.HttpsError('failed-precondition',
        `Bid must be at least $${(d.currentBid + d.minIncrement).toFixed(2)}`);
    }
    txn.update(auctionRef, {
      currentBid: bidAmount,
      currentBidderId: uid,
      bidCount: admin.firestore.FieldValue.increment(1),
    });
    txn.create(auctionRef.collection('bids').doc(), {
      userId: uid, displayName, amount: bidAmount,
      timestamp: admin.firestore.FieldValue.serverTimestamp(),
    });
  });
  return { success: true };
});
```

**Expected result:** Bids are validated atomically via Firestore transaction. Simultaneous bids resolve correctly with only the higher bid winning.

### 5. Automatically end auctions and notify winners

Create a scheduled Cloud Function named endAuctions that runs every minute (or every 5 minutes). It queries auctions where status equals 'active' and endTime is less than or equal to now. For each expired auction, update status to 'ended'. If currentBidderId is not empty (someone bid), create a notification document for the winner at users/{currentBidderId}/notifications with title 'You won!', body including the item title and winning bid amount. Also notify the seller that their item sold. If no one bid (currentBidderId is empty), notify the seller that the auction ended without bids. In the FlutterFlow UI, add Conditional Visibility on the Place Bid section: hide when status equals 'ended'. Show a 'Winner' banner Container when status is 'ended' and currentBidderId is not empty, displaying the winner's name and winning bid.

**Expected result:** Auctions end automatically when their time expires. Winners and sellers receive notifications. The UI updates to show the final result.

## Complete code example

File: `Auction System Architecture`

```text
Firestore Data Model:
├── auctions/{auctionId}
│   ├── title: String ("Vintage Guitar")
│   ├── description: String
│   ├── imageUrl: String
│   ├── startingPrice: Double (100.00)
│   ├── currentBid: Double (275.00)
│   ├── currentBidderId: String ("uid_xyz")
│   ├── minIncrement: Double (5.00)
│   ├── endTime: Timestamp (2026-03-30 18:00 UTC)
│   ├── status: String ("active" | "ended")
│   ├── bidCount: Integer (12)
│   ├── sellerId: String
│   └── bids/{bidId}  (subcollection)
│       ├── userId: String
│       ├── displayName: String ("JaneDoe")
│       ├── amount: Double (275.00)
│       └── timestamp: Timestamp

Firestore Rules:
match /auctions/{auctionId} {
  allow read: if request.auth != null;
  allow write: if false;  // Only Cloud Functions
}
match /auctions/{auctionId}/bids/{bidId} {
  allow read: if request.auth != null;
  allow write: if false;  // Only Cloud Functions
}

Auction Listing Page:
└── ListView (auctions, status: active, orderBy endTime ASC, real-time)
    └── AuctionCard Component
        ├── Image (imageUrl)
        ├── Text (title)
        ├── Text (currentBid, large, bold, "$275.00")
        ├── Text (bidCount, "12 bids")
        ├── Text (countdown: "2h 15m 30s", updated every 1s)
        │   └── Red styling when < 5 minutes
        └── On Tap → Navigate AuctionDetailPage

Auction Detail Page:
├── Image (hero, large)
├── Text (title + description)
├── Text (currentBid, real-time, large)
├── Text (countdown timer)
├── ListView (bid history, last 10, newest first)
│   └── Row → Text(displayName) + Text(amount) + Text(timeAgo)
├── Sticky Bottom Container [Cond. Vis: status == active]
│   ├── TextField (bid amount, min: currentBid + minIncrement)
│   └── Button ("Place Bid")
│       └── On Tap → Backend Call: placeBid Cloud Function
└── Winner Banner [Cond. Vis: status == ended && currentBidderId != '']
    └── Text ("Won by {name} for ${currentBid}")

Cloud Functions:
├── placeBid (callable): Firestore transaction validation + write
└── endAuctions (scheduled, every 1 min): close expired + notify
```

## Common mistakes

- **Updating currentBid without a Firestore transaction so two simultaneous bids can overwrite each other** — If two users bid at the same moment, both read the same currentBid (e.g., $100). User A bids $110, User B bids $105. Without a transaction, the last write wins, potentially setting currentBid to $105 even though $110 was placed first. The higher bid is lost. Fix: Use a Firestore transaction in the Cloud Function. The transaction reads currentBid, verifies the new bid exceeds it by the minimum increment, and writes atomically. If another bid was placed between the read and write, the transaction retries automatically.
- **Letting clients update auction documents directly instead of through Cloud Functions** — Users can call the Firestore API and set currentBid to any value, or set their own userId as currentBidderId without actually placing a valid bid. This completely compromises auction integrity. Fix: Set Firestore rules to block all client writes on auction documents: allow write: if false. Route all bid submissions through the placeBid Cloud Function that validates every rule before writing.
- **Using a client-side timer for auction ending instead of a server-side scheduled function** — If no user has the auction page open when endTime passes, the auction never ends. Or if a user's device clock is wrong, they see incorrect countdown times and may bid on an expired auction. Fix: Use a scheduled Cloud Function that runs every minute, querying active auctions where endTime has passed. Server timestamps are authoritative. The client countdown is just for display; the Cloud Function is the actual enforcement.

## Best practices

- Validate all bids in a Cloud Function with a Firestore transaction to prevent race conditions
- Block all direct client writes to auction documents via Firestore Security Rules
- Use real-time Backend Queries (Single Time Query OFF) for live bid updates without page refresh
- Run a scheduled Cloud Function to end expired auctions authoritatively
- Display a countdown timer updated every second for urgency, but enforce timing server-side
- Notify both the winner and seller when an auction ends via in-app notifications
- Store bidder displayName on each bid document to avoid N+1 user doc lookups in bid history

## Frequently asked questions

### How do I build the countdown timer that updates every second?

Create a Custom Function getTimeRemaining that takes the auction's endTime and returns a formatted string. Use a Timer Custom Action (periodic, 1000ms interval) that recalculates the countdown every second and updates a Component State variable. The Text widget binds to this variable and re-renders each tick.

### What happens if no one bids on an auction?

The endAuctions Cloud Function checks if currentBidderId is empty after setting status to ended. If empty, it notifies the seller that the auction ended without bids. The UI shows 'No bids' instead of a winner banner. The seller can choose to relist the item.

### Can I add a 'Buy Now' price alongside bidding?

Yes. Add a buyNowPrice field on the auction document. Show a Buy Now button that calls a Cloud Function which sets currentBid to buyNowPrice, currentBidderId to the buyer, and status to ended in a transaction. This immediately closes the auction.

### How do I extend the auction if a bid comes in during the last minute?

In the placeBid Cloud Function, after a successful bid, check if endTime minus now is less than 60 seconds. If so, extend endTime by 2 minutes. This prevents auction sniping where users wait until the last second to bid.

### How do I handle payment after the auction ends?

When the endAuctions function closes an auction with a winner, create a pending_payment document with auctionId, winnerId, amount, and status pending. Send the winner a notification with a Pay Now link that initiates a Stripe Checkout session for the winning bid amount.

### Can RapidDev help build a full auction marketplace?

Yes. A production auction platform needs escrow payments, seller verification, dispute resolution, shipping integration, bid increment rules by price range, auction scheduling, and fraud detection. RapidDev can architect the full platform with secure Cloud Functions and payment integrations.

---

Source: https://www.rapidevelopers.com/flutterflow-tutorials/how-to-build-an-auction-or-bidding-system-in-flutterflow
© RapidDev — https://www.rapidevelopers.com/flutterflow-tutorials/how-to-build-an-auction-or-bidding-system-in-flutterflow
