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

How to Create a Smart Home Device Management App in FlutterFlow

FlutterFlow cannot directly control smart home devices via Bluetooth or local WiFi from its visual builder. Instead, connect to manufacturer cloud APIs (SmartThings, Tuya, Philips Hue) through Cloud Functions that proxy REST requests. Use a room-based TabBar layout with device cards, toggle switches, and sliders that poll the cloud every 30 seconds for live device state.

What you'll learn

  • Why FlutterFlow uses cloud APIs instead of direct Bluetooth or WiFi for device control
  • How to structure a room-based TabBar layout with device cards
  • How to call a Cloud Function proxy to read and control smart home devices
  • How to implement 30-second polling for real-time device state updates
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner10 min read45-60 minFlutterFlow Pro+ (code export required for custom packages)March 2026RapidDev Engineering Team
TL;DR

FlutterFlow cannot directly control smart home devices via Bluetooth or local WiFi from its visual builder. Instead, connect to manufacturer cloud APIs (SmartThings, Tuya, Philips Hue) through Cloud Functions that proxy REST requests. Use a room-based TabBar layout with device cards, toggle switches, and sliders that poll the cloud every 30 seconds for live device state.

Smart Home Control via Manufacturer Cloud APIs

Smart home platforms like SmartThings, Tuya, and Philips Hue expose REST APIs that let you read device state and send commands through the internet. FlutterFlow connects to these APIs through Firebase Cloud Functions that act as a secure proxy — keeping your API credentials on the server and returning clean JSON to your app. The result is a room-organized dashboard where users can toggle lights, adjust thermostats, and check sensor readings, all updating automatically every 30 seconds.

Prerequisites

  • A FlutterFlow Pro account with a Firebase project connected
  • A smart home platform account (SmartThings, Tuya, or Philips Hue) with at least one device
  • Your platform's API credentials (client ID, client secret, or API key)
  • Basic familiarity with FlutterFlow's widget tree and Action Flows

Step-by-step guide

1

Create the Firestore data model for rooms and devices

Open your Firebase console and create two Firestore collections. The first, named 'rooms', stores documents with fields: name (String), icon (String), and order (Integer). The second, named 'devices', stores documents with fields: room_id (String), name (String), type (String — light/thermostat/sensor), state (Map — contains on: Boolean, brightness: Integer, temperature: Double), last_updated (Timestamp), and platform_device_id (String). Back in FlutterFlow, open the Firestore panel, click 'Get Schema from Firestore', and import both collections. This gives your app strongly-typed references to use in Backend Queries throughout the UI.

Expected result: Both collections appear in FlutterFlow's Firestore panel with all fields mapped correctly.

2

Deploy a Cloud Function proxy for device commands

In your Firebase project, create a Cloud Function named 'smartHomeProxy'. This function accepts a POST body with device_id, command (get_state or set_state), and payload. It reads API credentials from Firebase environment config (never hardcode them), calls the manufacturer REST API, and writes the updated state back to the corresponding Firestore device document. Deploy the function using the Firebase CLI. In FlutterFlow, open the API Calls panel, click '+', choose 'Cloud Function', paste the function URL, add a header of Content-Type: application/json, and set the body schema to match the fields above. Save as 'SmartHomeProxy'.

functions/index.js
1// functions/index.js (Firebase Cloud Function)
2const functions = require('firebase-functions');
3const admin = require('firebase-admin');
4const axios = require('axios');
5admin.initializeApp();
6
7exports.smartHomeProxy = functions.https.onCall(async (data, context) => {
8 if (!context.auth) throw new functions.https.HttpsError('unauthenticated', 'Must be signed in');
9 const { device_id, command, payload } = data;
10 const apiKey = functions.config().smarthome.api_key;
11 const baseUrl = functions.config().smarthome.base_url;
12
13 let result;
14 if (command === 'get_state') {
15 const res = await axios.get(`${baseUrl}/devices/${device_id}/state`, {
16 headers: { Authorization: `Bearer ${apiKey}` }
17 });
18 result = res.data;
19 } else if (command === 'set_state') {
20 const res = await axios.post(`${baseUrl}/devices/${device_id}/commands`, payload, {
21 headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' }
22 });
23 result = res.data;
24 }
25
26 await admin.firestore().doc(`devices/${device_id}`).update({
27 state: result.state,
28 last_updated: admin.firestore.FieldValue.serverTimestamp()
29 });
30 return { success: true, state: result.state };
31});

Expected result: Cloud Function is deployed and reachable. Test it from the Firebase console with a sample device_id to confirm it returns state data.

3

Build the room-based TabBar layout

On your home page in FlutterFlow, drag a Column widget to fill the screen. Inside it, add a TabBar widget. In the TabBar properties, set 'Dynamic Tabs' to true and bind the Tab Labels to a Backend Query on the 'rooms' collection ordered by the 'order' field. Each tab's label binds to the room name field. Below the TabBar, add a TabBarView that matches. Inside each view, place a GridView widget (2 columns, spacing 12). The GridView's data source is a Backend Query on the 'devices' collection filtered by room_id equal to the current tab's room document ID. This ensures each tab shows only its room's devices.

Expected result: The app shows room tabs at the top and a 2-column device grid below, populated from Firestore.

4

Design device cards with toggle and slider controls

Create a reusable Component called 'DeviceCard'. Inside it, add a Container with rounded corners (radius 16) and a soft shadow. Place an Icon (bound to the device type field — use a conditional to pick a lightbulb, thermostat, or sensor icon), a Text widget for the device name, and a Row at the bottom with a Toggle widget (bound to state.on) and a Slider widget (visible only when type equals 'light', bound to state.brightness, range 0-100). On the Toggle's On Changed action, add a Custom Action that calls the 'SmartHomeProxy' API Call with command: set_state and payload: { on: the new toggle value }. On the Slider's On Change End action, do the same with payload: { brightness: the slider value }.

Expected result: Each device shows an icon, name, live toggle state, and (for lights) a brightness slider that immediately sends a command when adjusted.

5

Implement 30-second polling with a Timer action

On your home page's initState, add an Action Flow that starts a Periodic Timer set to 30,000 milliseconds. Inside the timer callback, add a Backend Query Refresh action targeting both the rooms and devices queries. This re-fetches all device states from Firestore, which was updated by a separate scheduled Cloud Function that polls the manufacturer API every 60 seconds. To avoid stale displays, also add a manual refresh: place a RefreshIndicator widget wrapping the TabBarView so users can pull-to-refresh at any time. Disable the timer in the page's dispose action by storing the timer reference in Page State and calling timer.cancel().

Expected result: Device states refresh automatically every 30 seconds and instantly on pull-to-refresh. The UI never shows data older than 30 seconds.

6

Add a device detail sheet with full controls

Create a Bottom Sheet page called 'DeviceDetailSheet' with a Page Parameter called 'device' of type DeviceDocument. On a device card tap action, navigate to this bottom sheet passing the tapped device. Inside the sheet, show all device properties: name, room, last_updated (formatted as 'Updated X minutes ago'), current state values, and a full set of controls. For thermostats, add a large dial using a CircularSlider or two increment/decrement buttons for temperature. For sensors, show the last reading value in a large Text widget with units. Add a 'Rename Device' TextField that calls a Firestore update action on blur.

Expected result: Tapping any device card opens a bottom sheet with full detail and advanced controls for that specific device type.

Complete working example

functions/index.js
1// Firebase Cloud Function — Smart Home Proxy
2// Bridges FlutterFlow app to smart home manufacturer APIs
3// Deploy with: firebase deploy --only functions
4
5const functions = require('firebase-functions');
6const admin = require('firebase-admin');
7const axios = require('axios');
8
9admin.initializeApp();
10const db = admin.firestore();
11
12// Called from FlutterFlow to get or set device state
13exports.smartHomeProxy = functions.https.onCall(async (data, context) => {
14 if (!context.auth) {
15 throw new functions.https.HttpsError('unauthenticated', 'Must be signed in');
16 }
17
18 const { device_id, command, payload } = data;
19 if (!device_id || !command) {
20 throw new functions.https.HttpsError('invalid-argument', 'device_id and command required');
21 }
22
23 const apiKey = functions.config().smarthome.api_key;
24 const baseUrl = functions.config().smarthome.base_url;
25 const headers = { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' };
26
27 try {
28 let deviceState;
29 if (command === 'get_state') {
30 const res = await axios.get(`${baseUrl}/devices/${device_id}/state`, { headers });
31 deviceState = res.data.state || res.data;
32 } else if (command === 'set_state') {
33 await axios.post(`${baseUrl}/devices/${device_id}/commands`, payload || {}, { headers });
34 // Re-fetch state after command
35 const res = await axios.get(`${baseUrl}/devices/${device_id}/state`, { headers });
36 deviceState = res.data.state || res.data;
37 } else {
38 throw new functions.https.HttpsError('invalid-argument', `Unknown command: ${command}`);
39 }
40
41 // Write updated state to Firestore for real-time UI updates
42 await db.doc(`devices/${device_id}`).update({
43 state: deviceState,
44 last_updated: admin.firestore.FieldValue.serverTimestamp()
45 });
46
47 return { success: true, state: deviceState };
48 } catch (err) {
49 console.error('smartHomeProxy error:', err.message);
50 throw new functions.https.HttpsError('internal', err.message);
51 }
52});
53
54// Scheduled function: polls manufacturer API for all devices every 60s
55exports.pollAllDevices = functions.pubsub
56 .schedule('every 1 minutes')
57 .onRun(async () => {
58 const apiKey = functions.config().smarthome.api_key;
59 const baseUrl = functions.config().smarthome.base_url;
60 const headers = { Authorization: `Bearer ${apiKey}` };
61
62 const snapshot = await db.collection('devices').get();
63 const updates = snapshot.docs.map(async (doc) => {
64 const device_id = doc.data().platform_device_id;
65 if (!device_id) return;
66 try {
67 const res = await axios.get(`${baseUrl}/devices/${device_id}/state`, { headers });
68 await doc.ref.update({
69 state: res.data.state || res.data,
70 last_updated: admin.firestore.FieldValue.serverTimestamp()
71 });
72 } catch (e) {
73 console.warn(`Failed to poll device ${device_id}:`, e.message);
74 }
75 });
76 await Promise.allSettled(updates);
77 console.log(`Polled ${snapshot.size} devices`);
78 return null;
79 });

Common mistakes when creating a Smart Home Device Management App in FlutterFlow

Why it's a problem: Trying to use Bluetooth or mDNS directly from FlutterFlow's visual builder

How to avoid: Always route device communication through the manufacturer's cloud API via a Cloud Function. If you genuinely need local Bluetooth, export the FlutterFlow project and add the flutter_blue_plus package to the exported code.

Why it's a problem: Hardcoding the manufacturer API key directly in the FlutterFlow API Call headers

How to avoid: Store API keys in Firebase environment config using 'firebase functions:config:set smarthome.api_key=YOUR_KEY' and access them only inside Cloud Functions.

Why it's a problem: Polling the manufacturer API from every client device individually

How to avoid: Use a single scheduled Cloud Function that polls the API and writes results to Firestore. All clients then subscribe to Firestore for state, which is designed for fan-out reads.

Why it's a problem: Not cancelling the Periodic Timer when the page is disposed

How to avoid: Store the timer reference in Page State, then add a page dispose Action Flow that cancels the timer.

Best practices

  • Always proxy manufacturer APIs through Cloud Functions — never expose API keys to the client.
  • Write device state to Firestore from your Cloud Function so the UI benefits from Firestore's real-time fan-out instead of each client polling independently.
  • Use optimistic UI updates: flip the toggle instantly in the UI before the API call returns, then revert if the call fails.
  • Cap polling frequency at 30 seconds minimum for battery efficiency; use Firestore real-time listeners for critical safety devices.
  • Add a 'last_updated' timestamp to each device card so users can tell at a glance how fresh the data is.
  • Store per-room layout preferences (grid vs list) in Firestore user settings so preferences persist across devices.
  • Add error states to device cards — a red border and 'Offline' label when a device hasn't been reachable for over 5 minutes.

Still stuck?

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

ChatGPT Prompt

I'm building a smart home app in FlutterFlow that controls devices via the Tuya cloud API. I have a Firebase project with Firestore collections for rooms and devices. Write a Firebase Cloud Function in Node.js that accepts a device_id and command (get_state or set_state), calls the Tuya API with credentials from Firebase config, and writes the updated device state back to Firestore. Include error handling for API timeouts.

FlutterFlow Prompt

In my FlutterFlow project I have a 'devices' Firestore collection with fields: name, type, state (Map with 'on' Boolean and 'brightness' Integer), room_id. Create a reusable Component called DeviceCard that shows the device name, a toggle bound to state.on, and a Slider (only visible when type equals 'light') bound to state.brightness. The toggle's On Changed action should call an API Call named SmartHomeProxy with body: { device_id: device.id, command: 'set_state', payload: { on: newValue } }.

Frequently asked questions

Can FlutterFlow control smart home devices without internet?

Not with the visual builder alone. Local control (LAN/Bluetooth) requires native Flutter packages added after code export. For most apps, cloud-based control through manufacturer APIs is sufficient and much easier to maintain.

Which smart home platforms work best with this approach?

Tuya (covers the most white-label devices), SmartThings (Samsung ecosystem), and Philips Hue all have well-documented REST APIs. Tuya is the easiest to start with because it has an official Flutter SDK and free developer tier.

How do I handle devices that go offline?

Add an 'is_online' Boolean field to each device document. Your Cloud Function should catch API errors and set is_online to false. In the DeviceCard component, add a Conditional that overlays an 'Offline' badge when is_online is false.

Will the 30-second polling run up my Firestore read costs?

The polling itself is a Cloud Function-to-Firestore write, which is very cheap. The UI uses real-time Firestore listeners (Backend Query with real-time enabled), not repeated reads. You pay per document change, not per connected client.

Can I add voice control like 'Hey Google, turn off the lights'?

Yes, but not inside FlutterFlow directly. You need to connect your Cloud Functions to Google Home or Amazon Alexa as a Smart Home Action/Skill. The FlutterFlow app and the voice platform both call the same Cloud Function layer.

How many devices can this architecture handle?

The scheduled polling Cloud Function uses Promise.allSettled so all devices are polled in parallel. Firebase allows up to 500 concurrent Firestore writes per second per database, which comfortably handles several thousand devices.

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.