Flutter+三方库集成+鸿蒙开发:智能扫地机器人控制系统全栈实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

本项目是一套智能扫地机器人专属控制系统,核心采用Flutter搭建跨平台移动端/桌面端控制界面,依托实用三方库实现机器人联动、路径规划、状态监控、指令下发,同时兼容鸿蒙开发(HarmonyOS NEXT) 实现鸿蒙生态设备(手机、平板、智慧屏)对扫地机器人的统一控制。全程提供可落地的详细步骤,从环境搭建到项目打包,零基础也能完成完整项目实践,最终实现:远程控制扫地机器人启动/暂停、模式切换(自动清扫、定点清扫、沿边清扫)、路径可视化、电量/清扫进度监控、故障报警等核心功能。
在这里插入图片描述

一、项目核心技术栈(聚焦扫地机器人场景)

核心围绕智能扫地机器人的控制需求,筛选适配性强、易上手的技术与三方库,避免冗余,确保实战性:

  1. Flutter:跨平台主应用开发(支持Android、iOS、Windows、macOS),一套代码覆盖多端控制界面,无需单独开发多端版本。
  2. Flutter核心三方库(扫地机器人专属)
    • mqtt_client:MQTT协议通信(物联网主流),实现Flutter端与扫地机器人、鸿蒙设备的指令/状态传输(核心三方库)。
    • provider:全局状态管理,统一管理扫地机器人状态(启动/暂停、模式、电量、故障),实现跨页面状态同步。
    • fl_chart:数据可视化,展示扫地机器人清扫面积、电量变化、清扫时长等数据。
    • shared_preferences:本地缓存,存储机器人连接信息、常用清扫模式、历史清扫记录。
    • flutter_map:地图与路径可视化,展示扫地机器人实时位置、清扫路径,支持定点清扫区域选择。
    • connectivity_plus:网络状态监听,提醒用户设备连接状态(WiFi/局域网),避免指令下发失败。
  3. 鸿蒙开发:HarmonyOS NEXT(Stage模型),开发鸿蒙设备控制端,实现智慧屏、鸿蒙手机对扫地机器人的本地/远程控制,兼容鸿蒙分布式能力。
  4. 机器人通信适配:支持主流扫地机器人通信协议(MQTT为主,备用WebSocket),可适配ESP32模拟机器人(无实物也可调试)或真实扫地机器人(小米、科沃斯等开源协议设备)。
  5. 辅助工具:EMQ X(MQTT服务器)、MQTTX(通信调试工具)、DevEco Studio(鸿蒙开发IDE)、VS Code(Flutter开发IDE)。

二、开发环境准备(必做步骤,缺一不可)

所有环境均提供具体下载路径和验证方法,确保新手也能顺利完成配置,重点适配扫地机器人的通信需求。

2.1 Flutter开发环境配置(控制端核心)

  1. 下载Flutter SDK:Flutter官方下载(建议下载稳定版,避免beta版兼容性问题),根据自身系统(Windows/Mac)选择对应版本。
  2. 配置系统环境变量:
    • Windows:右键「此电脑」→属性→高级系统设置→环境变量,将Flutter SDK的bin目录(如:D:\flutter\bin)添加到系统PATH中。
    • Mac:打开终端,编辑~/.bash_profile~/.zshrc,添加命令export PATH=$PATH:/Users/你的用户名/flutter/bin,保存后执行source ~/.bash_profile生效。
  3. 安装开发工具:
    • 安装Android Studio(必装,用于Android模拟器/真机调试),安装时勾选「Android SDK」「Android Emulator」组件。
    • (可选)Mac用户安装Xcode,用于iOS端调试。
    • 推荐安装VS Code,安装Flutter、Dart插件(打开VS Code→扩展→搜索「Flutter」「Dart」,点击安装)。
  4. 环境验证:打开终端/CMD,执行命令:
    flutter doctor
    
    确保无红色错误(黄色警告可忽略),出现「All done!」即为配置成功。

2.2 鸿蒙开发环境配置(鸿蒙设备适配)

  1. 下载DevEco Studio:鸿蒙开发工具官方下载,选择对应系统版本。
  2. 安装OpenHarmony SDK:打开DevEco Studio,按照引导安装SDK,选择API 11及以上版本(HarmonyOS NEXT最新稳定版),勾选「Stage模型」相关组件。
  3. 创建鸿蒙模拟器:打开DevEco Studio→Tools→Device Manager,创建鸿蒙手机/智慧屏模拟器(推荐API 11,分辨率1080*1920),启动模拟器验证环境。
  4. 安装鸿蒙MQTT依赖:提前下载@ohos/mqtt依赖包,后续在项目中直接导入(适配扫地机器人通信)。

2.3 MQTT服务器搭建(机器人通信核心,必做)

扫地机器人、Flutter端、鸿蒙端的所有指令和状态,均通过MQTT服务器传输,推荐使用免费开源的EMQ X服务器,步骤如下:

  1. 下载EMQ X服务器:EMQ X官方下载,选择「EMQ X Open Source」,根据系统选择对应版本。
  2. 启动MQTT服务器:
    • Windows:双击安装包完成安装,启动EMQ X服务(可在服务列表中找到「emqx」,设置为自动启动)。
    • Mac/Linux:终端执行命令sudo emqx start,启动后执行sudo emqx status验证是否启动成功。
  3. 测试服务器:打开浏览器,访问http://127.0.0.1:18083,默认账号admin,密码public,登录后可查看服务器状态。
  4. 安装MQTTX调试工具:MQTTX官方下载,安装后打开,创建连接(服务器地址127.0.0.1,端口1883,无密码),连接成功后即可调试通信。

2.4 扫地机器人模拟(无实物也可调试)

若没有真实扫地机器人,可使用ESP32开发板模拟,步骤简化:

  1. 下载ESP32固件(含MQTT通信功能):可在GitHub搜索「ESP32 扫地机器人模拟固件」,下载后刷入ESP32。
  2. ESP32连接WiFi:配置WiFi名称和密码,确保与Flutter端、鸿蒙端、MQTT服务器在同一局域网。
  3. 验证模拟:通过MQTTX发送指令(如「start」),ESP32会返回「已启动清扫」,模拟机器人响应。

三、Flutter项目创建与三方库集成(核心步骤)

此环节聚焦扫地机器人控制需求,创建标准化项目结构,集成所有核心三方库,每一步均提供具体命令和代码,可直接复制执行。

3.1 创建Flutter智能扫地机器人项目

  1. 打开终端/CMD,执行命令创建新项目(项目名称可自定义,此处以smart_sweeping_robot_flutter为例):
    flutter create smart_sweeping_robot_flutter
    cd smart_sweeping_robot_flutter
    
  2. 打开项目:用VS Code打开创建的项目,删除默认模板代码(lib/main.dart中的默认Widget),保留主入口结构。

3.2 集成核心三方库(扫地机器人专属)

  1. 打开项目根目录下的pubspec.yaml文件,在dependencies节点下添加以下依赖(版本可根据最新稳定版调整):
    dependencies:
      flutter:
        sdk: flutter
      # MQTT通信(核心,机器人指令/状态传输)
      mqtt_client: ^10.2.0
      # 状态管理(机器人状态统一管理)
      provider: ^6.1.1
      # 数据可视化(清扫数据、电量展示)
      fl_chart: ^0.68.0
      # 本地缓存(机器人连接信息、历史记录)
      shared_preferences: ^2.2.3
      # 路径可视化(扫地机器人清扫路径、位置)
      flutter_map: ^6.1.0
      # 网络状态监听(确保设备联网)
      connectivity_plus: ^6.0.1
      # 图标库(扫地机器人相关图标)
      font_awesome_flutter: ^10.4.0
    
  2. 安装三方库:终端执行命令flutter pub get,等待安装完成(若出现报错,可执行flutter pub upgrade更新依赖)。
  3. 验证集成:在main.dart中导入任意一个三方库(如import 'package:provider/provider.dart';),无报错即为集成成功。

3.3 项目基础架构搭建(标准化,便于后续扩展)

创建清晰的目录结构,将功能模块化,避免代码混乱,适配扫地机器人的控制逻辑:

  1. lib目录下创建以下文件夹:
    lib/
    ├── models/          # 数据模型(扫地机器人模型、状态模型、清扫记录模型)
    ├── providers/       # 状态管理(机器人状态、连接状态、清扫数据)
    ├── services/        # 服务层(MQTT通信、本地缓存、网络监听)
    ├── pages/           # 页面(首页、机器人控制页、路径可视化页、设置页)
    ├── widgets/         # 自定义组件(机器人状态卡片、模式切换按钮、路径地图)
    └── main.dart        # 项目入口(初始化MQTT、状态管理、路由)
    
  2. 创建各目录下的基础文件,例如:
    • models/robot_model.dart:扫地机器人数据模型
    • providers/robot_provider.dart:机器人状态管理
    • services/mqtt_service.dart:MQTT通信服务
    • pages/home_page.dart:项目首页

四、Flutter核心功能开发(含三方库实战,扫地机器人专属)

此环节为项目核心,所有功能均围绕智能扫地机器人的控制需求开发,每一步都有完整代码,可直接复制使用,重点演示三方库的实战用法。

4.1 扫地机器人数据模型定义(models/robot_model.dart)

定义机器人的核心属性,适配后续状态管理和UI展示:

// 扫地机器人数据模型
class SweepingRobot {
  final String id; // 机器人唯一ID(用于区分多台设备)
  final String name; // 机器人名称(如:客厅扫地机器人)
  bool isRunning; // 是否正在清扫(启动/暂停)
  String workMode; // 工作模式(auto:自动清扫,point:定点清扫,edge:沿边清扫)
  int battery; // 电量(0-100)
  double cleanArea; // 已清扫面积(单位:㎡)
  int cleanTime; // 已清扫时长(单位:分钟)
  bool isFault; // 是否故障
  String faultMsg; // 故障信息(如:电量过低、卡住)
  List<Map<double, double>> cleanPath; // 清扫路径(坐标集合)

  SweepingRobot({
    required this.id,
    required this.name,
    this.isRunning = false,
    this.workMode = "auto",
    this.battery = 100,
    this.cleanArea = 0.0,
    this.cleanTime = 0,
    this.isFault = false,
    this.faultMsg = "",
    this.cleanPath = const [],
  });

  // 复制模型(用于状态更新,避免直接修改原数据)
  SweepingRobot copyWith({
    String? id,
    String? name,
    bool? isRunning,
    String? workMode,
    int? battery,
    double? cleanArea,
    int? cleanTime,
    bool? isFault,
    String? faultMsg,
    List<Map<double, double>>? cleanPath,
  }) {
    return SweepingRobot(
      id: id ?? this.id,
      name: name ?? this.name,
      isRunning: isRunning ?? this.isRunning,
      workMode: workMode ?? this.workMode,
      battery: battery ?? this.battery,
      cleanArea: cleanArea ?? this.cleanArea,
      cleanTime: cleanTime ?? this.cleanTime,
      isFault: isFault ?? this.isFault,
      faultMsg: faultMsg ?? this.faultMsg,
      cleanPath: cleanPath ?? this.cleanPath,
    );
  }
}

4.2 MQTT通信服务封装(services/mqtt_service.dart)

基于mqtt_client三方库,封装扫地机器人的通信逻辑,实现连接、指令下发、状态接收,核心代码如下:

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

// MQTT通信服务(核心,连接Flutter与扫地机器人、鸿蒙设备)
class MQTTService {
  // MQTT服务器配置(本地服务器,若远程控制可修改为云服务器地址)
  final String server = '127.0.0.1'; // 服务器IP
  final int port = 1883; // MQTT默认端口
  final String clientId = 'flutter_sweeping_robot_client'; // Flutter端客户端ID(唯一)
  late MqttServerClient client; // MQTT客户端实例

  // 初始化MQTT连接(在项目启动时调用)
  Future<bool> initMQTT() async {
    client = MqttServerClient(server, clientId);
    client.port = port;
    client.keepAlivePeriod = 60; // 心跳周期(60秒)
    client.logging(on: true); // 开启日志(调试用)
    client.onConnected = _onConnected; // 连接成功回调
    client.onDisconnected = _onDisconnected; // 断开连接回调
    client.onMessageReceived = _onMessageReceived; // 接收消息回调

    try {
      await client.connect();
      if (client.connectionStatus?.state == MqttConnectionState.connected) {
        print('MQTT连接成功(Flutter端)');
        // 订阅扫地机器人相关主题(接收机器人状态)
        // 主题格式:smart/sweeping_robot/机器人ID/state
        client.subscribe('smart/sweeping_robot/1/state', MqttQos.atMostOnce);
        return true;
      } else {
        print('MQTT连接失败:${client.connectionStatus?.state}');
        client.disconnect();
        return false;
      }
    } catch (e) {
      print('MQTT连接异常:$e');
      client.disconnect();
      return false;
    }
  }

  // 连接成功回调
  void _onConnected() {
    print('MQTT已成功连接到服务器');
  }

  // 断开连接回调
  void _onDisconnected() {
    print('MQTT已断开连接,尝试重连...');
    initMQTT(); // 自动重连
  }

  // 接收消息回调(接收扫地机器人状态、鸿蒙设备指令)
  void _onMessageReceived(MqttMessage message) {
    final payload = MqttPublishMessage.fromMessage(message);
    final messageContent = String.fromCharCodes(payload.payload.message!);
    print('收到MQTT消息:$messageContent');
    // 后续会结合状态管理,解析消息并更新机器人状态
  }

  // 下发控制指令(控制扫地机器人)
  // 指令格式:{"cmd": "start", "mode": "auto"}(start/stop/change_mode等)
  void publishControlCmd(String robotId, Map<String, dynamic> cmd) {
    if (client.connectionStatus?.state != MqttConnectionState.connected) {
      print('MQTT未连接,无法下发指令');
      return;
    }
    final topic = 'smart/sweeping_robot/$robotId/control'; // 控制主题
    final cmdString = cmd.toString(); // 指令转为字符串
    final builder = MqttClientPayloadBuilder();
    builder.addString(cmdString);
    // 发布指令
    client.publishMessage(
      topic,
      MqttQos.atMostOnce,
      builder.payload!,
    );
    print('下发指令:$cmdString,主题:$topic');
  }

  // 断开MQTT连接
  void disconnectMQTT() {
    client.disconnect();
    print('MQTT已主动断开连接');
  }
}

功能说明:通过mqtt_client三方库,实现Flutter端与MQTT服务器的稳定连接,支持下发控制指令(启动、暂停、切换模式),接收扫地机器人的实时状态(电量、清扫进度、故障),同时支持与鸿蒙端互通。

4.3 机器人状态管理(providers/robot_provider.dart)

基于provider三方库,实现全局机器人状态管理,统一处理状态更新、指令下发,核心代码如下:

import 'package:flutter/material.dart';
import '../models/robot_model.dart';
import '../services/mqtt_service.dart';

// 扫地机器人状态管理(全局可访问)
class RobotProvider with ChangeNotifier {
  final MQTTService _mqttService = MQTTService(); // MQTT服务实例
  late SweepingRobot _robot; // 当前控制的扫地机器人
  bool _isMqttConnected = false; // MQTT连接状态

  // 初始化(项目启动时调用)
  Future<void> init() async {
    // 初始化机器人(默认数据,后续会从本地缓存/机器人接收真实数据)
    _robot = SweepingRobot(
      id: '1',
      name: '客厅扫地机器人',
    );
    // 初始化MQTT连接
    _isMqttConnected = await _mqttService.initMQTT();
    // 监听MQTT消息,更新机器人状态
    _mqttService.client.onMessageReceived = _updateRobotState;
    notifyListeners();
  }

  // 从MQTT消息中更新机器人状态
  void _updateRobotState(MqttMessage message) {
    final payload = MqttPublishMessage.fromMessage(message);
    final messageContent = String.fromCharCodes(payload.payload.message!);
    // 解析消息(假设消息格式为JSON字符串)
    // 示例消息:{"isRunning":true,"workMode":"auto","battery":80,"cleanArea":5.2,"cleanTime":10}
    try {
      // 实际开发中可使用json.decode解析,此处简化演示
      final stateMap = _parseMessage(messageContent);
      _robot = _robot.copyWith(
        isRunning: stateMap['isRunning'] ?? false,
        workMode: stateMap['workMode'] ?? 'auto',
        battery: stateMap['battery'] ?? 100,
        cleanArea: stateMap['cleanArea'] ?? 0.0,
        cleanTime: stateMap['cleanTime'] ?? 0,
        isFault: stateMap['isFault'] ?? false,
        faultMsg: stateMap['faultMsg'] ?? '',
        cleanPath: stateMap['cleanPath'] ?? [],
      );
      notifyListeners(); // 通知UI更新
    } catch (e) {
      print('解析机器人状态失败:$e');
    }
  }

  // 简化消息解析(实际使用json.decode)
  Map<String, dynamic> _parseMessage(String message) {
    // 此处仅为演示,实际开发中替换为JSON解析
    if (message.contains('isRunning')) {
      return {
        'isRunning': message.contains('true'),
        'workMode': message.contains('auto') ? 'auto' : 'point',
        'battery': 80,
        'cleanArea': 5.2,
        'cleanTime': 10,
      };
    }
    return {};
  }

  // 控制机器人启动/暂停
  void toggleRobotRunning() {
    final cmd = {
      'cmd': _robot.isRunning ? 'stop' : 'start',
      'mode': _robot.workMode,
    };
    _mqttService.publishControlCmd(_robot.id, cmd);
    // 本地临时更新状态(后续会被机器人真实状态覆盖)
    _robot = _robot.copyWith(isRunning: !_robot.isRunning);
    notifyListeners();
  }

  // 切换机器人工作模式
  void changeWorkMode(String mode) {
    final cmd = {
      'cmd': 'change_mode',
      'mode': mode,
    };
    _mqttService.publishControlCmd(_robot.id, cmd);
    _robot = _robot.copyWith(workMode: mode);
    notifyListeners();
  }

  // 定点清扫(传入定点坐标)
  void pointClean(double x, double y) {
    final cmd = {
      'cmd': 'point_clean',
      'x': x,
      'y': y,
    };
    _mqttService.publishControlCmd(_robot.id, cmd);
    _robot = _robot.copyWith(workMode: 'point');
    notifyListeners();
  }

  // Getter方法(供UI获取状态)
  SweepingRobot get robot => _robot;
  bool get isMqttConnected => _isMqttConnected;
}

功能说明:通过provider三方库,实现全局机器人状态的统一管理,UI页面可通过Provider.of<RobotProvider>(context)获取机器人状态、调用控制方法,无需手动传值,实现状态与UI的同步更新。

4.4 清扫路径可视化(widgets/clean_path_map.dart)

基于flutter_map三方库,实现扫地机器人清扫路径、实时位置的可视化,核心代码如下:

import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import '../providers/robot_provider.dart';
import 'package:provider/provider.dart';

// 清扫路径可视化组件
class CleanPathMap extends StatelessWidget {
  const CleanPathMap({super.key});

  
  Widget build(BuildContext context) {
    final robotProvider = Provider.of<RobotProvider>(context);
    final robot = robotProvider.robot;

    // 路径坐标转换(模拟,实际可根据机器人真实坐标调整)
    List<LatLng> pathPoints = robot.cleanPath.map((point) {
      return LatLng(point.keys.first, point.values.first);
    }).toList();

    return Container(
      height: 300,
      width: double.infinity,
      margin: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(12),
        border: Border.all(color: Colors.grey[300]!),
      ),
      child: FlutterMap(
        options: MapOptions(
          center: pathPoints.isNotEmpty ? pathPoints.first : LatLng(30.0, 120.0),
          zoom: 18.0,
        ),
        children: [
          // 地图瓦片(使用开源瓦片服务)
          TileLayer(
            urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
            subdomains: const ['a', 'b', 'c'],
          ),
          // 清扫路径
          PolylineLayer(
            polylines: [
              Polyline(
                points: pathPoints,
                strokeWidth: 3.0,
                color: Colors.blue,
              ),
            ],
          ),
          // 机器人实时位置
          MarkerLayer(
            markers: [
              Marker(
                point: pathPoints.isNotEmpty ? pathPoints.last : LatLng(30.0, 120.0),
                width: 40,
                height: 40,
                child: const Icon(
                  Icons.room_cleaner,
                  color: Colors.red,
                  size: 32,
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

4.5 主页面开发(pages/home_page.dart)

整合所有核心功能,实现扫地机器人的控制界面,包含状态显示、模式切换、路径可视化等,核心代码如下:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/robot_provider.dart';
import '../widgets/clean_path_map.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  
  Widget build(BuildContext context) {
    final robotProvider = Provider.of<RobotProvider>(context);
    final robot = robotProvider.robot;
    final isConnected = robotProvider.isMqttConnected;

    return Scaffold(
      appBar: AppBar(
        title: const Text('智能扫地机器人控制'),
        centerTitle: true,
        backgroundColor: Colors.blue,
        actions: [
          IconButton(
            icon: const Icon(Icons.settings),
            onPressed: () {
              // 跳转设置页面(后续实现)
            },
          ),
        ],
      ),
      body: SingleChildScrollView(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            // 连接状态提示
            Container(
              width: double.infinity,
              padding: const EdgeInsets.symmetric(vertical: 8),
              color: isConnected ? Colors.green[100] : Colors.red[100],
              child: Text(
                isConnected ? '已连接扫地机器人' : '未连接机器人,请检查网络',
                textAlign: TextAlign.center,
                style: TextStyle(
                  color: isConnected ? Colors.green : Colors.red,
                ),
              ),
            ),

            // 机器人状态卡片
            Container(
              margin: const EdgeInsets.all(16),
              padding: const EdgeInsets.all(16),
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(12),
                boxShadow: [
                  BoxShadow(
                    color: Colors.grey[200]!,
                    blurRadius: 5,
                    offset: const Offset(0, 2),
                  ),
                ],
                color: Colors.white,
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    robot.name,
                    style: const TextStyle(
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 16),
                  // 电量显示
                  Row(
                    children: [
                      const FaIcon(FontAwesomeIcons.batteryFull, color: Colors.green),
                      const SizedBox(width: 8),
                      Text('电量:${robot.battery}%'),
                    ],
                  ),
                  const SizedBox(height: 8),
                  // 工作状态
                  Row(
                    children: [
                      FaIcon(
                        robot.isRunning ? FontAwesomeIcons.play : FontAwesomeIcons.pause,
                        color: robot.isRunning ? Colors.blue : Colors.grey,
                      ),
                      const SizedBox(width: 8),
                      Text(robot.isRunning ? '正在清扫' : '已暂停'),
                    ],
                  ),
                  const SizedBox(height: 8),
                  // 工作模式
                  Row(
                    children: [
                      const FaIcon(FontAwesomeIcons.gears, color: Colors.orange),
                      const SizedBox(width: 8),
                      Text('工作模式:${_getModeText(robot.workMode)}'),
                    ],
                  ),
                  const SizedBox(height: 8),
                  // 清扫数据
                  Row(
                    children: [
                      const FaIcon(FontAwesomeIcons.chartArea, color: Colors.purple),
                      const SizedBox(width: 8),
                      Text('已清扫:${robot.cleanArea}㎡ / ${robot.cleanTime}分钟'),
                    ],
                  ),
                  // 故障提示
                  if (robot.isFault)
                    Padding(
                      padding: const EdgeInsets.only(top: 8),
                      child: Row(
                        children: [
                          const FaIcon(FontAwesomeIcons.triangleExclamation, color: Colors.red),
                          const SizedBox(width: 8),
                          Text(robot.faultMsg, style: const TextStyle(color: Colors.red)),
                        ],
                      ),
                    ),
                ],
              ),
            ),

            // 控制按钮
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                // 启动/暂停按钮
                ElevatedButton(
                  onPressed: isConnected ? () => robotProvider.toggleRobotRunning() : null,
                  style: ElevatedButton.styleFrom(
                    padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12),
                    backgroundColor: robot.isRunning ? Colors.red : Colors.blue,
                  ),
                  child: Text(robot.isRunning ? '暂停清扫' : '启动清扫'),
                ),
                const SizedBox(width: 16),
                // 模式切换按钮
                DropdownButton<String>(
                  value: robot.workMode,
                  onChanged: isConnected
                      ? (value) {
                          if (value != null) {
                            robotProvider.changeWorkMode(value);
                          }
                        }
                      : null,
                  items: const [
                    DropdownMenuItem(value: 'auto', child: Text('自动清扫')),
                    DropdownMenuItem(value: 'point', child: Text('定点清扫')),
                    DropdownMenuItem(value: 'edge', child: Text('沿边清扫')),
                  ],
                ),
              ],
            ),

            // 清扫路径可视化
            const CleanPathMap(),

            // 清扫数据图表(fl_chart三方库)
            Container(
              margin: const EdgeInsets.all(16),
              padding: const EdgeInsets.all(16),
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(12),
                color: Colors.white,
                boxShadow: [
                  BoxShadow(
                    color: Colors.grey[200]!,
                    blurRadius: 5,
                    offset: const Offset(0, 2),
                  ),
                ],
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Text(
                    '清扫数据统计',
                    style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                  ),
                  const SizedBox(height: 16),
                  // 电量变化折线图
                  SizedBox(
                    height: 200,
                    child: LineChart(
                      LineChartData(
                        gridData: const FlGridData(show: true),
                        titlesData: const FlTitlesData(show: true),
                        borderData: const FlBorderData(show: true),
                        lineBarsData: [
                          LineChartBarData(
                            spots: const [
                              FlSpot(0, 100),
                              FlSpot(1, 90),
                              FlSpot(2, 80),
                              FlSpot(3, 75),
                              FlSpot(4, 70),
                            ],
                            isCurved: true,
                            color: Colors.green,
                            title: '电量变化',
                          ),
                        ],
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  // 转换工作模式文本
  String _getModeText(String mode) {
    switch (mode) {
      case 'auto':
        return '自动清扫';
      case 'point':
        return '定点清扫';
      case 'edge':
        return '沿边清扫';
      default:
        return '自动清扫';
    }
  }
}

4.6 项目入口配置(main.dart)

初始化状态管理、MQTT服务,配置路由,核心代码如下:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'providers/robot_provider.dart';
import 'pages/home_page.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => RobotProvider()..init(), // 初始化状态管理和MQTT
      child: MaterialApp(
        title: '智能扫地机器人控制',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: const HomePage(),
        debugShowCheckedModeBanner: false,
      ),
    );
  }
}

五、鸿蒙开发:鸿蒙设备控制端实现(适配扫地机器人)

本环节实现鸿蒙设备(手机、智慧屏)对扫地机器人的控制,采用HarmonyOS NEXT Stage模型,与Flutter端共用MQTT服务器,实现指令互通、状态同步,步骤详细可落地。

5.1 创建鸿蒙扫地机器人控制项目

  1. 打开DevEco Studio,点击「Create Project」,选择「Empty Ability」,点击「Next」。
  2. 配置项目信息:
    • Project Namesmart_sweeping_robot_harmony(自定义)。
    • Bundle Namecom.example.smartsweepingrobot(自定义,需唯一)。
    • Save Location:选择项目保存路径。
    • Compile SDK:选择API 11(HarmonyOS NEXT)。
    • Model:选择「Stage」(最新模型,推荐)。
  3. 点击「Finish」,等待项目初始化完成(首次初始化可能需要下载依赖,耐心等待)。
  4. 清理默认代码:删除pages/index.ets中的默认Widget,保留基础结构。

5.2 鸿蒙MQTT通信集成(核心,与Flutter端互通)

  1. 导入MQTT依赖:将提前下载的@ohos/mqtt依赖包复制到项目的node_modules目录下,或在oh-package.json5中添加依赖:
    "dependencies": {
      "@ohos/mqtt": "^1.0.0"
    }
    
    然后在终端执行npm install,安装依赖。
  2. 创建MQTT服务工具类:在utils目录下创建mqttService.ets,封装鸿蒙端MQTT通信逻辑,核心代码如下:
    import mqtt from '@ohos/mqtt';
    
    // 鸿蒙端MQTT服务(与Flutter端共用服务器,控制扫地机器人)
    export class MqttService {
      private client: mqtt.MqttClient | null = null;
      private server = 'tcp://127.0.0.1:1883'; // MQTT服务器地址(与Flutter端一致)
      private clientId = 'harmony_sweeping_robot_client'; // 鸿蒙端客户端ID(唯一)
      private robotId = '1'; // 扫地机器人ID(与Flutter端一致)
      private controlTopic = `smart/sweeping_robot/${this.robotId}/control`; // 控制主题
      private stateTopic = `smart/sweeping_robot/${this.robotId}/state`; // 状态主题
    
      // 初始化MQTT连接
      async initMqtt(): Promise<boolean> {
        try {
          this.client = mqtt.connect(this.server, {
            clientId: this.clientId,
            keepalive: 60,
            clean: true,
          });
    
          // 连接成功回调
          this.client.on('connect', () => {
            console.log('鸿蒙端MQTT连接成功');
            // 订阅机器人状态主题
            this.client?.subscribe(this.stateTopic, { qos: 0 });
          });
    
          // 接收消息回调(接收机器人状态)
          this.client.on('message', (topic: string, payload: Buffer) => {
            const message = payload.toString();
            console.log(`鸿蒙端收到消息:${message}`);
            // 后续更新UI状态
          });
    
          // 断开连接回调
          this.client.on('close', () => {
            console.log('鸿蒙端MQTT断开连接,尝试重连');
            this.initMqtt();
          });
    
          return true;
        } catch (e) {
          console.log(`鸿蒙端MQTT连接失败:${e}`);
          return false;
        }
      }
    
      // 下发控制指令(与Flutter端指令格式一致)
      publishControlCmd(cmd: Record<string, any>): void {
        if (!this.client || !this.client.connected) {
          console.log('MQTT未连接,无法下发指令');
          return;
        }
        const cmdString = JSON.stringify(cmd);
        this.client.publish(this.controlTopic, cmdString, { qos: 0 });
        console.log(`鸿蒙端下发指令:${cmdString}`);
      }
    
      // 断开MQTT连接
      disconnectMqtt(): void {
        this.client?.end();
        console.log('鸿蒙端MQTT已断开连接');
      }
    }
    

5.3 鸿蒙控制界面开发(适配扫地机器人)

开发鸿蒙端控制界面,实现与Flutter端一致的功能:启动/暂停、模式切换、状态显示,核心代码(pages/index.ets)如下:

import { MqttService } from '../utils/mqttService';
import router from '@ohos.router';

@Entry
@Component
struct SweepingRobotControlPage {
  // 状态管理
  @State isConnected: boolean = false;
  @State isRunning: boolean = false;
  @State workMode: string = 'auto'; // auto/point/edge
  @State battery: number = 100;
  @State cleanArea: number = 0.0;
  @State cleanTime: number = 0;
  private mqttService: MqttService = new MqttService();

  // 页面初始化时调用
  aboutToAppear() {
    this.initMqtt();
  }

  // 初始化MQTT
  async initMqtt() {
    this.isConnected = await this.mqttService.initMqtt();
    // 监听机器人状态(简化演示,实际解析MQTT消息更新状态)
    this.mqttService.client?.on('message', (topic: string, payload: Buffer) => {
      const state = JSON.parse(payload.toString());
      this.isRunning = state.isRunning ?? false;
      this.workMode = state.workMode ?? 'auto';
      this.battery = state.battery ?? 100;
      this.cleanArea = state.cleanArea ?? 0.0;
      this.cleanTime = state.cleanTime ?? 0;
    });
  }

  // 启动/暂停清扫
  toggleRunning() {
    const cmd = {
      cmd: this.isRunning ? 'stop' : 'start',
      mode: this.workMode
    };
    this.mqttService.publishControlCmd(cmd);
    this.isRunning = !this.isRunning;
  }

  // 切换工作模式
  changeMode(mode: string) {
    const cmd = {
      cmd: 'change_mode',
      mode: mode
    };
    this.mqttService.publishControlCmd(cmd);
    this.workMode = mode;
  }

  build() {
    Column() {
      // 顶部导航栏
      Text('智能扫地机器人控制')
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 30, bottom: 20 })
        .textAlign(TextAlign.Center);

      // 连接状态提示
      Text(this.isConnected ? '已连接机器人' : '未连接,请检查网络')
        .fontSize(16)
        .color(this.isConnected ? Color.Green : Color.Red)
        .margin({ bottom: 20 })
        .textAlign(TextAlign.Center);

      // 状态卡片
      Column() {
        Text(`设备名称:客厅扫地机器人`)
          .fontSize(18)
          .margin({ bottom: 10 });
        Text(`电量:${this.battery}%`)
          .fontSize(18)
          .margin({ bottom: 10 });
        Text(`状态:${this.isRunning ? '正在清扫' : '已暂停'}`)
          .fontSize(18)
          .margin({ bottom: 10 });
        Text(`模式:${this.getModeText()}`)
          .fontSize(18)
          .margin({ bottom: 10 });
        Text(`已清扫:${this.cleanArea}㎡ / ${this.cleanTime}分钟`)
          .fontSize(18);
      }
      .padding(20)
      .backgroundColor(Color.White)
      .borderRadius(12)
      .shadow({ radius: 5, color: Color.Grey[200]!, offset: { x: 0, y: 2 } })
      .margin({ bottom: 20 })
      .width('90%');

      // 控制按钮
      Row() {
        Button(this.isRunning ? '暂停清扫' : '启动清扫')
          .width(150)
          .height(50)
          .backgroundColor(this.isRunning ? Color.Red : Color.Blue)
          .fontSize(18)
          .onClick(() => this.toggleRunning())
          .enabled(this.isConnected);

        Button('切换模式')
          .width(150)
          .height(50)
          .backgroundColor(Color.Orange)
          .fontSize(18)
          .margin({ left: 20 })
          .onClick(() => this.showModeDialog())
          .enabled(this.isConnected);
      }
      .justifyContent(FlexAlign.Center)
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.Grey[100]);
  }

  // 转换模式文本
  getModeText(): string {
    switch (this.workMode) {
      case 'auto':
        return '自动清扫';
      case 'point':
        return '定点清扫';
      case 'edge':
        return '沿边清扫';
      default:
        return '自动清扫';
    }
  }

  // 模式选择弹窗
  showModeDialog() {
    AlertDialog.show({
      title: '选择工作模式',
      message: '',
      actions: [
        {
          text: '自动清扫',
          onClick: () => {
            this.changeMode('auto');
            AlertDialog.close();
          }
        },
        {
          text: '定点清扫',
          onClick: () => {
            this.changeMode('point');
            AlertDialog.close();
          }
        },
        {
          text: '沿边清扫',
          onClick: () => {
            this.changeMode('edge');
            AlertDialog.close();
          }
        }
      ]
    });
  }
}

5.4 Flutter与鸿蒙设备互通(关键步骤)

确保Flutter端、鸿蒙端、扫地机器人(或模拟设备)连接同一MQTT服务器,实现双向通信,步骤如下:

  1. 确认所有设备在同一局域网(WiFi),MQTT服务器地址统一为127.0.0.1:1883(若为远程控制,替换为云服务器地址)。
  2. 统一通信主题:
    • 控制主题smart/sweeping_robot/1/control(Flutter端、鸿蒙端均向此主题下发指令)。
    • 状态主题smart/sweeping_robot/1/state(扫地机器人向此主题发送状态,Flutter端、鸿蒙端订阅此主题)。
  3. 测试互通:
    • Flutter端点击「启动清扫」,下发指令 → MQTT服务器 → 扫地机器人(或模拟设备)执行,同时向状态主题发送「正在清扫」状态 → Flutter端、鸿蒙端同步更新UI。
    • 鸿蒙端切换「沿边清扫」模式,下发指令 → 扫地机器人执行 → Flutter端UI同步更新模式。

六、完整项目联调测试(必做,确保功能可用)

联调测试分为「无实物模拟测试」和「实物机器人测试」,新手可先进行模拟测试,熟悉流程后再接入真实设备。

6.1 无实物模拟测试(推荐新手)

  1. 启动MQTT服务器:打开EMQ X服务器,确保状态为「运行中」,通过MQTTX工具连接服务器(地址127.0.0.1:1883)。
  2. 运行Flutter项目:
    • 打开Android Studio或VS Code,启动Android模拟器(或连接真机)。
    • 执行命令flutter run,启动Flutter应用,验证MQTT连接状态、控制功能、路径可视化、数据图表是否正常。
  3. 运行鸿蒙项目:
    • 打开DevEco Studio,启动鸿蒙模拟器(或连接鸿蒙真机)。
    • 点击「Run」按钮,启动鸿蒙应用,验证MQTT连接、控制功能是否正常。
  4. 双向通信测试:
    • 在Flutter端点击「启动清扫」,查看鸿蒙端UI是否同步更新为「正在清扫」。
    • 在鸿蒙端切换「定点清扫」模式,查看Flutter端UI是否同步更新模式。
    • 通过MQTTX向状态主题发送模拟状态消息(如{"isRunning":true,"battery":70}),验证两端UI是否同步更新。

6.2 实物机器人测试(进阶)

  1. 准备真实扫地机器人:选择支持MQTT协议的扫地机器人(如小米、科沃斯部分型号),或使用ESP32开发板+电机模块搭建简易扫地机器人。
  2. 配置机器人MQTT:将机器人的MQTT服务器地址、端口、主题配置为与Flutter端、鸿蒙端一致。
  3. 连接机器人:确保机器人与Flutter端、鸿蒙端在同一局域网,启动机器人,验证MQTT连接。
  4. 功能测试:
    • 远程控制机器人启动/暂停、切换模式,验证机器人是否正常响应。
    • 查看机器人实时状态(电量、清扫进度、路径),验证两端UI是否同步更新。
    • 测试故障报警:模拟机器人卡住、电量过低,验证两端是否弹出故障提示。

七、项目打包与部署(实战收尾)

7.1 Flutter项目打包

  1. Android打包
    • 终端执行命令flutter build apk,生成APK文件(路径:build/app/outputs/flutter-apk/app-release.apk)。
    • (可选)生成App Bundle:flutter build appbundle,用于上传Google Play。
  2. iOS打包(Mac环境):
    • 终端执行命令flutter build ios,生成iOS安装包,通过Xcode上传至App Store。
  3. Windows/macOS打包
    • Windows:flutter build windows,生成exe安装包。
    • macOS:flutter build macos,生成dmg安装包。

7.2 鸿蒙项目打包

  1. 打开DevEco Studio,点击「Build」→「Build Hap(s)/APP(s)」→「Build Hap(s)」。
  2. 等待打包完成,生成HAP文件(路径:build/outputs/hap/debug)。
  3. (可选)生成APP包:点击「Build APP(s)」,用于上传鸿蒙应用市场。

7.3 MQTT服务器部署(远程控制)

  1. 选择云服务器(如阿里云、腾讯云),安装EMQ X服务器。
  2. 配置服务器安全组,开放1883(MQTT)、18083(EMQ X控制台)端口。
  3. 修改Flutter端、鸿蒙端、机器人的MQTT服务器地址为云服务器公网IP,实现远程控制。

八、项目扩展与优化(进阶方向)

  1. 多设备管理:扩展支持多台扫地机器人,通过ID区分,实现批量控制。
  2. 语音控制:集成speech_to_text三方库,实现语音指令控制(如「启动清扫」「切换模式」)。
  3. 定时任务:添加定时清扫功能,支持设置清扫时间、重复周期。
  4. 数据统计:完善清扫数据统计,生成周/月清扫报告,支持数据导出。
  5. 鸿蒙分布式能力:利用鸿蒙分布式技术,实现跨设备协同控制(如手机控制、智慧屏显示)。
  6. 安全性优化:添加MQTT用户名/密码认证,加密通信数据,防止指令被篡改。

九、常见问题与解决方案

  1. MQTT连接失败
    • 检查服务器地址、端口是否正确,确保所有设备在同一局域网。
    • 关闭防火墙/杀毒软件,或开放1883端口。
    • 验证EMQ X服务器是否正常运行,通过http://127.0.0.1:18083查看状态。
  2. Flutter三方库安装报错
    • 执行flutter pub upgrade更新依赖版本。
    • 清理缓存:flutter clean,然后重新执行flutter pub get
  3. 鸿蒙MQTT依赖导入失败
    • 确认@ohos/mqtt依赖包版本与API 11兼容。
    • 执行npm install重新安装依赖,或手动复制依赖包到node_modules目录。
  4. 路径可视化不显示
    • 检查flutter_map依赖是否正确导入,确保网络正常(加载地图瓦片需要联网)。
    • 验证路径坐标格式是否正确,LatLng类导入是否正确。

本项目完整覆盖Flutter+三方库+鸿蒙开发全栈技术,从环境搭建到功能开发、联调测试、打包部署,提供了可直接复刻的详细步骤,适合零基础开发者学习物联网跨平台开发。通过本项目,可掌握Flutter跨平台开发、三方库实战、鸿蒙开发、MQTT通信等核心技能,为后续智能家居、物联网项目开发打下坚实基础。

需要我帮你把这份文档整理成可直接复制的完整项目代码包(含Flutter与鸿蒙双端完整源码、依赖配置、MQTT调试脚本)吗?

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐