ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

鸿蒙Flutter学习Day1:用智能家居监控模型掌握Dart变量与基本类型

鸿蒙Flutter学习Day1:用智能家居监控模型掌握Dart变量与基本类型 写这篇学习记录之前先交代一下背景今天是“鸿蒙跨端框架Flutter学习”系列的第一天主题是变量与基本类型练习载体是一套智能家居监控模型。我用这个场景不是想蹭智能家居的热度而是因为变量和类型这种基础概念光靠“抄代码”根本记不住必须放到一个能看得见数据的模型里你才知道每个类型到底在管什么。温度用double、电量用int、设备名用String这些不是死记的规则而是数据分析时的自然选择。这篇内容适合两类人一是刚接触 Flutter、对 Dart 语言还不太熟的新手二是已经写过其他语言、想快速了解 Dart 和 Flutter 在变量处理上有哪些特殊之处的开发者。我会从环境准备讲到代码落地再讲到第一天最容易踩的坑尽量让你看完就能照着搭一套自己的监控模型。1 为什么用“智能家居监控模型”来练变量与基本类型1.1 一个真实场景胜过十个理论案例变量和基本类型是几乎所有编程语言的第一课也是最容易被低估的一课。很多同学能背出int是整数、double是浮点数、String是字符串但一上手就乱。问题出在哪里出在这些概念没有跟真实数据挂钩。智能家居监控模型天然适合做切入口。你想想看一个典型的家庭监控界面里有什么温度、湿度、PM2.5、设备开关状态、电池电量、告警信息。这些数据本身就是不同类型而且含义非常明确。温度 26.5 摄氏度一定得是浮点数因为小数点后面有值设备电量 78%通常取整数百分比设备名称“客厅空调”一定是字符串设备是否在线只能是 true/false多条告警信息适合放进一个字符串列表传感器的一组读数用键值对映射最方便我刻意选择这个模型就是想让你在第一天就建立一种“数据驱动”的直觉看到一块数据先想它属于什么类型再想用什么变量去承接。这个习惯比记住十条规定都有用。1.2 鸿蒙、Flutter、Dart 的关系先理顺标题里写“鸿蒙跨端框架Flutter”这句话实际包含了几层意思。鸿蒙是当前终端生态里一个很重要的方向很多开发团队在做技术选型时都会把鸿蒙纳入评估范围Flutter 是跨端 UI 框架它会用 Dart 语言来写业务逻辑和界面Flutter 官方还支持 Android、iOS、Web 等平台这意味着你把界面和逻辑写在 Flutter 层后续迁移到不同平台工作量相对可控。Day 1 的重点是先把 Dart 语言的变量功底打牢。今天写的智能家居监控模型业务逻辑部分放到任何支持 Flutter 的环境里都能跑这一点也正好体现了“跨端”的意义——你不用为每个平台单独维护一套数据模型。至于鸿蒙设备的具体联调方式不同厂商的工具链差异比较大后面等逻辑成熟了再单独开一篇聊。1.3 环境准备从 flutter create 跑起来如果你还没装过 Flutter 环境先把官网推荐的步骤走一遍。装好 Flutter SDK 之后在终端里运行flutter doctor检查一下依赖是不是齐全。这一步会告诉你缺了哪些东西最常见的就是 Android Studio 或者 Android SDK 路径没配好。接着用命令行创建一个新项目flutter create smart_home_monitor进入目录后用编辑器打开lib/main.dart这就是你第一天要反复改的文件。运行平台方面新手优先用 Android 模拟器界面刷新很快没有模拟器的话用 Chrome 跑 Web 版也行只是个别桌面端组件会有细微差异。今天不碰复杂的 UI先把数据模型写在main.dart里跑通一个最简单的页面就行。注意flutter create之后会自动生成一大坨示例代码你可能觉得眼花。不用纠结直接把main.dart清空按后续步骤重写。2 Dart 变量到底怎么声明基本类型怎么选2.1 var、final、const 三兄弟选哪个Dart 里声明变量最常见的三个关键字是var、final、const第一天很容易混。var表示“类型推断”。你写成var temperature 26.5Dart 会自动推断出temperature是一个double。之后你再赋一个字符串给它编译器会直接报错。这让初学者挺意外——var 不是 JavaScript 里那种百变的变量类型一旦确定就不变了。final表示“只能赋值一次”。比如设备编号final String deviceId dev-001;这个值初始化后就不允许再改。它不是常量但它对你是一个约束这道数据从创建起就不该变。const比final更严格它要求值必须是在编译期就能确定的常量。比如const int maxDeviceCount 30;表示这个设备数量上限是个编译期常量。如果这个数值来自运行时接口返回那就不能声明为const。给个直观对比var temperature 26.5; // 类型推断为 double之后还可以重新赋值 final String deviceId dev-001; // 只能赋一次 const int maxDeviceCount 30; // 编译期常量我自己的习惯是能确定不变的优先用final这个习惯能减少很多意外的状态污染。2.2 六种基本类型逐个拆解Dart 的常用内置类型第一天只要掌握六个int、double、String、bool、List、Map。int用来表达整数设备数量、电量百分比、告警条数都用它。double用来表达小数温度、湿度传感器读数都属于这一类。num是它们的父类平时少用但如果一个变量今天可能是整数、明天可能是小数可以声明为num。String用于文本直接支持单引号或双引号。它还有一个很舒服的特性叫字符串插值用$变量名直接拼到字符串里。比如var roomName 客厅; var temperature 26.5; print($roomName当前温度是$temperature°C);输出会是“客厅当前温度是26.5°C”不需要做恶心人的拼接。bool只有两个值true和false。判断设备在线状态、是否发生告警都用它。List就是数组有序列表。比如ListString warningMessages [温度偏高, 滤网需要更换];在 Dart 里更地道的写法是var warningMessages String[温度偏高, 滤网需要更换];箭头尖括号前面的类型表示后面那个列表里装的全是字符串。Map就是键值对集合适合表达“一个传感器的多重信息”。比如MapString, dynamic sensorData { temperature: 26.5, humidity: 62.3, };这里dynamic是一个比较灵活的类型表示“运行时才知道具体类型”。它能给你方便但也容易把类型错误拖到运行时才爆发新手尽量少用。如果把 value 类型统一成double那么在取值时就不用做额外的转换。2.3 类型转换和空安全绕不开的坎类型之间有时候需要转换。比如你从接口拿到一个字符串26.5要参与数值比较就需要转成double。var text 26.5; var value double.parse(text); // value 变成 double 类型 var rounded value.round(); // 取整方法返回 int反过来把数字拼到界面里可以用toStringAsFixed控制小数位var temp 26.566; print(temp.toStringAsFixed(1)); // 输出 26.6空安全是 Dart 比较有特点的设计。声明一个变量时默认它不允许为null这其实是好事因为空引用报错真的很难排查。如果你明确允许某个变量暂时没值就要在类型后面加个问号String? lastError; // 可能为空也可能有值 print(lastError?.length ?? 0); // 如果为空就输出 0第一天可能会觉得这个?系列操作符很烦但它能避免一大类崩溃。等后面处理接口数据时你会感谢这个设计。3 手写智能家居监控模型把类型用起来3.1 定义设备信息模型先做第一步用一个类来表示一台智能设备。这里用class是因为 Dart 里没有传统意义上的“结构体”但一个类在只装数据时本质上就承担了结构体的角色。它正好能把你学过的几种基本类型集中到一个模型里。class SmartDevice { final String deviceId; final String deviceName; final bool isOnline; final int batteryLevel; final ListString tags; SmartDevice({ required this.deviceId, required this.deviceName, required this.isOnline, required this.batteryLevel, this.tags const [], }); String get statusText isOnline ? 在线 : 离线; override String toString() { return $deviceName($deviceId) 状态:$statusText 电量:$batteryLevel%; } }注意几点我用了命名参数创建对象时写deviceId: dev-001比写一堆位置参数可读得多。batteryLevel用int因为电量在 UI 上通常显示为整数百分比。tags是字符串列表默认给一个空列表这样新设备没打标签时不会报错。3.2 定义传感器读数类型接下来做一个传感器读数类。温度、湿度、PM2.5 虽然名字不一样但结构类似都有一个名字、一个数值、一个单位。用同一个类来表达最合适class SensorReading { final String name; final double value; final String unit; const SensorReading({ required this.name, required this.value, required this.unit, }); String get display $name: $value $unit; }这里value必须选double因为传感器读数几乎不可能都是整数。用const构造函数是我个人的偏好传感器读数创建完就不该改用const还能让这些对象在编译期被复用性能上有一点点好处。3.3 在 Flutter 页面展示监控数据模型类定义好之后把它们装进一个主页面的渲染流程。先写一个最简单的main.dart把设备信息和传感器读数打印出来验证逻辑void main() { var livingRoomDevice SmartDevice( deviceId: dev-001, deviceName: 客厅空调, isOnline: true, batteryLevel: 78, tags: [空调, 客厅], ); var readings SensorReading[ SensorReading(name: 温度, value: 26.5, unit: °C), SensorReading(name: 湿度, value: 62.3, unit: %), SensorReading(name: PM2.5, value: 35.0, unit: μg/m³), ]; print(设备信息: $livingRoomDevice); print(传感器数量: ${readings.length}); for (var reading in readings) { print(reading.display); } }如果你想在手机上看到界面就再加一个 Flutter 页面import package:flutter/material.dart; class MonitorDashboard extends StatelessWidget { final ListSensorReading readings; const MonitorDashboard({super.key, required this.readings}); override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text(智能家居监控模型)), body: ListView.builder( itemCount: readings.length, itemBuilder: (context, index) { final item readings[index]; return ListTile( leading: const Icon(Icons.sensors), title: Text(item.name), trailing: Text(item.display), ); }, ), ); } }把原来那个main()改成runApp的形式传进去MonitorDashboard就能看效果。void main() { runApp( MaterialApp( home: MonitorDashboard( readings: [ SensorReading(name: 温度, value: 26.5, unit: °C), SensorReading(name: 湿度, value: 62.3, unit: %), ], ), ), ); }如果你在模拟器里看到了一列传感器数据说明变量、类型、列表、类这一整条链路已经跑通了。3.4 模拟告警判断练 bool 和操作符数字类型和列表类型都练过之后再加一点判断逻辑让 bool 真正派上用场。这里我会定义一个告警阈值映射然后遍历传感器读数看是否触发告警MapString, double alertConfig { temperatureMax: 30.0, humidityMin: 40.0, }; var hasAlert false; for (var reading in readings) { if (reading.name 温度 reading.value alertConfig[temperatureMax]!) { hasAlert true; break; } if (reading.name 湿度 reading.value alertConfig[humidityMin]!) { hasAlert true; break; } } print(是否有告警: $hasAlert);这里出现了一个!操作符它的含义是“我确定这个值不为空”。因为在 Dart 里Map的取值操作返回的是可空类型编译器不能保证alertConfig[temperatureMax]一定有值。新手可以先用!快速跑通后面再学更安全的写法比如double? maxTemp alertConfig[temperatureMax]; if (maxTemp ! null reading.value maxTemp) { hasAlert true; }这种写法不用!逻辑上也更稳妥。4 第一天踩过的坑与排查实录4.1 变量声明的两种典型报错第一种是和类型推断有关。你写var x 10;之后又写x abc;编译器会直接提示A value of type String cant be assigned to a variable of type int。这是好事它帮你把错误拦截在编译阶段。很多人抱怨 Dart 不够灵活其实等到项目变大类型固定反而少很多隐性 bug。第二种是final变量再赋值的报错。比如final deviceId dev-001; deviceId dev-002; // 这里报错报错内容大概是Cant assign to the final variable。它的含义是“你创建的这个值不允许再改变”不是代码有逻辑漏洞而是你的设计自相矛盾。处理方式要看业务如果设备编号确实允许变化就把final改成var如果不允许变就要检查赋值逻辑是不是写错了。4.2 空安全引发的崩溃如果你看到控制台出现这样一段日志E/flutter (31173): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled exception先别慌这行日志本身不定义是“引擎不知道该把异常扔到哪去”的兜底提示。更重要的是它下面那几行堆栈信息。我今天就踩到过一次我从Map里取alertConfig[temperatureMax]没有判断空值就直接参与比较运行时得到null然后抛异常。排查思路比较固定先读堆栈找到是哪个文件哪一行抛出来的再看那一行有没有访问可空类型最后用安全调用?.或判断! null来解决。这个习惯从第一天就开始练习后面写接口请求时能省大量时间。4.3 关于“Flutter新建项目跑不起来”的排查顺序很多新手在flutter create之后直接按运行发现项目一直卡在启动阶段以为是自己的代码有问题。我建议按这个顺序排查先跑flutter doctor看环境缺不缺组件再看左下角运行目标是不是已经选到了可用的设备最后打开终端手动执行flutter run这里会把真正的报错打出来。网上常见的一些问题比如 Gradle 下载慢、依赖拉取失败基本都是网络和环境问题和 Dart 变量本身无关。另外如果你用 Android 模拟器第一次冷启动确实慢耐心等两三分钟属于正常现象。判断是死锁还是慢要看终端里有没有持续输出日志。白天我一开始以为项目没跑起来结果只是模拟器还没把首次构建做完。5 Day1 之后我的一点思路调整5.1 第一天到什么程度才算学到位我的判断标准不是你默写了多少条语法而是你看到一份杂乱的数据时能在心里快速给字段分层哪些是标识、哪些是数值、哪些是状态。标识用String连续型数值用double离散型数值用int二值状态用bool一组数据用List键值关系用Map。如果你能拿着“智能家居监控模型”这个例子自己动手扩展一个设备类型比如加一个“窗帘传感器”字段并且不查资料就写对类型声明那今天的知识就算真正吸收了。5.2 下一步组件通信、状态管理与下拉刷新第一天我把所有状态都直接写在了一个文件里这肯定不够。下一步我会把智能家居监控模型拆成真正的组件研究 Flutter 里的组件通信方式。从目前了解的情况看父传子用构造函数参数子传父用回调跨组件共享状态可以引入 Provider 这样的状态管理方案。后续的教程里我应该会把设备列表改成能够下拉刷新让监控模型更接近真实 App 的交互。在此之前我建议你也先别急着追求架构认认真真把变量和类型吃透。因为你后面学到的每个复杂概念比如 Provider 里保存的数据状态、组件之间传递的对象底层都是一组一组被类型约束的变量。地基不稳上层学得再花哨也容易塌。最后分享一个小技巧学变量时多问自己一句“这个数据放在现实生活里是什么”。温度计读数不会是一个整数设备地址不会是一个布尔值告警内容不会是一串可空字符串。想清楚数据本身的性质类型的答案自然就出来了。这是第一天我最有体会的一点也希望能帮你少走弯路。
返回列表