You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Flutter集成HiveMQ MQTT Dart类实现发布订阅功能故障排查求助

Fixing MQTT Client Integration in Flutter

Let's break down why your MQTT client isn't working in Flutter and fix it step by step.

Key Issues in Your Current Code

  1. Repeated Client Initialization: Every time you tap the button, you call prepareMqttClient() which re-creates the MQTT client and tries to connect again. This causes connection conflicts and unexpected behavior.
  2. Unawaited Async Operations: prepareMqttClient() is an async method, but you call it without await, so subscription/publish actions run before the connection is fully established.
  3. Hardcoded Publish Logic: The initial publish is tied to client setup, but you need to trigger dynamic publishes on button taps instead.

Modified MQTT Client Wrapper

First, update your MQTTClientWrapper to separate initialization, connection, and publish logic, plus add connection safeguards:

import 'dart:io';
import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqtt_client/mqtt_server_client.dart';

enum MqttCurrentConnectionState { IDLE, CONNECTING, CONNECTED, DISCONNECTED, ERROR_WHEN_CONNECTING }
enum MqttSubscriptionState { IDLE, SUBSCRIBED }

class MQTTClientWrapper {
  MqttServerClient? client;
  MqttCurrentConnectionState connectionState = MqttCurrentConnectionState.IDLE;
  MqttSubscriptionState subscriptionState = MqttSubscriptionState.IDLE;

  // Initialize client, connect, and subscribe once
  Future<void> initClient() async {
    if (client != null && connectionState == MqttCurrentConnectionState.CONNECTED) {
      return; // Skip if already connected
    }
    _setupClient();
    await _connect();
    if (connectionState == MqttCurrentConnectionState.CONNECTED) {
      _subscribeToTopic('Dart/Mqtt_client/testtopic');
    }
  }

  Future<void> _connect() async {
    try {
      print('Connecting to MQTT broker...');
      connectionState = MqttCurrentConnectionState.CONNECTING;
      // Replace with your HiveMQ credentials
      await client!.connect('YOUR_HIVEMQ_USERNAME', 'YOUR_HIVEMQ_PASSWORD');
    } on Exception catch (e) {
      print('Connection failed: $e');
      connectionState = MqttCurrentConnectionState.ERROR_WHEN_CONNECTING;
      client!.disconnect();
    }

    if (client!.connectionStatus?.state == MqttConnectionState.connected) {
      connectionState = MqttCurrentConnectionState.CONNECTED;
      print('Successfully connected to broker');
    } else {
      print('Connection failed: ${client!.connectionStatus}');
      connectionState = MqttCurrentConnectionState.ERROR_WHEN_CONNECTING;
      client!.disconnect();
    }
  }

  void _setupClient() {
    // Replace with your HiveMQ host and client ID
    client = MqttServerClient.withPort(
        'YOUR_HIVEMQ_HOST.hivemq.cloud', 'YOUR_UNIQUE_CLIENT_ID', 8883);
    client!.secure = true;
    client!.securityContext = SecurityContext.defaultContext;
    client!.keepAlivePeriod = 20;
    client!.autoReconnect = true; // Auto-reconnect on disconnection
    client!.onDisconnected = _onDisconnected;
    client!.onConnected = _onConnected;
    client!.onSubscribed = _onSubscribed;
  }

  void _subscribeToTopic(String topicName) {
    print('Subscribing to topic: $topicName');
    client!.subscribe(topicName, MqttQos.atMostOnce);
    client!.updates.listen((List<MqttReceivedMessage<MqttMessage>> messages) {
      final payload = messages[0].payload as MqttPublishMessage;
      final message = MqttPublishPayload.bytesToStringAsString(payload.payload.message);
      print('New message received: $message');
      // Add logic here to update your Flutter UI if needed
    });
  }

  // Public method to publish messages dynamically
  void publishMessage(String topic, String message) {
    if (client == null || connectionState != MqttCurrentConnectionState.CONNECTED) {
      print('Cannot publish: MQTT client not connected');
      return;
    }
    final builder = MqttClientPayloadBuilder();
    builder.addString(message);
    print('Publishing message "$message" to topic: $topic');
    client!.publishMessage(topic, MqttQos.exactlyOnce, builder.payload!);
  }

  // Callback handlers
  void _onSubscribed(String topic) {
    print('Subscribed to topic: $topic');
    subscriptionState = MqttSubscriptionState.SUBSCRIBED;
  }

  void _onDisconnected() {
    print('Client disconnected');
    connectionState = MqttCurrentConnectionState.DISCONNECTED;
  }

  void _onConnected() {
    connectionState = MqttCurrentConnectionState.CONNECTED;
    print('Client connected successfully');
  }
}

Updated Flutter UI Code

Now modify your Flutter state class to initialize the MQTT client once, and only trigger publishes on button taps:

import 'package:flutter/material.dart';
import 'package:mqtt_test/mqttService/mqtt_strm.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MQTT PUB SUB Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const MyHomePage(title: 'MQTT Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final MQTTClientWrapper _mqttClient = MQTTClientWrapper();
  bool _counter = true;
  final String _pubTopic = "testTopic";

  @override
  void initState() {
    super.initState();
    // Initialize MQTT client once when the widget loads
    _initMqttClient();
  }

  Future<void> _initMqttClient() async {
    await _mqttClient.initClient();
  }

  void _toggleAndPublish() {
    setState(() {
      _counter = !_counter;
      // Publish message based on toggle state
      _mqttClient.publishMessage(_pubTopic, _counter ? "1" : "0");
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('$_counter', style: Theme.of(context).textTheme.headline4),
            const Text('Published to MQTT'),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _toggleAndPublish,
        tooltip: 'Toggle & Publish',
        child: const Icon(Icons.add),
      ),
    );
  }
}

Critical Fixes Explained

  1. Single Initialization: The MQTT client is initialized once in initState() instead of on every button tap, preventing duplicate connections.
  2. Async/Await Handling: We properly await the client initialization to ensure connection is ready before publishing.
  3. Dynamic Publishing: The publishMessage method is now a separate public method, allowing you to send custom messages on button taps.
  4. Connection Safeguards: We added checks to ensure we only publish when the client is connected, and enabled auto-reconnect for better reliability.

Final Checks

  • Replace all placeholder values (YOUR_HIVEMQ_USERNAME, YOUR_HIVEMQ_PASSWORD, etc.) with your actual HiveMQ credentials.
  • Ensure your Flutter app has internet permission:
    • For Android: Add <uses-permission android:name="android.permission.INTERNET"/> to AndroidManifest.xml.
    • For iOS: No extra setup needed for TLS connections (HiveMQ uses TLS by default).

内容的提问来源于stack exchange,提问作者Jephtah Jephtah

火山引擎 最新活动