Flutter MQTT, tek bir Dart kod tabanının Android, iOS, masaüstü ve web üzerinden cihazlarınızla konuşmasını sağlar. Bu eğitimde mqtt_client paketiyle bir Flutter uygulamasını bulut broker'a bağlayacak, canlı sensör değerlerini gösterecek ve bir cihaza açma/kapama komutları göndereceksiniz; yerel platformlarda TLS, tarayıcıda ise güvenli WebSocket kullanacaksınız.
Ne Geliştireceksiniz?
Uygulamanın tek bir ekranı vardır: bir cihaz publish ettiğinde güncellenen canlı bir sıcaklık değeri, bir bağlantı durumu çipi ve bir röleyi açıp kapatmak için komut publish eden bir anahtar. Arka planda iki topic kullanır:
| Topic | Yön | Payload örneği |
|---|---|---|
connectmq/demo/<your-id>/sensor/temperature |
cihazdan uygulamaya | {"value": 22.4} |
connectmq/demo/<your-id>/relay/set |
uygulamadan cihaza | ON veya OFF |
Test için broker.connectmq.com adresindeki public MQTT test broker'ını kullanıyoruz. Boş olmayan herhangi bir client ID ile anonim bağlantıları kabul eder, ancak üzerindeki her şey herkes tarafından okunabilir; bu yüzden rastgele bir topic öneki kullanın ve asla gerçek veri göndermeyin. Üretime geçtiğinizde kendi ConnectMQ broker'ınızın kimlik bilgilerini kullanırsınız; kodun yapısı değişmez.
Flutter MQTT Paketi Seçimi
En yaygın kullanılan seçenek pub.dev'deki mqtt_client paketidir. İki client sınıfıyla gelir:
- Android, iOS, macOS, Windows ve Linux için
MqttServerClient. Düz TCP, TLS veya WebSocket konuşur. - Flutter web için
MqttBrowserClient; tarayıcılar ham TCP soketlerine izin vermediğinden yalnızca WebSocket kullanabilir.
mqtt_client, MQTT 3.1.1'i uygular. Mesaj süresi dolumu (message expiry) veya user properties gibi MQTT 5 özelliklerine ihtiyacınız varsa, aynı geliştirici çok benzer bir API'ye sahip mqtt5_client paketini de sürdürmektedir. Bunun gibi bir pano ve komut uygulaması için 3.1.1 fazlasıyla yeterlidir. Kütüphaneleri diller arasında karşılaştırıyorsanız, MQTT client kütüphanelerine genel bakış yazısı ödünleşimleri ele alır.
Bağımlılığı ekleyin:
flutter pub add mqtt_client
Portlar ve Taşıma Katmanları
Broker dört listener sunar ve her platform için birini seçersiniz:
| Platform | Sınıf | Taşıma | Port |
|---|---|---|---|
| Android, iOS, masaüstü | MqttServerClient |
TLS | 8883 |
| Android, iOS, masaüstü (sıkı firewall arkasında) | useWebSocket ile MqttServerClient |
WSS | 8084, yol /mqtt |
| Flutter web | MqttBrowserClient |
WSS | 8084, yol /mqtt |
| Yalnızca yerel hata ayıklama | MqttServerClient |
TCP | 1883 |
1883 portu şifrelenmemiştir; onu hızlı denemeler için saklayın. Dizüstü bilgisayarınızdan dışarı çıkan her şey 8883 veya 8084 kullanmalıdır. Tarayıcı taşıma katmanlarının arka planını merak ediyorsanız WebSocket üzerinden MQTT yazısını okuyun.
Flutter MQTT Bağlantı Servisini Oluşturma
MQTT'yi widget'larınızın dışında tutun. Küçük bir servis sınıfı client'a sahip olur, sıcaklıkların bir stream'ini ve bağlantı durumunu sunar ve bir setRelay metodu sağlar. Flutter web dart:io import edemez; bu yüzden client'ı koşullu bir import ile oluşturuyoruz.
lib/mqtt/client_factory.dart:
export 'client_factory_server.dart'
if (dart.library.js_interop) 'client_factory_web.dart';
lib/mqtt/client_factory_server.dart:
import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqtt_client/mqtt_server_client.dart';
MqttClient createClient(String host, String clientId) {
final client = MqttServerClient.withPort(host, clientId, 8883);
client.secure = true; // TLS, sistem güven deposuna göre doğrulanır
return client;
}
lib/mqtt/client_factory_web.dart:
import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqtt_client/mqtt_browser_client.dart';
MqttClient createClient(String host, String clientId) {
final client = MqttBrowserClient.withPort('wss://$host/mqtt', clientId, 8084);
client.websocketProtocols = MqttClientConstants.protocolsSingleDefault;
return client;
}
lib/mqtt/mqtt_service.dart:
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:mqtt_client/mqtt_client.dart';
import 'client_factory.dart';
class MqttService {
MqttService({
this.host = 'broker.connectmq.com',
this.username,
this.password,
String? clientId,
String? topicPrefix,
}) : clientId = clientId ?? 'flutter-${Random().nextInt(1000000000)}',
topicPrefix = topicPrefix ?? 'connectmq/demo/${Random().nextInt(1000000000)}';
final String host;
final String? username;
final String? password;
final String clientId;
final String topicPrefix;
late final MqttClient _client;
final _temperature = StreamController<double>.broadcast();
final _connected = StreamController<bool>.broadcast();
Stream<double> get temperature => _temperature.stream;
Stream<bool> get connected => _connected.stream;
String get sensorTopic => '$topicPrefix/sensor/temperature';
String get relayTopic => '$topicPrefix/relay/set';
Future<void> connect() async {
_client = createClient(host, clientId);
_client.keepAlivePeriod = 30;
_client.autoReconnect = true;
_client.resubscribeOnAutoReconnect = true;
_client.logging(on: false);
_client.onConnected = () => _connected.add(true);
_client.onDisconnected = () => _connected.add(false);
_client.onAutoReconnected = () => _connected.add(true);
final connMessage = MqttConnectMessage()
.withClientIdentifier(clientId)
.startClean();
if (username != null && password != null) {
connMessage.authenticateAs(username!, password!);
}
_client.connectionMessage = connMessage;
try {
await _client.connect();
} on Exception {
_client.disconnect();
rethrow;
}
if (_client.connectionStatus?.state != MqttConnectionState.connected) {
throw StateError('MQTT connect failed: ${_client.connectionStatus}');
}
_client.subscribe(sensorTopic, MqttQos.atLeastOnce);
_client.updates!.listen(_onMessages);
}
void _onMessages(List<MqttReceivedMessage<MqttMessage?>> messages) {
for (final m in messages) {
final publish = m.payload as MqttPublishMessage;
final text = MqttPublishPayload.bytesToStringAsString(publish.payload.message);
try {
final value = (jsonDecode(text)['value'] as num).toDouble();
_temperature.add(value);
} catch (_) {
// Bozuk payload'ları stream'i çökertmek yerine yok say.
}
}
}
void setRelay(bool on) {
final builder = MqttClientPayloadBuilder()..addString(on ? 'ON' : 'OFF');
_client.publishMessage(relayTopic, MqttQos.atLeastOnce, builder.payload!);
}
void dispose() {
_client.disconnect();
_temperature.close();
_connected.close();
}
}
Burada birkaç ayrıntı önemlidir. autoReconnect ile resubscribeOnAutoReconnect birlikte, kararsız bir mobil ağın sizi bağlı ama sağır bırakmamasını sağlar. startClean(), yalnızca canlı veriyle ilgilenen bir arayüz client'ı için doğrudur; çevrimdışıyken komutları kaçırmaması gereken bir client ise kalıcı oturum kullanmalıdır (bkz. MQTT kalıcı oturumları). JSON ayrıştırması try bloğuna alınmıştır, çünkü public bir topic'e herkes publish edebilir.
Flutter Arayüzü
lib/main.dart:
import 'package:flutter/material.dart';
import 'mqtt/mqtt_service.dart';
void main() => runApp(const MaterialApp(home: DeviceScreen()));
class DeviceScreen extends StatefulWidget {
const DeviceScreen({super.key});
@override
State<DeviceScreen> createState() => _DeviceScreenState();
}
class _DeviceScreenState extends State<DeviceScreen> {
final mqtt = MqttService();
bool relayOn = false;
String? error;
@override
void initState() {
super.initState();
mqtt.connect().catchError((e) => setState(() => error = '$e'));
}
@override
void dispose() {
mqtt.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Flutter MQTT Demo')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StreamBuilder<bool>(
stream: mqtt.connected,
initialData: false,
builder: (_, s) => Chip(
label: Text(s.data! ? 'Connected' : 'Disconnected'),
),
),
if (error != null) Text(error!, style: const TextStyle(color: Colors.red)),
const SizedBox(height: 24),
StreamBuilder<double>(
stream: mqtt.temperature,
builder: (_, s) => Text(
s.hasData ? '${s.data!.toStringAsFixed(1)} °C' : '-- °C',
style: Theme.of(context).textTheme.displayMedium,
),
),
const SizedBox(height: 24),
SwitchListTile(
title: const Text('Relay'),
value: relayOn,
onChanged: (v) {
setState(() => relayOn = v);
mqtt.setRelay(v);
},
),
const SizedBox(height: 24),
SelectableText('Topic prefix: ${mqtt.topicPrefix}'),
],
),
),
);
}
}
Topic öneki ekrana yazdırılır; böylece test için bir terminale kopyalayabilirsiniz. Flutter'ı yerel veya JavaScript tabanlı yığınlarla kıyaslıyorsanız, aynı uygulamanın Swift ile iOS MQTT client ve React Native MQTT sürümleriyle karşılaştırın.
Simüle Edilmiş Bir Cihazla Test
Henüz donanıma ihtiyacınız yok. İki terminal açın ve Mosquitto komut satırı araçlarıyla cihaz rolünü üstlenin. PREFIX değerini uygulamada gösterilen değerle değiştirin:
# Uygulamanın gönderdiği komutları izle
mosquitto_sub -h broker.connectmq.com -p 1883 -i sim-sub-81723 -t "PREFIX/relay/set" -v
# Her 2 saniyede bir sahte sıcaklık değeri publish et
while true; do
mosquitto_pub -h broker.connectmq.com -p 1883 -i sim-pub-81723 \
-t "PREFIX/sensor/temperature" -m "{\"value\": $((RANDOM % 10 + 18)).$((RANDOM % 10))}"
sleep 2
done
Uygulamadaki anahtarı değiştirdiğinizde subscriber terminalinde ON veya OFF geldiğini görmelisiniz. Daha sonra gerçek bir kart bağlarsanız, ESP32 MQTT eğitimi aynı topic desenini kullanır.
Daha Fazla Cihaz için Flutter MQTT Topic Yapısı
Tek cihaz kolaydır. Uygulama bir cihaz listesine dönüştüğünde, her cihaz widget'ı için ayrı bir subscription oluşturmaktan kaçının. <prefix>/+/sensor/temperature gibi bir wildcard'a bir kez subscribe olun, _onMessage içinde cihaz ID'sini topic'ten ayrıştırın ve tek bir değer yerine cihaza göre anahtarlanmış bir map yayınlayın. Bu, subscription sayısını sabit tutar; client başına limitleri olan paylaşımlı broker'larda bu önemlidir; public broker client başına 10 subscription'a izin verir.
İki alışkanlık daha işe yarar. Cihazların mevcut durumlarını retain bayrağıyla publish etmesini sağlayın; böylece yeni açılan bir uygulama varsayılan false yerine gerçek röle durumunu hemen gösterir. MQTT retained mesajları rehberi bunun nasıl yapılacağını anlatır. Ayrıca anahtar konumuna güvenmek yerine cihazın komutları relay/state gibi ayrı bir state topic'inde onaylamasını sağlayın. Arayüz, kullanıcının neye dokunduğunu değil cihazın ne raporladığını yansıtmalıdır; böylece bir komut kaybolursa anahtar bir sonraki raporda gerçek duruma geri döner.
Test Broker'ından Üretime Geçiş
Public broker denemeler içindir. Gerçek bir ürün için tam yönetilen bir MQTT servisinde özel bir broker oluşturun; böylece yalnızca sizin cihazlarınız ve uygulamalarınız bağlanabilir:
- ConnectMQ portalına kaydolun, bir plan seçin ve bir broker oluşturun.
- Üretilen kullanıcı adını, parolayı ve client ID'yi kopyalayın.
- Servisi bunlarla oluşturun:
MqttService(username: 'YOUR_USERNAME', password: 'YOUR_PASSWORD', clientId: 'YOUR_CLIENT_ID', topicPrefix: 'acme/site1'). - 8883 portunda TLS'i koruyun (web için 8084'te WSS).
ConnectMQ'da client ID'ler verilen kimlik bilgilerine bağlıdır; bu nedenle aynı anda bağlanan her uygulama kurulumunun kendi kimliğine ihtiyacı vardır. Public bir uygulamanın her kopyasına tek bir paylaşılan parola gömmeyin; kontrol ettiğiniz bir backend'den kullanıcı başına veya cihaz başına kimlik bilgileri verin ve mobil bir binary'ye gömülen her şeyin çıkarılabilir olduğunu varsayın.
Platform Tuzakları ve Sorun Giderme
| Belirti | Olası neden | Çözüm |
|---|---|---|
| Android release derlemesi bağlanamıyor, debug çalışıyor | İnternet izni eksik | Ana AndroidManifest.xml dosyasına <uses-permission android:name="android.permission.INTERNET"/> ekleyin |
| macOS uygulaması "Operation not permitted" ile başarısız oluyor | Sandbox giden ağ trafiğini engelliyor | Entitlements dosyalarında com.apple.security.network.client seçeneğini etkinleştirin |
| Web derlemesi hiç bağlanmıyor | HTTPS sayfasında MqttServerClient veya ws:// kullanılıyor |
8084 portunda /mqtt yoluyla wss:// ve MqttBrowserClient kullanın |
| Bağlanıyor, sonra her dakika kopuyor | Keep-alive, boştaki NAT zaman aşımından uzun | keepAlivePeriod değerini 20 ila 30 saniyeye düşürün |
| Uygulama devam ettikten sonra mesajlar duruyor | Soket arka planda kapatıldı, yeniden subscribe yok | autoReconnect ve resubscribeOnAutoReconnect açık kalsın |
| Bağlantı reddedildi, "identifier rejected" | Boş veya yinelenen client ID | Oturum başına benzersiz, boş olmayan bir client ID kullanın |
Mobil işletim sistemleri arka plandaki uygulamaları agresif biçimde askıya alır. iOS'ta uygulama arka plandayken MQTT bağlantısını açık tutmaya çalışmayın; uygulama ön plana döndüğünde yeniden bağlanın ve uykudaki bir telefona ulaşması gereken uyarılar için push bildirimleri kullanın. MQTT keep-alive aralığı rehberi, hücresel ağlar için zaman aşımlarının nasıl ayarlanacağını açıklar.
Kabloda gerçekte ne olduğunu görmeniz gerekirse, ConnectMQ portalı tarayıcı içi bir MQTT client ve canlı bir topic explorer içerir; bu, sorunun uygulamanızda mı yoksa cihazda mı olduğunu doğrulamanın en hızlı yoludur.
ConnectMQ ile Başlayın
Flutter uygulamanız gerçek bir broker'a hazır. app.connectmq.com üzerinde kredi kartı gerekmeden ücretsiz bir Serverless broker oluşturun, üretilen kimlik bilgilerini MqttService içine kopyalayın; Android, iOS, masaüstü ve web derlemelerinizin tamamı aynı güvenli uç noktayı paylaşsın. Ücretsiz katmanın ötesine geçtiğinizde aynı kod ücretli ve dedicated planlarda da çalışır.
Sık Sorulan Sorular
Flutter MQTT, Flutter web'de çalışır mı?
Evet, ancak yalnızca WebSocket üzerinden. 8084 portunda wss://broker.connectmq.com/mqtt ile MqttBrowserClient kullanın; tarayıcılar 1883 veya 8883 portlarına ham TCP bağlantısı açamaz.
mqtt_client paketi MQTT 5'i destekliyor mu?
Hayır, mqtt_client MQTT 3.1.1'i uygular. User properties veya message expiry gibi MQTT 5 özelliklerine ihtiyacınız varsa kardeş paket mqtt5_client'ı kullanın.
Flutter uygulamam arka plandayken MQTT bağlantısını canlı tutabilir miyim?
Mobilde güvenilir şekilde hayır, çünkü iOS ve Android arka plandaki soketleri askıya alır. Uygulama devam ettiğinde yeniden bağlanın ve uygulama kapalıyken kullanıcılara ulaşması gereken uyarılar için push bildirimleri kullanın.
Bir Flutter uygulaması hangi QoS'u kullanmalı?
Kaybetmek istemediğiniz komutlar ve okumalar için QoS 1 iyi bir varsayılandır. Ücretsiz ConnectMQ Serverless planı QoS 1'e kadar destekler; ücretli planlar QoS 2 ekler.



