IoT Eğitimleri

Flutter MQTT Client: Platformlar Arası Bir IoT Uygulaması Geliştirme

mqtt_client paketiyle Android, iOS, masaüstü ve web derlemelerini TLS veya güvenli WebSocket üzerinden bir bulut broker'a bağlayan, canlı sensör verisi okuyan ve bir cihazı kontrol eden uygulamalı bir Flutter MQTT eğitimi.

Bulut broker'a bağlı, canlı sensör değerlerini ve bir cihaz anahtarını telefonda ve tarayıcıda gösteren Flutter MQTT uygulaması
Bunu kendi MQTT broker’ınızda çalıştırınFree planla özel, yönetilen bir MQTT broker alın ve hazır kod örnekleriyle başlayın.
Ücretsiz deneyin Kod örnekleri Kart gerekmez

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:

  1. ConnectMQ portalına kaydolun, bir plan seçin ve bir broker oluşturun.
  2. Üretilen kullanıcı adını, parolayı ve client ID'yi kopyalayın.
  3. Servisi bunlarla oluşturun: MqttService(username: 'YOUR_USERNAME', password: 'YOUR_PASSWORD', clientId: 'YOUR_CLIENT_ID', topicPrefix: 'acme/site1').
  4. 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.

İlgili yazılar

Test, Betik ve Hata Ayıklama için En İyi MQTT CLI Araçları

mosquitto_pub/sub, MQTTX CLI, HiveMQ MQTT CLI, MQTT.js CLI, curl ve mqttui'nin uygulamalı karşılaştırması; betik yazma ve hata ayıklama için doğru aracı seçebilmeniz için aynı test broker.connectmq.com üzerinde çalıştırıldı.

7 dk okuma

Tüm yazılar →

Özel MQTT broker’ınız birkaç tık uzağınızda

Hesabınızı oluşturun, broker kimlik bilgilerinizi alın ve ilk cihazınızı bağlayın — kurulacak, yamalanacak ya da ölçeklendirilecek sunucu yok.