Feature spec
BeginnerCategory
vertical-tools
Build with AI
2–4 hours with FlutterFlow; 3–5 hours with Lovable (web version)
Custom build
3–7 days custom dev
Running cost
$0/mo up to 1K users; $25–50/mo at 10K users
Works on
Everything it takes to ship a Digital Business Card Generator — parts, prompts, and real costs.
A digital business card generator needs four pieces: a form that captures contact details, a live preview widget that mirrors input in real time, a QR code linking to a public card URL, and a one-tap share mechanism (NFC on Android, share sheet on iOS). With FlutterFlow you can ship in 2–4 hours for $0/month — QR generation with qr_flutter is free, and Supabase's free tier handles card storage easily. NFC write requires a Custom Action on the FlutterFlow Pro plan.
What a Digital Business Card Generator Actually Is
A digital business card generator lets users build a shareable contact profile within your app: they fill in their name, title, company, social links, and a bio, see a live preview of how the card looks, and get a QR code and NFC tap that others can use to view and save their details. The value over a static PDF is the live URL — when the user updates their job title, every QR scan reflects the change immediately. The real product decisions are how to structure the public card URL (UUID slug vs vanity handle), what 'save to contacts' means on each platform (vCard download vs native contacts API), and how to make NFC work across the Android-writes / iOS-reads divide.
What users consider table stakes in 2026
- Live card preview that updates character-by-character as the user types — no save button required to see the result
- QR code visible on the card itself, large enough to scan from 20cm, linking to the public card URL
- One-tap NFC share on Android that writes the card URL to an NFC tag instantly
- Share sheet button that composes a native iOS or Android share with the card URL and a short introduction message
- Multiple color themes or card templates the user can switch between with a single tap
- View count displayed on the card owner's dashboard so they know how many people have opened their card
Anatomy of the Feature
Six components — the QR code generation and NFC layer are free; the live preview state management is where FlutterFlow builds most often stall.
Card editor form
UIFlutter Form with TextFormField inputs for display_name, job_title, company, email, phone, website, LinkedIn URL, Twitter/X handle, Instagram handle, and bio. Validation runs on change (not on submit) so errors appear while the user is typing. Avatar upload uses image_picker to select a photo and uploads it to Supabase Storage.
Note: Use TextEditingController bound to a ValueNotifier for live preview — App State variable updates trigger full widget rebuilds which feel laggy on fast typists.
Live card preview widget
UIA Flutter Container styled to match the selected template renders name, title, company, avatar, and social icons in real time using ValueListenableBuilder subscribed to the ValueNotifier. Theme color applied with ColorScheme.fromSeed. Card proportions match standard business card ratio (3.5:2 inches at 88:51mm).
Note: Render the preview as a Stack so the QR code widget overlays the bottom corner of the card — this gives an accurate at-a-glance view of how the physical or screenshot card will look.
QR code generation
UIqr_flutter package renders a QR code Flutter Widget from the card's public URL string (e.g., https://app.com/card/{card_id}). No API call required — QR generation is 100% client-side. ScalableImage provides SVG export for high-resolution printing. QR error correction level H handles a partially obscured code.
Note: The QR encodes the public URL, not raw vCard data — this way the card stays current when details change. Raw vCard QRs become stale the moment a user changes phone number.
NFC sharing layer
Serviceflutter_nfc_kit package writes the card URL as an NDEF Text record to an NFC tag on Android. iOS supports NFC read (Core NFC) since iOS 13, but NFC write requires Core NFC background tag writing which needs a special Apple entitlement — provide the share sheet as the iOS primary share path. Check NfcManager.instance.isAvailable() before showing the NFC button.
Note: NFC write is declared in AndroidManifest.xml (android.permission.NFC), not as a runtime permission — no permission dialog appears. If the device has no NFC hardware, hide the button entirely.
Card public URL and data storage
DataEach card has a UUID primary key used as the public URL slug — uuid prevents slug collision without a uniqueness check at write time. Supabase SELECT RLS policy is public (no auth required) for active cards so anyone with the URL or QR can view the card without signing in. The view_count column is incremented via a Supabase RPC function on every public page load.
Note: Store the full production domain as a constant in your app, not derived from window.location — in development and on test devices the domain would otherwise resolve to localhost.
Contact save mechanism
ServiceOn the public card view page, the Save to Contacts button calls flutter_contacts to create a device contact from the card's fields (name, phone, email, company, website). Fallback: generate and download a .vcf file (vCard 3.0 format) which the device's native contacts app opens automatically — works on both platforms without the contacts permission.
Note: iOS requires NSContactsUsageDescription in Info.plist before flutter_contacts can request permission. The vCard fallback bypasses this requirement entirely and is often the better default for a public card viewer.
The data model
Run this in the Supabase SQL editor. The cards table has a public SELECT policy so anyone with the UUID can view a card — no sign-in required. The card_views table tracks view analytics per card.
1create table public.cards (2 id uuid primary key default gen_random_uuid(),3 user_id uuid references auth.users(id) on delete cascade not null,4 slug text unique not null default gen_random_uuid()::text,5 display_name text not null,6 job_title text,7 company text,8 email text,9 phone text,10 website text,11 linkedin_url text,12 twitter_url text,13 instagram_url text,14 bio text,15 avatar_url text,16 theme_color text not null default '#1a1a2e',17 template_id text not null default 'classic',18 is_active bool not null default true,19 view_count int not null default 0,20 created_at timestamptz not null default now(),21 updated_at timestamptz not null default now()22);2324create table public.card_views (25 id uuid primary key default gen_random_uuid(),26 card_id uuid references public.cards(id) on delete cascade not null,27 viewer_ip text,28 referrer text,29 viewed_at timestamptz not null default now()30);3132alter table public.cards enable row level security;33alter table public.card_views enable row level security;3435-- Anyone can view active cards (public card URL)36create policy "Active cards are publicly readable"37 on public.cards for select38 using (is_active = true);3940-- Only card owner can read all their cards (including inactive)41create policy "Owner can read all own cards"42 on public.cards for select43 using (auth.uid() = user_id);4445-- Only authenticated users can create cards46create policy "Authenticated users can create cards"47 on public.cards for insert48 with check (auth.uid() = user_id);4950-- Only card owner can update or delete51create policy "Owner can update own cards"52 on public.cards for update53 using (auth.uid() = user_id);5455create policy "Owner can delete own cards"56 on public.cards for delete57 using (auth.uid() = user_id);5859-- Card views: insert is public (no auth to track views); only owner can read their card's views60create policy "Anyone can record a card view"61 on public.card_views for insert62 with check (true);6364create policy "Card owner can read their card views"65 on public.card_views for select66 using (67 auth.uid() = (select user_id from public.cards where id = card_id)68 );6970-- RPC to increment view count atomically71create or replace function public.increment_card_view(card_id uuid)72returns void language plpgsql security definer as $$73begin74 update public.cards set view_count = view_count + 1 where id = card_id;75end;76$$;7778-- Trigger to update updated_at on card edit79create or replace function public.set_updated_at()80returns trigger language plpgsql as $$81begin82 new.updated_at = now();83 return new;84end;85$$;8687create trigger cards_set_updated_at88 before update on public.cards89 for each row execute function public.set_updated_at();9091-- Storage bucket for card avatars92-- Run this separately in Supabase Dashboard → Storage → New bucket93-- Bucket name: card-avatars | Public: true9495-- Index for fast owner card lookups96create index cards_user_id_idx on public.cards (user_id);97create index card_views_card_id_idx on public.card_views (card_id, viewed_at desc);Heads up: The increment_card_view RPC uses SECURITY DEFINER so unauthenticated public page visitors can increment the count without needing INSERT RLS on the cards table — the function runs as the Supabase postgres role.
Build it — pick your path
Each tool fits this feature differently. Switch tabs to compare fit, time, the exact steps, and a copy-paste prompt.
FlutterFlow is the best fit: Widget Tree maps directly to the card editor and preview layout, qr_flutter integrates as a Custom Widget, and Supabase storage connects natively. NFC write requires a Custom Action on the Pro plan.
Step by step
- 1Create a new FlutterFlow project with Supabase backend; paste the SQL schema above in the Supabase SQL editor to create the cards and card_views tables
- 2Build the card editor page: add TextFormField widgets for each card field and bind them to Page State variables; add a qr_flutter Custom Widget that reads the card's public URL from Page State
- 3Add a second column or Stack widget alongside the form inputs to serve as the live preview — bind its text and color properties to the same Page State variables
- 4Add a Save Card button that calls Supabase INSERT (cards table) with all Page State values and navigates to the card detail page on success
- 5For NFC write: add flutter_nfc_kit as a Custom Package and create a Custom Action that calls NfcManager.instance.startSession() writing an NDEF Text record with the card URL; test on a physical Android device with an NFC tag
- 6Enable Camera, Photos, and Contacts permissions in Settings → Permissions with descriptive strings; build via FlutterFlow's test build or deploy to TestFlight/Play Console for device testing
Where this path bites
- Live preview via FlutterFlow App State variables triggers full widget rebuilds and lags 1–2 keystrokes behind input — use a local StatefulWidget with ValueNotifier for smooth preview updates
- qr_flutter Custom Widget requires Pro plan ($70/mo) or must be implemented via a Custom Widget definition uploaded to FlutterFlow
- NFC write Custom Action and flutter_contacts Save to Contacts both require Pro plan; the vCard download fallback works on the free plan
Third-party services you'll need
A digital business card generator needs almost no external services — QR generation and NFC are free libraries, and Supabase covers card storage.
| Service | What it does | Free tier | Paid from |
|---|---|---|---|
| Supabase | Card data storage, Auth, public card view RLS policies, Storage for avatar images | 2 projects, 500MB DB, 1GB Storage | Pro $25/mo |
| Supabase Storage | Avatar image hosting for card profile photos, public read access | Included in Supabase tiers; 1GB free | $0.09/GB bandwidth after free tier (approx) |
| qr_flutter | Client-side QR code generation in Flutter — no API call, no cost, runs on-device | Free — open-source MIT license | Free |
Swipe the table sideways to see pricing.
What it costs to run
Drag through the tiers to see how your monthly bill scales with users — no surprises later.
Estimated monthly running cost
Supabase free tier handles 100 users with card data and avatar images well within 500MB DB and 1GB Storage limits. QR generation is client-side. No per-call service costs.
Estimates use verified 2026 pricing and assume typical usage per user. Your real bill depends on activity, storage, and third-party plans.
What breaks when AI tools build this
The failures people actually hit on their first build — the symptom, why it happens, and the exact fix.
FlutterFlow live preview lags 1–2 keystrokes behind input
Symptom: FlutterFlow App State variables are updated asynchronously and trigger a full widget rebuild on every change. When the card editor updates a display_name App State variable on each keystroke, the live preview widget re-renders from scratch including network image fetch for the avatar — this creates a visible stutter that makes the editor feel broken on fast typists.
Fix: Use a local StatefulWidget with TextEditingController and ValueNotifier instead of App State for the editor-to-preview data bridge. Wrap the preview widget in ValueListenableBuilder — it only rebuilds the preview subtree, not the entire page. Switch to App State only on Save.
NFC write fails silently on Android without availability check
Symptom: flutter_nfc_kit's NfcManager.instance.startSession() throws PlatformException if NFC hardware is unavailable or disabled in the device settings — but the error is swallowed silently in many FlutterFlow Custom Action implementations. The user taps the NFC button and nothing happens, with no feedback.
Fix: Call await NfcManager.instance.isAvailable() before showing the NFC button. If it returns false, hide the NFC option and show only the share sheet button. If it returns true but session start fails, catch the exception and show a snackbar: 'Enable NFC in your device settings to use tap sharing.'
QR code points to localhost in development builds
Symptom: AI tools frequently generate the public card URL by reading window.location.origin (web) or using a BuildConfig variable that defaults to http://localhost in debug mode. The QR code on a development build encodes localhost:3000/card/[id] — scanning it on another phone opens nothing.
Fix: Store the production base URL as a Supabase environment variable (or a compile-time constant in Flutter). Generate the public card URL as https://[PRODUCTION_DOMAIN]/card/[card_id] regardless of the build environment. Never derive the domain from the runtime host.
Slug collision from name-based slugs
Symptom: AI tools often generate card slugs from the user's name — john-doe, jane-smith. When a second John Doe creates a card, the slug insert fails with a unique constraint violation and the user sees a cryptic error. Worse, some implementations silently append a number (john-doe-2) without telling the user their vanity URL changed.
Fix: Use UUID v4 as the primary slug — uuid prevents collision without any uniqueness check at write time. If you want human-readable vanity URLs, offer them as an optional secondary field with explicit conflict handling: check uniqueness, show 'this handle is taken', and let the user pick another.
iOS contacts permission missing from Info.plist crashes the app
Symptom: flutter_contacts requires NSContactsUsageDescription in Info.plist before calling requestPermission(). FlutterFlow's auto-generated Info.plist often does not include this key. When the user taps 'Save to Contacts', iOS throws a privacy usage description exception and the app crashes immediately.
Fix: Add NSContactsUsageDescription to Info.plist via FlutterFlow project settings → iOS → Info.plist Entries: key NSContactsUsageDescription, value 'Used to save received business cards to your contacts.' This string appears in the iOS permission dialog. Alternatively, use the vCard download fallback which requires no permission.
Best practices
Use UUID as the card slug — it prevents collision entirely and eliminates the need for slug uniqueness checks at write time
Always encode the public card URL in the QR code, not raw vCard data — URL-based QR codes stay current when contact details change; vCard QRs go stale immediately
Check NFC hardware availability before rendering the NFC button — hiding an unavailable feature is better than showing a tap that does nothing
Provide the vCard (.vcf) download as a universal contacts fallback — it works on both iOS and Android without any permission and requires no native API
Store the production domain as a constant, never derived from the runtime host — QR codes and NFC tags written in development would encode localhost URLs
Use the increment_card_view Supabase RPC for atomic view count updates — a read-then-write from the client creates race conditions when multiple people open the same card simultaneously
Cache avatar images at the CDN layer (Cloudflare free plan) — public card pages with avatars can generate significant Supabase Storage bandwidth at scale
Show the user their own card as others see it (the public view page) before they share it — a one-click preview link builds confidence and reduces support tickets about 'my card looks wrong'
When You Need Custom Development
FlutterFlow ships a solid personal digital card in 2–4 hours. These are the signals you have grown past that:
- You need batch card creation for enterprise teams — 500 employee cards auto-populated from a corporate directory (Active Directory, Okta SCIM) requires SSO and a data sync pipeline
- Physical NFC card fulfillment is part of the product — printing a physical NFC card pre-programmed with the digital URL requires integration with a card printing vendor's fulfillment API
- An analytics dashboard showing per-card view trends, QR scan counts, and contact save rates needs a proper events pipeline beyond the simple view_count integer
- White-label the card platform for agencies — multiple brand workspaces with custom domains, logo upload, and template restrictions per brand require a workspace isolation layer
RapidDev builds these features for production
Real auth, database, and payments — fixed price, delivered in weeks. You own 100% of the code.
Frequently asked questions
Can I create a digital business card without an app?
Yes — a web app (Lovable path) generates QR codes and a public card URL that works in any browser. You lose NFC tap sharing and native contacts integration, but the core card-and-QR experience is fully functional as a PWA. The FlutterFlow native app path adds NFC write, native contacts saving, and a smoother live preview.
What is the best way to share a business card on mobile?
On Android: NFC tap is the fastest — the recipient holds their phone near yours and the card URL opens automatically. On iOS: the share sheet (share_plus) sends the card URL via iMessage, AirDrop, WhatsApp, or email in one tap — iOS NFC write requires a special Apple entitlement that most consumer apps do not have. Show both options when available and default to share sheet on iOS.
How does NFC business card sharing work on iPhone?
iPhones can read NFC tags (since iPhone 7, iOS 11) but cannot write them from a third-party app without the Apple Core NFC background tag writing entitlement — this requires explicit Apple approval. In practice, the best iOS sharing experience is the share sheet or AirDrop. If your product specifically needs iOS NFC write, pursue the entitlement via the Apple developer program; it takes several weeks.
Can people view my card without downloading the app?
Yes — the card lives at a public URL (e.g., https://yourapp.com/card/[id]) with a public Supabase SELECT policy that requires no authentication to view. Anyone who scans the QR code or taps the NFC tag sees the card in their browser instantly. iOS App Clip (custom build only) lets recipients save the card without even downloading the full app.
How do I add a QR code to my digital business card?
Use the qr_flutter package in Flutter (FlutterFlow path) or qrcode.react in a React app (Lovable web path). Pass the card's public URL as the data string — not raw contact information. The QR code points to the live URL, so when you update your email or phone number, every previously printed or shared QR code reflects the change on next scan.
Can I have multiple business cards for different roles?
Yes — each card is a separate row in the cards table with its own UUID slug and settings. A user can have a personal card, a consulting card, and a side project card, each with its own theme, template, and contact details. The My Cards page lists all active cards and lets the user switch between them. Each card gets its own QR code.
How do I track who viewed my digital business card?
The view_count column increments atomically via a Supabase RPC function on every public page load — this gives a total scan count per card. The card_views table records each view with a timestamp and optional referrer so you can see view trends over time. For specific viewer identification (who exactly viewed your card), you would need to require sign-in on the public view page — which most card products deliberately avoid to reduce friction.
What is a vCard and how does it work?
A vCard (.vcf file) is the universal standard format for contact data — all phones and email clients can import it. When you generate a .vcf file with a user's name, phone, email, company, and website and serve it as a download, the recipient's device asks if they want to add it to their contacts. It works without any API, any permission (on the reader's side), and any app — making it the most reliable 'save to contacts' mechanism across all platforms.
Need this feature production-ready?
RapidDev builds a digital business card generator into real apps — auth, database, payments — at $13K–$25K.
Book a free consultation30-min call. No commitment.