Create a real-time bidding experience with live Firestore listeners, animated bid counters, urgency-colored countdown timers, and anti-sniping logic. Set Single Time Query to OFF on the auction document so bids appear instantly for all users. A Cloud Function extends the auction by two minutes when a bid arrives in the last thirty seconds, preventing last-second sniping.
Building a Real-Time Auction Experience in FlutterFlow
Auction apps live or die by their real-time experience. This tutorial focuses on the live bidding UX: animated bid counters, urgency-colored countdowns, a horizontal bid ticker, sound effects on new bids, and anti-sniping protection. You will use Firestore real-time listeners and Cloud Functions to deliver an engaging auction experience.
Prerequisites
- A FlutterFlow project with Firebase authentication enabled
- Firestore database with an existing auctions collection (or create one in this tutorial)
- Cloud Functions enabled on the Firebase Blaze plan
- Basic familiarity with FlutterFlow Action Flows and Conditional Visibility
Step-by-step guide
Set up the Firestore schema for auctions and bids
Set up the Firestore schema for auctions and bids
Create an `auctions` collection with fields: title (String), description (String), imageUrl (String), startingPrice (Double), currentBid (Double), currentBidderId (String), endTime (Timestamp), status (String: active/ended), bidCount (Integer). Add a `bids` subcollection under each auction with fields: bidderId (String), bidderName (String), amount (Double), timestamp (Timestamp). In FlutterFlow, register both collections in the Data section. The currentBid and currentBidderId on the auction document enable a single real-time listener to show the latest bid without querying the bids subcollection.
Expected result: The auctions collection and bids subcollection are created and registered in FlutterFlow's Data panel.
Build the auction detail page with a real-time bid display
Build the auction detail page with a real-time bid display
Create an AuctionDetail page that receives an auctionId parameter. Add a Backend Query on the auctions collection for this document with Single Time Query set to OFF — this creates a real-time listener that fires whenever the document changes. Display the auction image in an Image widget, the title as a heading Text, and the current bid amount in a large bold Text widget. To animate bid changes, wrap the bid Text in a Container and use a Conditional Style: when the bid value changes, briefly flash the background color to green for 500ms using an Animated Container or Custom Widget with AnimatedSwitcher. Below the bid, show the current bidder name and total bid count.
Expected result: The auction page updates in real time when any user places a bid, with the bid amount visually flashing to indicate a new bid.
Create the countdown timer with urgency color transitions
Create the countdown timer with urgency color transitions
Add a Container below the bid display for the countdown timer. Use a Custom Widget that takes the endTime Timestamp as a parameter and runs a Timer.periodic every second. Calculate the remaining time: endTime - DateTime.now(). Display as 'Xh Xm Xs' in a Text widget. Change the Container background color based on remaining time: green when more than 5 minutes remain, yellow when between 1 and 5 minutes, red when under 1 minute, and pulsing red (animated opacity) when under 10 seconds. When the timer reaches zero, update the UI to show 'Auction Ended' and disable the bid button.
1// Custom Widget: AuctionCountdown2import 'dart:async';3import 'package:flutter/material.dart';45class AuctionCountdown extends StatefulWidget {6 final double width;7 final double height;8 final DateTime endTime;910 const AuctionCountdown({11 Key? key,12 required this.width,13 required this.height,14 required this.endTime,15 }) : super(key: key);1617 @override18 State<AuctionCountdown> createState() => _AuctionCountdownState();19}2021class _AuctionCountdownState extends State<AuctionCountdown> {22 Timer? _timer;23 Duration _remaining = Duration.zero;2425 @override26 void initState() {27 super.initState();28 _timer = Timer.periodic(29 const Duration(seconds: 1), (_) => _tick());30 _tick();31 }3233 void _tick() {34 final now = DateTime.now();35 setState(() {36 _remaining = widget.endTime.isAfter(now)37 ? widget.endTime.difference(now)38 : Duration.zero;39 });40 }4142 Color get _bgColor {43 if (_remaining.inMinutes >= 5) return Colors.green;44 if (_remaining.inMinutes >= 1) return Colors.orange;45 return Colors.red;46 }4748 @override49 void dispose() {50 _timer?.cancel();51 super.dispose();52 }5354 @override55 Widget build(BuildContext context) {56 final h = _remaining.inHours;57 final m = _remaining.inMinutes % 60;58 final s = _remaining.inSeconds % 60;59 return Container(60 width: widget.width,61 padding: const EdgeInsets.all(12),62 decoration: BoxDecoration(63 color: _bgColor,64 borderRadius: BorderRadius.circular(8),65 ),66 child: Text(67 _remaining == Duration.zero68 ? 'Auction Ended'69 : '${h}h ${m}m ${s}s',70 textAlign: TextAlign.center,71 style: const TextStyle(72 color: Colors.white,73 fontSize: 24,74 fontWeight: FontWeight.bold),75 ),76 );77 }78}Expected result: A countdown timer displays remaining time with color changes: green above 5 minutes, yellow under 5, red under 1, and pulsing red under 10 seconds.
Add the bid placement form with validation and Cloud Function
Add the bid placement form with validation and Cloud Function
Below the countdown, add a Row with a TextField (number input, hint text 'Enter bid amount') and a Button labeled 'Place Bid'. On button tap, validate that the entered amount is greater than the current bid plus a minimum increment (e.g., currentBid + 1.00). If invalid, show a SnackBar error. If valid, call a Cloud Function via an API Call that receives auctionId and bidAmount. The Cloud Function uses a Firestore transaction to read the current auction, verify the bid is still higher than currentBid, create a bid document in the subcollection, update currentBid and currentBidderId on the auction document, and increment bidCount. The transaction prevents race conditions where two users bid simultaneously.
Expected result: Users enter a bid amount, the Cloud Function validates and records it atomically, and the real-time listener updates all connected users instantly.
Implement anti-sniping logic in the Cloud Function
Implement anti-sniping logic in the Cloud Function
In the same Cloud Function that processes bids, after successfully recording the bid, check if the auction's endTime is within 30 seconds of the current server time. If so, extend endTime by 2 minutes using admin.firestore.Timestamp.fromDate(new Date(endTime.toDate().getTime() + 120000)). This anti-sniping rule gives all bidders a fair chance to respond to last-second bids. The real-time listener on the auction document will automatically update the countdown timer on all connected clients to reflect the new endTime.
Expected result: When a bid arrives in the last 30 seconds, the auction automatically extends by 2 minutes, and all users see the countdown reset to reflect the extension.
Add a horizontal bid history ticker and sound effects
Add a horizontal bid history ticker and sound effects
At the bottom of the auction page, add a horizontal ListView bound to the bids subcollection ordered by timestamp descending, limited to the last 20 bids. Each item is a compact Container showing the bidder name and amount in a Chip-like style. Set Single Time Query to OFF so new bids appear in the ticker instantly. For sound effects, create a Custom Action using the audioplayers package: on each new bid detected (listen to the auction document's bidCount field changing), play a short notification sound from an asset file. This gives the auction a lively, engaging feel.
Expected result: A scrolling horizontal ticker shows recent bids in real time, and a notification sound plays each time a new bid is placed.
Complete working example
1// Cloud Function: Place bid with anti-sniping2// Deploy: firebase deploy --only functions34const functions = require('firebase-functions');5const admin = require('firebase-admin');6admin.initializeApp();78exports.placeBid = functions.https.onCall(async (data, context) => {9 if (!context.auth) throw new functions.https.HttpsError(10 'unauthenticated', 'Must be logged in');1112 const { auctionId, bidAmount } = data;13 const db = admin.firestore();14 const auctionRef = db.collection('auctions').doc(auctionId);1516 return db.runTransaction(async (txn) => {17 const auctionDoc = await txn.get(auctionRef);18 const auction = auctionDoc.data();1920 // Validate auction is active21 const now = admin.firestore.Timestamp.now();22 if (auction.endTime.toMillis() < now.toMillis()) {23 throw new functions.https.HttpsError(24 'failed-precondition', 'Auction has ended');25 }2627 // Validate bid amount28 const minBid = auction.currentBid + 1.0;29 if (bidAmount < minBid) {30 throw new functions.https.HttpsError(31 'invalid-argument',32 `Bid must be at least $${minBid.toFixed(2)}`);33 }3435 // Record the bid36 const bidRef = auctionRef.collection('bids').doc();37 txn.set(bidRef, {38 bidderId: context.auth.uid,39 bidderName: context.auth.token.name || 'Anonymous',40 amount: bidAmount,41 timestamp: now,42 });4344 // Update auction document45 const updates = {46 currentBid: bidAmount,47 currentBidderId: context.auth.uid,48 bidCount: admin.firestore.FieldValue.increment(1),49 };5051 // Anti-sniping: extend by 2 min if bid in last 30s52 const endMs = auction.endTime.toMillis();53 const nowMs = now.toMillis();54 if (endMs - nowMs < 30000) {55 updates.endTime = admin.firestore.Timestamp56 .fromMillis(endMs + 120000);57 updates.extensionCount =58 admin.firestore.FieldValue.increment(1);59 }6061 txn.update(auctionRef, updates);62 return { success: true, newBid: bidAmount };63 });64});Common mistakes
Why it's a problem: Not implementing anti-sniping protection for auctions
How to avoid: In the Cloud Function, check if the bid arrives within 30 seconds of endTime. If so, extend endTime by 2 minutes to give all bidders a fair chance to respond.
Why it's a problem: Letting clients write bids directly to Firestore without a Cloud Function
How to avoid: Route all bids through a Cloud Function that uses a Firestore transaction to read the current state, validate, and write atomically.
Why it's a problem: Using Single Time Query ON for the auction document
How to avoid: Set Single Time Query to OFF on the Backend Query for the auction document so FlutterFlow creates a real-time listener that updates the UI instantly on every change.
Best practices
- Set Single Time Query to OFF on all auction-related queries for real-time updates
- Denormalize currentBid on the auction document to avoid subcollection queries on listing pages
- Use Firestore transactions in Cloud Functions for all bid operations to prevent race conditions
- Color-code the countdown timer to create urgency: green, yellow, red based on time remaining
- Show the minimum accepted bid amount near the input field to reduce invalid bid attempts
- Limit the bid history ticker to the last 20 bids to avoid excessive reads
- Play a brief sound on new bids to make the auction feel lively and engaging
- Log anti-sniping extensions on the auction document so admins can audit extended auctions
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I'm building a real-time auction app in FlutterFlow with Firestore. I need live bid updates via real-time listeners, a countdown timer that changes color based on urgency, anti-sniping logic that extends the auction by 2 minutes when bids arrive in the last 30 seconds, and a Cloud Function for atomic bid placement. Show me the Firestore schema and Cloud Function code.
Create an auction detail page with a large current bid display, a countdown timer that changes color from green to red as time runs out, a bid input field with a Place Bid button, and a horizontal scrolling list of recent bids.
Frequently asked questions
How does anti-sniping work in this auction system?
When a bid arrives within the last 30 seconds of the auction, the Cloud Function automatically extends the endTime by 2 minutes. This gives all bidders time to respond and prevents last-second sniping from dominating the auction.
Can I set a maximum number of anti-sniping extensions?
Yes. Track an extensionCount field on the auction document. In the Cloud Function, only extend if extensionCount is below your limit (e.g., 5 extensions maximum).
How do I handle the case where two users bid at the exact same time?
The Cloud Function uses a Firestore transaction that reads the current bid, validates, and writes atomically. If two bids arrive simultaneously, one transaction succeeds and the other retries with the updated state, ensuring consistency.
Can I add a reserve price that must be met for the auction to close?
Yes. Add a reservePrice field to the auction document. When the auction ends (via a scheduled Cloud Function), check if currentBid >= reservePrice. If not, set status to 'reserve_not_met' and notify the seller.
How do I notify the winning bidder when the auction ends?
Create a scheduled Cloud Function that checks for auctions where endTime has passed and status is still 'active'. Update status to 'ended' and send a push notification or email to the currentBidderId.
Can RapidDev help build a full auction marketplace platform?
Yes. RapidDev can implement a complete auction platform with seller dashboards, escrow payments, automated listing schedules, bid analytics, and mobile push notifications.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation