Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Sync User Data Across Devices in FlutterFlow

In FlutterFlow, Firestore IS your cross-device sync engine — data written to Firestore is instantly available on all devices signed in as the same user. The most common mistake is storing user preferences or app data in FlutterFlow's App State, which lives only in device memory. Store everything that should persist or sync in Firestore user documents. Enable offline persistence for smooth offline experiences.

What you'll learn

  • Why Firestore provides automatic cross-device sync and App State does not
  • Which data belongs in Firestore versus App State versus local storage
  • How to enable Firestore offline persistence for offline-first behavior
  • How to resolve write conflicts using Firestore atomic operations
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner10 min read30-45 minFlutterFlow Free+ (Firebase required)March 2026RapidDev Engineering Team
TL;DR

In FlutterFlow, Firestore IS your cross-device sync engine — data written to Firestore is instantly available on all devices signed in as the same user. The most common mistake is storing user preferences or app data in FlutterFlow's App State, which lives only in device memory. Store everything that should persist or sync in Firestore user documents. Enable offline persistence for smooth offline experiences.

Cross-Device Sync Is Already Built In

Many FlutterFlow developers ask 'how do I sync data across devices?' without realizing the answer is already in their app: any data in Firestore automatically appears on every device where the user is signed in, because Firestore is a cloud database with real-time listeners. The real question is whether you are storing data in the right place. This guide clarifies the three storage tiers in FlutterFlow and shows you exactly which data goes where.

Prerequisites

  • FlutterFlow project with Firebase and Firestore configured
  • Firebase Authentication set up with user accounts
  • Basic understanding of Firestore collections and documents
  • A user document in Firestore for each authenticated user

Step-by-step guide

1

Understand the Three Storage Tiers in FlutterFlow

FlutterFlow has three places to store data, each with different persistence and sync behavior. App State lives only in device memory — it is reset every time the app restarts and never synchronizes between devices. Page State is even more temporary — it is reset whenever the user navigates away from that page. Firestore is a cloud database — data persists permanently, syncs across all devices in real time, and survives app restarts. The decision rule is simple: if the data should exist tomorrow or on another device, it must go in Firestore. App State is only for temporary UI state that does not need to survive navigation (like whether a dropdown is open, or which tab is selected).

Expected result: You can categorize every piece of data in your app: UI state (App State), temporary form data (Page State), and persistent user data (Firestore).

2

Move User Preferences from App State to Firestore

Identify all App State variables that represent user preferences or settings: dark mode toggle, notification preferences, language selection, accessibility settings, default filters, and saved layouts. For each one, add a corresponding field to your users Firestore collection schema in FlutterFlow. On app startup (main page On Load action), fetch the current user document and populate App State from the Firestore values. When the user changes a preference, update both the App State variable (for immediate UI response) and the Firestore user document (for persistence and cross-device sync). This two-write pattern gives instant UI updates while ensuring persistence.

user_preferences.dart
1// Pattern: load user preferences from Firestore on app start
2// and save changes back to Firestore when updated
3import 'package:cloud_firestore/cloud_firestore.dart';
4import 'package:firebase_auth/firebase_auth.dart';
5
6// Load preferences on startup
7Future<Map<String, dynamic>> loadUserPreferences() async {
8 final uid = FirebaseAuth.instance.currentUser?.uid;
9 if (uid == null) return {};
10
11 final snap = await FirebaseFirestore.instance
12 .collection('users')
13 .doc(uid)
14 .get();
15
16 final data = snap.data() ?? {};
17 return {
18 'dark_mode': data['preferences']?['dark_mode'] ?? false,
19 'language': data['preferences']?['language'] ?? 'en',
20 'notifications_enabled': data['preferences']?['notifications_enabled'] ?? true,
21 'default_currency': data['preferences']?['default_currency'] ?? 'USD',
22 };
23}
24
25// Save a preference change
26Future<void> savePreference(String key, dynamic value) async {
27 final uid = FirebaseAuth.instance.currentUser?.uid;
28 if (uid == null) return;
29
30 await FirebaseFirestore.instance
31 .collection('users')
32 .doc(uid)
33 .update({'preferences.$key': value});
34}

Expected result: User toggles dark mode on their phone, then opens the app on their tablet — dark mode is already on without any additional action.

3

Enable Firestore Offline Persistence

Firestore offline persistence allows your app to read and write data even without an internet connection. Pending writes queue locally and sync automatically when connectivity returns. In FlutterFlow, enable offline persistence by adding a Custom Action that runs during app initialization. Set PersistenceSettings with the cacheSizeBytes configuration. With persistence enabled, your app's Firestore listeners serve data from the local cache when offline, so the UI never goes blank due to connectivity issues. Data written offline appears in the UI instantly from cache and then syncs to the server when back online.

enable_firestore_persistence.dart
1import 'package:cloud_firestore/cloud_firestore.dart';
2
3Future<void> enableFirestorePersistence() async {
4 try {
5 FirebaseFirestore.instance.settings = const Settings(
6 persistenceEnabled: true,
7 cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED,
8 );
9 } catch (e) {
10 // Persistence may already be enabled or not supported on this platform
11 // This is a non-fatal error
12 print('Firestore persistence setup: $e');
13 }
14}

Expected result: Taking the device offline and using the app continues to work — data displays from cache. When reconnected, any changes made offline sync automatically.

4

Sync Real-Time Data with Firestore Listeners

In FlutterFlow, any widget bound to a Firestore document or collection automatically receives real-time updates via StreamBuilder. When the user's Firestore document changes on one device, the StreamBuilder on all other signed-in devices receives the update within milliseconds and rebuilds the relevant widgets. To wire this up, make sure your preference-displaying widgets (like a dark mode toggle or language selector) read from the Firestore document stream rather than only from App State. Use FlutterFlow's Document reference binding on the current user's document to automatically propagate changes across all devices.

Expected result: Updating a setting on one device is visible on a second device within 1-2 seconds without needing to refresh the app.

5

Handle Offline Conflicts with Atomic Firestore Operations

When two devices edit the same user data while offline, they may produce conflicting writes that both need to sync when reconnecting. For most user preference data, last-write-wins (Firestore's default) is acceptable — the most recent change wins. But for data like read counts, bookmark lists, or shopping carts, use FieldValue.increment() and FieldValue.arrayUnion() so Firestore can merge the changes atomically rather than overwriting. For example, if the user adds items to a favorites list on two devices while offline, arrayUnion() merges both additions when they reconnect instead of one list overwriting the other.

favorites_sync.dart
1import 'package:cloud_firestore/cloud_firestore.dart';
2import 'package:firebase_auth/firebase_auth.dart';
3
4// Safe: add to favorites on two offline devices — both additions preserved
5Future<void> addToFavorites(String itemId) async {
6 final uid = FirebaseAuth.instance.currentUser?.uid;
7 if (uid == null) return;
8
9 await FirebaseFirestore.instance
10 .collection('users')
11 .doc(uid)
12 .update({
13 'favorites': FieldValue.arrayUnion([itemId]),
14 'favorites_count': FieldValue.increment(1),
15 });
16}
17
18// Safe: remove from favorites
19Future<void> removeFromFavorites(String itemId) async {
20 final uid = FirebaseAuth.instance.currentUser?.uid;
21 if (uid == null) return;
22
23 await FirebaseFirestore.instance
24 .collection('users')
25 .doc(uid)
26 .update({
27 'favorites': FieldValue.arrayRemove([itemId]),
28 'favorites_count': FieldValue.increment(-1),
29 });
30}

Expected result: Adding a favorite on an offline phone and a different favorite on an offline tablet — both appear in the favorites list after reconnecting, not just one.

Complete working example

cross_device_sync.dart
1// Complete cross-device sync setup for FlutterFlow
2// Include these Custom Actions in your app initialization flow
3
4import 'package:cloud_firestore/cloud_firestore.dart';
5import 'package:firebase_auth/firebase_auth.dart';
6
7// ─── Step 1: Enable offline persistence (call before any Firestore use) ───────
8
9void enableFirestorePersistence() {
10 try {
11 FirebaseFirestore.instance.settings = const Settings(
12 persistenceEnabled: true,
13 cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED,
14 );
15 } catch (_) {}
16}
17
18// ─── Step 2: Load user preferences on login ───────────────────────────────────
19
20Future<Map<String, dynamic>> loadUserPreferences() async {
21 final uid = FirebaseAuth.instance.currentUser?.uid;
22 if (uid == null) return {};
23
24 final snap = await FirebaseFirestore.instance
25 .collection('users').doc(uid).get();
26
27 final prefs = (snap.data()?['preferences'] as Map<String, dynamic>?) ?? {};
28 return {
29 'dark_mode': prefs['dark_mode'] ?? false,
30 'language': prefs['language'] ?? 'en',
31 'notifications_enabled': prefs['notifications_enabled'] ?? true,
32 'default_currency': prefs['default_currency'] ?? 'USD',
33 'onboarding_complete': snap.data()?['onboarding_complete'] ?? false,
34 };
35}
36
37// ─── Step 3: Save individual preference changes ───────────────────────────────
38
39Future<bool> savePreference(String key, dynamic value) async {
40 final uid = FirebaseAuth.instance.currentUser?.uid;
41 if (uid == null) return false;
42 try {
43 await FirebaseFirestore.instance
44 .collection('users').doc(uid)
45 .update({'preferences.$key': value});
46 return true;
47 } catch (_) {
48 return false;
49 }
50}
51
52// ─── Step 4: Atomic list operations (conflict-safe) ──────────────────────────
53
54Future<void> addToList(String field, dynamic item) async {
55 final uid = FirebaseAuth.instance.currentUser?.uid;
56 if (uid == null) return;
57 await FirebaseFirestore.instance.collection('users').doc(uid)
58 .update({field: FieldValue.arrayUnion([item])});
59}
60
61Future<void> removeFromList(String field, dynamic item) async {
62 final uid = FirebaseAuth.instance.currentUser?.uid;
63 if (uid == null) return;
64 await FirebaseFirestore.instance.collection('users').doc(uid)
65 .update({field: FieldValue.arrayRemove([item])});
66}
67
68// ─── Step 5: Subscribe to real-time user document updates ─────────────────────
69
70Stream<Map<String, dynamic>> userPreferencesStream(String uid) {
71 return FirebaseFirestore.instance
72 .collection('users').doc(uid)
73 .snapshots()
74 .map((snap) => (snap.data()?['preferences'] as Map<String, dynamic>?) ?? {});
75}

Common mistakes

Why it's a problem: Storing user preferences in App State instead of Firestore

How to avoid: Store all preferences in the user's Firestore document. Load them into App State on app startup for fast access, and write changes back to Firestore so they persist and sync.

Why it's a problem: Using set() with full preference object instead of nested dot-notation update

How to avoid: Use update() with dot notation keys like 'preferences.dark_mode' to update only the specific field that changed, leaving all other preference fields untouched.

Why it's a problem: Not initializing Firestore offline persistence before the first Firestore read

How to avoid: Call enableFirestorePersistence() in main.dart before runApp(), or in the very first Custom Action in your app initialization flow, before any Firestore reads.

Best practices

  • Use a nested 'preferences' map in the user document rather than top-level fields to keep the document organized and easy to migrate.
  • Always load preferences on app startup and populate App State — do not fetch from Firestore on every widget build.
  • Use FieldValue.arrayUnion() and FieldValue.arrayRemove() for list-type preferences so offline changes from multiple devices merge correctly.
  • Listen to the user document as a real-time stream so that changes made on one device appear on all others without requiring app restart.
  • Set a reasonable cache size for offline persistence — CACHE_SIZE_UNLIMITED is fine for most apps with modest data volumes, but set a cap (e.g., 100MB) for apps with large media metadata.
  • Migrate preferences from App State to Firestore gradually: add the Firestore read/write but keep the App State variable as a local mirror for backward compatibility.
  • Test cross-device sync explicitly: log in on two devices simultaneously and verify that a preference change on one appears on the other within a few seconds.

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I am building a FlutterFlow app with Firebase. Explain how Firestore provides automatic cross-device sync, which data should be in Firestore versus App State versus Page State, how to load and save user preferences from a Firestore user document, how to enable offline persistence, and how to use FieldValue.arrayUnion to handle offline conflict resolution for list-type data. Show complete Dart code.

FlutterFlow Prompt

In my FlutterFlow app, add an On Load action to my main app page that calls a Custom Action to load the current user's preferences from their Firestore document and sets App State variables: darkMode (bool), selectedLanguage (String), and notificationsEnabled (bool). Also add a Custom Action called savePreference(key, value) that updates a specific preference field using dot notation.

Frequently asked questions

Does Firestore sync data between devices instantly?

Firestore updates propagate to active listeners typically within 100-300 milliseconds on a good connection. Both devices must have an active real-time listener (StreamBuilder) on the same document for updates to appear immediately. If the second device opens the app later, it reads the latest data from Firestore on next query — no streaming required.

What happens to App State data when the user logs out?

App State is not tied to authentication — it persists for the duration of the app session regardless of who is logged in. If you store user-specific data in App State and the user logs out, that data remains in App State until the app restarts or until you explicitly clear it. Always clear App State variables when a user signs out by adding a Clear App State action to your sign-out flow.

How do I sync data for a user who uses the app without an account?

Use Firebase anonymous authentication. Sign users in anonymously on first launch and create a Firestore user document with their anonymous UID. When they later create a full account, link the anonymous account to the email/password account using linkWithCredential() — the UID stays the same and all their Firestore data remains attached. This gives you sync across anonymous sessions on the same device and an upgrade path to cross-device sync once they register.

What data should I never put in Firestore?

Never put sensitive credentials (API keys, passwords) in Firestore documents. Never store large binary files (images, videos, audio) as Base64 strings — use Firebase Storage instead and store the URL. Never store data that changes more than once per second per document — use Firestore for infrequent updates and consider Realtime Database for high-frequency counters.

How do I sync data that users should not be able to see on each other's devices?

Use Firestore Security Rules to restrict each user's data to only their own documents. The standard rule pattern is: allow read, write: if request.auth.uid == resource.data.user_id. This ensures users can only read and write documents where the user_id field matches their own authenticated UID, so cross-user data never leaks.

My users report that their data disappeared after reinstalling the app. What happened?

Reinstalling the app clears the local Firestore offline cache but does not affect the cloud data. The likely cause is that the data was stored in App State (which is cleared on reinstall) rather than Firestore. If the data genuinely exists in Firestore, it will reload the next time the user logs in and the relevant queries run. Verify this by checking the user's Firestore documents directly in Firebase Console.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.