FlutterFlow apps control IoT devices through three main patterns: MQTT via the mqtt_client Custom Widget connecting to a broker like AWS IoT or HiveMQ, REST API calls if the device vendor provides one, or Firebase as a relay where the device reads commands from Firestore and publishes state back. Most home IoT devices lack public IP addresses, so a message broker or cloud relay is required rather than direct connections.
Connect FlutterFlow to IoT devices via MQTT, REST, or Firebase relay
IoT devices run on local networks or behind NAT, so they are not directly reachable from a mobile app over the internet. FlutterFlow solves this with three approaches: MQTT (a lightweight pub/sub protocol used by most IoT hardware), vendor REST APIs (Philips Hue, TP-Link Tapo, Samsung SmartThings), and Firebase Firestore as a bidirectional command relay. This tutorial covers all three patterns so you can pick the right one for your hardware. You will build a device dashboard with real-time status indicators, on/off toggle switches, and a brightness slider.
Prerequisites
- A FlutterFlow project with Custom Actions enabled (any paid plan or free with custom code)
- At least one IoT device or MQTT broker to test against (HiveMQ Cloud free tier works)
- Basic understanding of FlutterFlow's Custom Action editor and Firestore Backend Queries
- Firebase project connected to FlutterFlow if using the Firestore relay pattern
Step-by-step guide
Add mqtt_client to Pubspec Dependencies
Add mqtt_client to Pubspec Dependencies
In FlutterFlow, go to Custom Code (left navigation) → Pubspec Dependencies → Add Dependency. Enter mqtt_client with version ^10.0.0. Click Save. This package handles WebSocket and TCP connections to any MQTT broker. If you plan to use Bluetooth Low Energy instead of MQTT, add flutter_blue_plus ^1.31.0 here as well. After saving, click Compile Code to verify the package resolves correctly before writing any Custom Actions.
Expected result: Pubspec compiles without errors and mqtt_client is listed in the dependencies panel.
Create the MQTT Connect and Subscribe Custom Action
Create the MQTT Connect and Subscribe Custom Action
Go to Custom Code → Custom Actions → Add Action. Name it mqttConnect. Import statements at the top: import 'package:mqtt_client/mqtt_client.dart'; import 'package:mqtt_client/mqtt_server_client.dart';. Create an MqttServerClient with your broker host and a unique clientIdentifier (use Uuid().v4() from the uuid package). Set client.port = 8883 for TLS or 1883 for plain TCP. Set client.secure = true if using TLS. Call await client.connect(username, password) inside a try/catch. On connection, subscribe to your device topic: client.subscribe('home/lights/status', MqttQos.atLeastOnce). Listen to client.updates to receive incoming messages and update App State variables (deviceOnline, brightness, temperature). Store the client instance in a global variable so other Actions can publish to it.
Expected result: The Custom Action connects to the broker and begins streaming device state updates into App State variables.
Build the device dashboard UI
Build the device dashboard UI
Create a new page named DeviceDashboard. Add a ListView widget and use Generate Dynamic Children bound to an App State variable deviceList (List of JSON, each item has id, name, online, brightness). Inside each list item, add: a Container row with a green or red Circle bound conditionally to item.online, a Text widget for item.name, a Switch widget with its value bound to item.online and its On Toggle calling a Custom Action mqttPublish with topic 'home/{id}/command' and payload '{"power": true/false}', and a Slider widget (visible only when item.online is true) bound to item.brightness and On Change Committed calling mqttPublish with payload '{"brightness": sliderValue}'. Call the mqttConnect action in the Page's On Page Load action flow.
Expected result: Dashboard displays device cards with live online/offline status, functional power toggles, and brightness sliders.
Use Firestore as a command relay (alternative to MQTT)
Use Firestore as a command relay (alternative to MQTT)
If your IoT device runs a firmware that can read from Firestore (ESP32 with Firebase Arduino SDK, for example), use Firestore as the message bus. Structure: collection devices, document per device with fields: power (Boolean), brightness (Integer), lastSeen (Timestamp), online (Boolean). In FlutterFlow, add a Backend Query on the DeviceDashboard page pointing to the devices collection with a real-time listener. Use Update Document actions from Switch and Slider widgets to write new values directly to the device document. The firmware polls or listens to Firestore changes, reads the command, actuates the hardware, then writes its current state back to the same document. This requires no MQTT broker — Firebase handles all message delivery.
Expected result: Toggling the FlutterFlow Switch updates the Firestore document, and the device firmware reads the change and responds within 1-2 seconds.
Integrate vendor REST APIs for commercial IoT platforms
Integrate vendor REST APIs for commercial IoT platforms
Many consumer IoT platforms (Philips Hue, TP-Link Tapo, Samsung SmartThings) expose REST APIs. In FlutterFlow, go to API Manager → Add API Group. For Philips Hue: Base URL https://api.meethue.com/bridge/{bridgeId}, header Authorization: Bearer {hue_token}. Add API Calls: GET /lights (list lights with status), PUT /lights/{id}/state with body {"on": true, "bri": 200} for control. Store the hue_token in FlutterFlow Secrets (Settings → Secrets) and reference it in the header as [hue_token]. Use Backend Queries on your dashboard page to poll the lights state every 30 seconds. For SmartThings: Base URL https://api.smartthings.com/v1, header Authorization: Bearer {st_token}, endpoints GET /devices and POST /devices/{deviceId}/commands.
Expected result: The API Group tests successfully and returns your device list with current power and brightness state.
Complete working example
1// Custom Action: mqttConnect2// Add to Pubspec: mqtt_client: ^10.0.0, uuid: ^4.0.03import 'package:mqtt_client/mqtt_client.dart';4import 'package:mqtt_client/mqtt_server_client.dart';5import 'package:uuid/uuid.dart';67// Store client globally so mqttPublish can reuse it8MqttServerClient? _mqttClient;910Future<bool> mqttConnect(11 String brokerHost,12 int brokerPort,13 String username,14 String password,15 String deviceTopic,16) async {17 final clientId = const Uuid().v4();18 _mqttClient = MqttServerClient(brokerHost, clientId);19 _mqttClient!.port = brokerPort;20 _mqttClient!.secure = brokerPort == 8883;21 _mqttClient!.keepAlivePeriod = 30;22 _mqttClient!.onDisconnected = () {23 // Update App State: deviceOnline = false24 };2526 try {27 await _mqttClient!.connect(username, password);28 } catch (e) {29 _mqttClient!.disconnect();30 return false;31 }3233 if (_mqttClient!.connectionStatus!.state !=34 MqttConnectionState.connected) {35 return false;36 }3738 _mqttClient!.subscribe(deviceTopic, MqttQos.atLeastOnce);3940 _mqttClient!.updates!.listen((messages) {41 final msg = messages[0];42 final payload = MqttPublishPayload.bytesToStringAsString(43 (msg.payload as MqttPublishMessage).payload.message,44 );45 // Parse payload JSON and update App State variables here46 // e.g. FFAppState().update(() { FFAppState().deviceStatus = payload; });47 });4849 return true;50}5152// Custom Action: mqttPublish53Future<void> mqttPublish(String topic, String payload) async {54 if (_mqttClient == null ||55 _mqttClient!.connectionStatus!.state !=56 MqttConnectionState.connected) {57 return;58 }59 final builder = MqttClientPayloadBuilder();60 builder.addString(payload);61 _mqttClient!.publishMessage(62 topic,63 MqttQos.atLeastOnce,64 builder.payload!,65 );66}Common mistakes
Why it's a problem: Sending IoT commands directly to the device IP from the FlutterFlow app
How to avoid: Use a message broker (MQTT via AWS IoT Core or HiveMQ) or a cloud relay (Firestore) that both the app and the device connect to outbound. The broker routes commands bidirectionally without requiring either side to have a public IP.
Why it's a problem: Opening a new MQTT connection on every page load or button tap
How to avoid: Connect once in the app's On App Load action or the dashboard page's On Page Load action. Store the client in a global Dart variable and reuse it in all publish actions. Reconnect only when the connection drops.
Why it's a problem: Hardcoding MQTT broker credentials in the Custom Action source code
How to avoid: Store broker username and password in Firestore (fetched at login for the authenticated user) or in FlutterFlow Secrets for development. Pass them as parameters to the mqttConnect action rather than hardcoding strings.
Best practices
- Always use TLS (port 8883) for MQTT connections — plain TCP (1883) transmits device commands as cleartext over the network
- Implement an exponential backoff reconnect strategy in the onDisconnected callback rather than immediately reconnecting to avoid hammering a temporarily unavailable broker
- Use QoS level 1 (atLeastOnce) for device commands so commands survive brief disconnections — QoS 0 is fire-and-forget and will silently drop messages during reconnects
- Debounce slider onChange events by at least 300ms before publishing to prevent flooding the broker with dozens of brightness messages per second while the user is dragging
- Store last-known device state in Firestore so the dashboard shows the correct state immediately on app open, before the MQTT connection re-establishes
- Add a connection timeout indicator in the UI — if the MQTT client has not connected within 10 seconds, show an error state with a retry button rather than a blank dashboard
- For battery-powered devices, use MQTT retain flag on status topics so newly connected clients immediately receive the last known state without waiting for the next device heartbeat
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I am building a FlutterFlow app to control IoT devices. I want to use MQTT with the mqtt_client Flutter package. Write me two Custom Action functions: (1) mqttConnect that takes broker host, port, username, password, and a topic string, connects with TLS, subscribes to the topic, and stores the client in a global variable; (2) mqttPublish that takes a topic and JSON payload string and publishes it to the existing connection. Include proper error handling and disconnection callbacks.
Create a device dashboard page in my FlutterFlow app. The page should load device data from the App State deviceList variable (List of JSON with fields: id, name, online, brightness). Show each device as a card with a colored online/offline indicator, a Switch for power control, and a Slider for brightness (0-255). Call the mqttConnect custom action on page load.
Frequently asked questions
Can FlutterFlow connect directly to IoT devices over Bluetooth?
Yes, using a Custom Widget or Custom Action with the flutter_blue_plus package from pub.dev. Add the package in Custom Code → Pubspec Dependencies. The Custom Action calls FlutterBluePlus.startScan(), connects to a BLE device by service UUID, and reads or writes characteristics. Note that BLE requires camera/Bluetooth permissions configured in FlutterFlow's App Details settings, and testing requires a physical device — the emulator does not support Bluetooth hardware.
Which MQTT broker should I use for a FlutterFlow IoT project?
For development and small projects, HiveMQ Cloud's free tier (up to 100 connections, no credit card) is the easiest starting point. For production, AWS IoT Core offers certificate-based auth, fine-grained device policies, and integration with other AWS services. For self-hosted setups, Mosquitto on a Cloud Run or Compute Engine instance gives you full control. All three work with the mqtt_client Flutter package.
How do I show real-time device status updates in the FlutterFlow UI?
There are two approaches. With MQTT: the Custom Action listening to client.updates parses incoming JSON and calls FFAppState().update() to modify App State variables — widgets bound to those variables rebuild automatically. With Firebase: add a Backend Query to the devices collection with the real-time stream option enabled — FlutterFlow automatically refreshes the UI whenever a Firestore document changes, with no custom code needed.
Can I control IoT devices that only work on a local Wi-Fi network?
Devices on a local network are not directly reachable from a mobile app on cellular data. You have two options: (1) deploy a local MQTT broker or HTTP relay on the same network (a Raspberry Pi or home server), and have your cloud MQTT bridge route commands to it; (2) use a cloud platform like Home Assistant Cloud or SmartThings that acts as a relay between the internet and your local devices. Direct local control only works if the app and device are on the same Wi-Fi network simultaneously.
What if I need help building a complex IoT dashboard with multiple device types?
Multi-vendor IoT projects involving MQTT, BLE, and REST APIs simultaneously can become complex quickly. RapidDev's engineers have built IoT dashboards in FlutterFlow for smart home, industrial monitoring, and agricultural sensor applications. If you need a production-ready solution with offline support and device grouping, reach out for a scoping call.
How do I handle a device going offline mid-session?
Subscribe to a Last Will and Testament (LWT) message in MQTT — configure the device to set a retained message on its status topic before connecting, and have it publish an 'offline' message as the LWT. When the broker detects the device disconnect, it delivers the LWT message to all subscribers. In the FlutterFlow MQTT listener, parse the status message and update an App State boolean for that device, changing the UI indicator from green to red automatically.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation