基于Flutter的水位监测APP与ESP模块数据交互方案问询
水位监测IoT APP开发求助
我正在Android Studio(Hedgehog版本)中使用Flutter开发一款水位监测APP,该项目属于IoT范畴。已完成水位指示器的UI设计,了解Blynk APP,希望打造一款匹配自定义UI、类似Blynk的APP实现水位监测。我具备ESP模块控制LED和继电器的经验,当前核心需求是实现ESP模块与移动端APP之间的数据收发。曾参考过IoT平台搭建教程,但因ESP和云技术基础薄弱难以理解,现寻求清晰的行动路线指导。
我的UI代码
import 'package:flutter/material.dart'; class RowWithTwoColumns extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Container( child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Transform.translate( offset: const Offset(20, 40), child: //Image.asset('assets/images/blue_1.png',width: 30,),), Image.asset( 'assets/images/blue_1.png', width: 30, ), ), Transform.translate( offset: const Offset(20, 80), child: Image.asset( 'assets/images/green.png', width: 30, ), ), Transform.translate( offset: const Offset(20, 120), child: Image.asset( 'assets/images/green.png', width: 30, ), ), Transform.translate( offset: const Offset(20, 160), child: Image.asset( 'assets/images/green.png', width: 30, ), ), Transform.translate( offset: const Offset(20, 200), child: Image.asset( 'assets/images/red_1.png', width: 30, ), ), Transform.translate( offset: const Offset(20, 240), child: Image.asset( 'assets/images/red_1.png', width: 30, ), ), Transform.translate( offset: const Offset(20, 280), child: Image.asset( 'assets/images/red_1.png', width: 30, ), ), Transform.translate( offset: const Offset(20, 300), child: Text( 'WATER\nLEVEL', textAlign: TextAlign.center, ), ), ], ), Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Transform.translate( offset: const Offset(20, 40), child: Text( '100 %', style: TextStyle(fontSize: 20), ), ), Transform.translate( offset: const Offset(20, 80), child: Text( '90 %', style: TextStyle(fontSize: 20), ), ), Transform.translate( offset: const Offset(20, 123), child: Text( '75 %', style: TextStyle(fontSize: 20), ), ), Transform.translate( offset: const Offset(20, 163), child: Text( '60 %', style: TextStyle(fontSize: 20), ), ), Transform.translate( offset: const Offset(20, 204), child: Text( '45 %', style: TextStyle(fontSize: 20), ), ), Transform.translate( offset: const Offset(20, 243), child: Text( '30 %', style: TextStyle(fontSize: 20), ), ), Transform.translate( offset: const Offset(20, 285), child: Text( '15 %', style: TextStyle(fontSize: 20), ), ), ], ), Column(mainAxisAlignment: MainAxisAlignment.start, children: [ Transform.translate( offset: const Offset(0, 32), child: SwitchScreen(), ), Transform.translate( offset: const Offset(0, 55), child: SwitchScreen(), ), Transform.translate( offset: const Offset(0, 75), child: SwitchScreen(), ), Transform.translate( offset: const Offset(0, 98), child: SwitchScreen(), ), Transform.translate( offset: const Offset(0, 120), child: SwitchScreen(), ), Transform.translate( offset: const Offset(0, 137), child: SwitchScreen(), ), Transform.translate( offset: const Offset(0, 162), child: SwitchScreen(), ), Transform.translate( offset: const Offset(0, 175), child: Text( 'ON \n NOTIFY', textAlign: TextAlign.center, ), ), ]), Column(mainAxisAlignment: MainAxisAlignment.start, children: [ Transform.translate( offset: const Offset(0, 32), child: SwitchScreen(), ), Transform.translate( offset: const Offset(0, 55), child: SwitchScreen(), ), Transform.translate( offset: const Offset(0, 75), child: SwitchScreen(), ), Transform.translate( offset: const Offset(0, 98), child: SwitchScreen(), ), Transform.translate( offset: const Offset(0, 120), child: SwitchScreen(), ), Transform.translate( offset: const Offset(0, 137), child: SwitchScreen(), ), Transform.translate( offset: const Offset(0, 162), child: SwitchScreen(), ), Transform.translate( offset: const Offset(0, 175), child: Text( 'OFF \n NOTIFY', textAlign: TextAlign.center, ), ), ]), ], ), ), appBar: AppBar( title: Text( 'Water Level Indicator', style: TextStyle(color: Colors.white), ), backgroundColor: Colors.blueAccent, centerTitle: true, )), ); } } class SwitchScreen extends StatefulWidget { @override SwitchClass createState() => new SwitchClass(); } class SwitchClass extends State { bool isSwitched = false; void toggleSwitch(bool value) { if (isSwitched == false) { setState(() { isSwitched = true; }); } else { setState(() { isSwitched = false; }); } } @override Widget build(BuildContext context) { return Column(mainAxisAlignment: MainAxisAlignment.center, children: [ Transform.scale( scale: 1, child: Switch( onChanged: toggleSwitch, value: isSwitched, )), ]); } }
UI截图

行动路线指导
一、先从本地WiFi通信入手(避开复杂云平台)
因为你ESP基础薄弱,优先选局域网HTTP通信,不需要云服务器,代码简单易调试,能快速跑通数据收发:
- ESP作为WiFi服务器,提供数据接口;Flutter APP作为客户端,调用接口获取水位、发送控制指令
二、ESP端开发步骤
配置WiFi连接
用Arduino IDE编写代码,让ESP连接到你的家庭WiFi,获取局域网IP:#include <WiFi.h> const char* ssid = "你的WiFi名称"; const char* password = "WiFi密码"; void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nWiFi连接成功,IP地址:"); Serial.println(WiFi.localIP()); // 记录这个IP,APP要用到 } void loop() {}搭建HTTP服务器,实现数据接口
添加WebServer库,实现两个核心接口:/getWaterLevel:返回当前水位数据/setSwitch:接收APP的开关控制指令
示例代码(ESP32):
#include <WiFi.h> #include <WebServer.h> const char* ssid = "你的WiFi名称"; const char* password = "WiFi密码"; WebServer server(80); // 模拟水位数据,实际替换为传感器读数 int waterLevel = 50; // 处理水位请求 void handleGetWaterLevel() { server.send(200, "text/plain", String(waterLevel)); } // 处理开关指令 void handleSetSwitch() { if (server.hasArg("state")) { String state = server.arg("state"); // 这里根据state控制继电器/LED,比如state=="1"触发开操作 Serial.print("收到开关指令:"); Serial.println(state); server.send(200, "text/plain", "success"); } else { server.send(400, "text/plain", "缺少state参数"); } } void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nWiFi连接成功,IP地址:"); Serial.println(WiFi.localIP()); // 注册接口 server.on("/getWaterLevel", handleGetWaterLevel); server.on("/setSwitch", handleSetSwitch); server.begin(); Serial.println("HTTP服务器已启动"); } void loop() { server.handleClient(); // 模拟水位变化,实际替换为传感器读取逻辑 waterLevel = random(10, 100); delay(1000); }接入水位传感器
将代码中的模拟水位数据替换为实际传感器(如超声波、液位传感器)的读数,确保传感器数据能正常输出到串口。
三、Flutter APP端开发步骤
添加网络依赖
在pubspec.yaml中添加http包:dependencies: flutter: sdk: flutter http: ^1.1.0执行
flutter pub get安装依赖。实现网络请求逻辑
添加工具函数,用于获取水位和发送开关指令:import 'package:http/http.dart' as http; // 获取水位数据 Future<int> fetchWaterLevel(String espIp) async { try { final response = await http.get(Uri.parse('http://$espIp/getWaterLevel')); if (response.statusCode == 200) { return int.parse(response.body); } } catch (e) { print("获取水位失败:$e"); } return 0; } // 发送开关指令 Future<void> sendSwitchCommand(String espIp, bool isOn) async { try { final state = isOn ? "1" : "0"; await http.get(Uri.parse('http://$espIp/setSwitch?state=$state')); } catch (e) { print("发送指令失败:$e"); } }关联UI与网络逻辑
- 将
RowWithTwoColumns改为StatefulWidget,以便更新UI状态 - 在
initState中启动定时任务,每隔2-3秒获取一次水位数据,更新UI上的百分比和指示灯 - 修改
SwitchClass的toggleSwitch方法,调用sendSwitchCommand发送指令
- 将
四、测试与调试
- 确保手机和ESP连接到同一WiFi网络
- 上传ESP代码,打开串口监视器确认WiFi连接成功并记录IP
- 运行Flutter APP,替换代码中的ESP IP,测试水位显示和开关控制功能
- 若连接失败,检查IP是否正确、WiFi信号是否稳定、设备是否被防火墙拦截
五、进阶远程通信(可选)
当本地通信跑通后,如需远程监控,推荐使用现成IoT云平台(比自建简单):
- 可选平台:阿里云IoT、腾讯云IoT、ThingSpeak
- 核心步骤:
- 在云平台创建设备,获取设备证书
- ESP端接入云平台SDK,上报水位数据
- Flutter APP通过云平台API获取数据、发送控制指令
内容的提问来源于stack exchange,提问作者Xeyad
相关产品推荐
相关产品推荐

