Dart - 认识Records
第一章:没有 Records 的日子,我们经历了什么?
作为一名 Dart/Flutter 开发者,你一定经历过这样的“至暗时刻”:

你编写了一个简易的工具函数,比如解析经纬度,或者从API响应中提取数据。你的逻辑清晰明了,代码也仅有寥寥数行。然而,当你写到return语句时,却陷入了困境。
“我有两个数据要返回,但我只能返回一个对象。”
在 Dart 3 引入 Records 之前,为了解决这个问题,我们通常只有三种选择。而这三种选择,坦白说,都很难受。
痛点一:为了喝杯奶,专门养头牛(定义专用类)
这是最“规范”,但也是最啰嗦的做法。
假设我们需要解析一个颜色字符串(如 #FF0033),并返回 R、G、B 三个整数值。在旧时代的 Dart 中,为了保持类型安全,我们需要专门定义一个类:
// 1. 先得定义一个类,还要写构造函数、toString、甚至重写 == 和 hashCode
class RGBColor {
final int r;
final int g;
final int b;
RGBColor(this.r, this.g, this.b);
}
// 2. 然后才能在函数里用它
RGBColor parseColor(String hex) {
// 假装这里有一段解析逻辑...
return RGBColor(255, 0, 51);
}
void main() {
final color = parseColor('#FF0033');
print('Red: ${color.r}');
}
为什么痛苦?
- 代码膨胀:你的项目里会充斥着大量像
RGBColor、UserInfoResult、LocationData这样的一次性类。它们可能只在一个函数里用了一次,但你却不得不为它们写上 5-10 行的定义代码。 - 命名困难:给这些临时类起名字是编程界最大的难题之一。
ParsedColor?ColorResult?ColorDTO?想想都头大。
痛点二:在那“裸奔”的类型安全(使用 List 或 Map)
如果你嫌定义类太麻烦,你可能会选择“偷懒”,使用 List 或者 Map 来打包数据。这就像是在代码里“裸奔”,虽然凉快,但是极其危险。
// 返回 List,但谁知道 index 0 是 R 还是 B?
Listint> parseColor(String hex) {
return [255, 0, 51];
}
void main() {
final color = parseColor('#FF0033');
// 痛点:完全没有语义,必须靠脑子记下标
final red = color[0];
// 痛点:越界风险,如果函数改了把 alpha 通道加进去,这里可能就崩了
final blue = color[2];
}
或者用 Map:
MapString, int> parseColor(String hex) {
return {'r': 255, 'g': 0, 'b': 51};
}
void main() {
final color = parseColor('#FF0033');
// 痛点:手写字符串 Key,一旦手抖写成 'R',编译不报错,运行直接崩
final red = color['r'];
// 痛点:返回类型是 int? (可空),你不得不到处写 ! 或者 ??
print(red! + 10);
}
为什么痛苦?
- 类型丢失:编译器帮不了你,你得自己保证下标和 Key 的正确性。
- 可读性差:
color[0]是什么?一个月后你自己都忘了。
痛点三:引入第三方库的尴尬(Tuple)
有些开发者会引入 package:tuple。这确实解决了一些问题,但体验依然不够原生。
import 'package:tuple/tuple.dart';
Tuple3int, int, int> parseColor(String hex) {
return const Tuple3(255, 0, 51);
}
void main() {
final color = parseColor('#FF0033');
// 痛点:item1, item2 是什么鬼?完全没有语义!
print('Red: ${color.item1}');
}
为什么痛苦?
- 语义不明:看着满屏的
item1、item2,代码的可维护性直线下降。
总结
在没有 Records 的时代,我们在**“繁琐的安全性”(写类)和“危险的便捷性”**(用 List/Map)之间反复横跳。
我们真正需要的是这样一种东西:
- 轻量:不需要专门定义一个类。
- 安全:编译器能知道里面有什么类型。
- 清晰:字段得有名字,别让我猜
item1是什么。
于是,Dart 3 带着 Records 来了。它完美地填补了这个空缺。那么它到底长什么样?又该如何使用?
请看下一章:《Records 的极简美学:像写函数参数一样定义数据》。
第二章:Records 的极简美学 —— 像写函数参数一样定义数据
在上一章的“吐槽大会”结束后,我们终于迎来了主角:Records (记录)。
Dart 团队在设计 Records 时,并没有发明一套古怪的新语法,而是做了一个非常天才的决定:直接复用函数的参数语法。
这就意味着,只要你会写函数,你就会写 Records。
1. 两种形态:哑巴盒子与标签盒子
Records 分为两种形态,分别对应我们熟悉的“位置参数”和“命名参数”。
形态一:位置记录 (Positional Records)
这对应了上一章提到的 Tuple 库,或者数组。
- 什么时候用? 当数据之间有明显的顺序关系,且大家都约定俗成时(比如经纬度、数学坐标、RGB 颜色)。
- 怎么写? 直接用括号包裹类型或值。
// 定义类型:(类型1, 类型2)
(double, double) getLocation() {
return (39.9, 116.4); // 返回值
}
void main() {
var location = getLocation();
// 访问方式:使用 $1, $2, $3...
print('Lat: ${location.$1}');
print('Lon: ${location.$2}');
}
评价:简单粗暴,但如果有 5 个字段,千万别用这个,否则下周你自己都不知道 $4 是什么。
形态二:命名记录 (Named Records) —— 推荐!
这才是 Records 的杀手锏,它几乎完美替代了简单的 DTO 类。
- 什么时候用? 绝大多数场景!特别是当你需要返回多个字段,且希望字段有清晰含义时。
- 怎么写? 用花括号
{}包裹,就像写命名参数一样。
// 定义类型:({类型 名字, 类型 名字})
({int r, int g, int b}) parseColor() {
// 返回值:带上名字,顺序无所谓
return (r: 255, b: 51, g: 0);
}
void main() {
var color = parseColor();
// 访问方式:直接用名字!类型极其安全!
print('Red: ${color.r}');
print('Green: ${color.g}');
}
评价:这简直就是**“匿名类”**的究极形态。没有 class 声明,没有构造函数,但有名字、有类型检查。
2. 终极奥义:模式匹配与解构
如果只是能通过 .r 访问属性,Records 还不够性感。Dart 3 同步带来的 模式匹配 (Pattern Matching) 才是让代码起飞的关键。
试想一下,你收到了一个包裹,过去你得先把包裹拿进屋(赋值给变量),然后再拆开(访问属性)。可现在,当你拿到包裹的那一刻,包裹就能“自动解体”,里面的东西直接飞到你的手中。
这就是 解构 (Destructuring)。
解构位置记录
var (lat, lon) = getLocation();
// 此时,lat 和 lon 已经是可用的 double 变量了
print('Lat: $lat');
解构命名记录 (最爽的语法)
// 语法糖:如果变量名和属性名一样,连“: var”都可以省!
var (:r, :g, :b) = parseColor();
print('Red: $r'); // 直接使用局部变量 r
3. Before & After:代码整容现场
让我们回到第一章那个解析颜色的痛点,看看用 Records 重写后有多清爽。
⛔️ Before (旧时代 - 定义类):
// 冗余代码:为了这点醋,包了顿饺子
class RGBColor {
final int r;
final int g;
final int b;
RGBColor(this.r, this.g, this.b);
}
RGBColor parseColor(String hex) {
// 逻辑...
return RGBColor(255, 0, 51);
}
void main() {
var c = parseColor('#FF0033');
print(c.r);
}
✅ After (Dart 3 - Records):
// 只有逻辑,没有废话
({int r, int g, int b}) parseColor(String hex) {
return (r: 255, g: 0, b: 51);
}
void main() {
// 一行解构,直接使用
final (:r, :g, :b) = parseColor('#FF0033');
print(r);
}
变化总结:
- 代码行数:从 15 行减少到 8 行。
- 样板代码:0 行。没有
class,没有构造函数。 - 可读性:语义完全保留,类型安全完全保留。
4. 总结:Records 不是万能的
读到这里,你可能想把项目里所有的类都换成 Records。请冷静!
Records 的定位非常明确:数据的“胶水”和“信封”。
-
请尽情使用 Records:
-
函数需要返回多个值时。
-
需要临时组合几个变量传给 UI 组件时。
-
处理 Map 的复合键时(比如
Map)。 -
请坚持使用 Class:
-
当你的数据需要行为(即方法/函数)时。
-
当你的数据结构非常庞大(超过 4-5 个字段)时。
-
当这是你 App 的核心领域模型(如
User,Order)时,因为 Records 不能继承,也没有名字,重构起来可能会有麻烦。
一句话总结:Dart 3 的 Records,让我们终于可以优雅地解决“为了两个数据专门写一个类”的历史难题。它是 Dart 变得更现代、更高效的重要拼图。
Swift 减少指令数量的编码 Tips
- 上一篇
- Swift 减少指令数量的编码 Tips
- 下一篇
- Dart - 认识Sealed(密封类)
-
- 文章 · 软件教程 | 1小时前 | docker · 端口映射 · 软件教程 · Docker Desktop · 容器网络 · 端口冲突 端口映射 localhost Docker Desktop 容器端口 Ports
- Docker Desktop 怎么查看容器端口:Ports 面板、浏览器访问与冲突处理
- 220浏览 收藏
-
- 文章 · 软件教程 | 3小时前 |
- Postman Console 怎么看请求实际发送内容:请求日志、变量展开与响应核对
- 391浏览 收藏
-
- 文章 · 软件教程 | 7小时前 |
- Chrome DevTools Performance 怎么录制长任务:Main 轨道、红色三角与回放核对
- 415浏览 收藏
-
- 文章 · 软件教程 | 8小时前 | future
- 从一个带并发数限制的请求深入 Dart 的 Future
- 269浏览 收藏
-
- 文章 · 软件教程 | 8小时前 | 强迫症
- Dart 3.7格式化工具“乱改代码”?强迫症必看
- 309浏览 收藏
-
- 文章 · 软件教程 | 8小时前 | Android
- Dart 速通攻略(面向 Android 工程师)
- 444浏览 收藏
-
- 文章 · 软件教程 | 8小时前 | Rust
- 假如Flutter用Rust,你也写不出更快的App
- 306浏览 收藏
-
- 文章 · 软件教程 | 8小时前 | 其他
- 最新的 Dart sdk 安装教程
- 311浏览 收藏
-
- 文章 · 软件教程 | 8小时前 | 其他
- Flutter路由3分钟学会
- 297浏览 收藏
-
- 文章 · 软件教程 | 8小时前 | scroll
- flutter_easy_refresh在3.38.3配合NestedScrollView的注意要点。
- 445浏览 收藏
-
- 文章 · 软件教程 | 8小时前 | 其他
- Kotlin vs Dart:当“优雅”变成心智负担,我选择了更简单的 Dart
- 445浏览 收藏
-
- 文章 · 软件教程 | 8小时前 | 其他
- Flutter自定义组件: 为横向列表自定义“进度条”式滚动指示器
- 452浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ljg-skills
- ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
- 5043次使用
-
- MELO音乐
- MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
- 4575次使用
-
- UniScribe
- UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
- 4529次使用
-
- 剧云
- 剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
- 4782次使用
-
- 万象有声
- 万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
- 4734次使用
-
- VS Code 怎么给 Go 项目配置测试任务:tasks.json 运行与结果验收
- 2026-07-09 501浏览
-
- Windows 11 如何开启 HEIF 图片支持
- 2026-05-31 501浏览
-
- TikTok用户画像与付费订阅变现方法
- 2026-05-27 501浏览
-
- 学信网学历翻译件申请方法
- 2026-05-27 501浏览
-
- Windows 11 24H2 更新失败0x80070005解决方法
- 2026-05-26 501浏览

