Dart 3.13 的到底改了什么?为什么很重要?有什么坑?
感觉 Dart 3.13 对外可能只是多了一个新语法但是 Dart 的核心设计者甚至针对 Primary Constructors 写了一篇超长文章来聊这事因为如果只看 Demo 上的例子这个改动好像也没什么比如加了 Primary Constructors 之后是这样 class Point(final int x, final int y);然后以前我们是这样class Point { final int x; final int y; ​ Point(this.x, this.y); }看起来就是少了几行代码好像 Dart 又从 Kotlin、Scala 借了一点语法糖设计的感觉但是实际对于 Dart 设计者来说这是 Dart 语法的一次重构过程。这次变动其实是 Dart 团队借 Primary Constructors 实现的过程重新整理了一遍 Dart 的「类、字段、参数、构造函数」之间的关系也是 Dart 一次重要的重构。所以 Dart 3.13 最终落地的东西可比一行class Point(...)大很多它包括declaring parametersprimary constructor body新的字段初始化作用域new()、factory()这套新的普通构造函数声明语法实际上从 Dart 3.13 开始构造函数这块的写法会导致整个 Dart 和 Flutter 出现一次比较明显的风格迁移。那为什么这么多年一直有人在说 Dart 不提供类似 Kotlin 的 Data Class然后现在反而做成了 Primary Constructor 确实 Dart language repository 这么多年来最热门的 feature request 一直是 Data Class类似 Kotlin 的data class User( val name: String, val age: Int )既可以自动声明字段和构造函数又会提供equals()、hashCode()、toString()、copy()等一系列 value semantics。但 Dart 团队整理了整个历史讨论之后发现其实很多 Dart 用户最想解决的其实是前半部分定义一个装数据的 class 太麻烦了比如传统 Dart 数据对象类似class Point { final int x; final int y; ​ Point(int x, int y) : x x, y y; }这个一个x的概念在一个简单的 class 里可能出现四次所以后来 Dart 加了 initializing formalclass Point { final int x; final int y; ​ Point(this.x, this.y); }这时候已经简化了一些但字段还是要声明一次构造参数再出现一次所以 Dart 团队随即把问题拆成两件事一个是怎样更简洁地声明「constructor parameter instance field」一个是怎样获得 value equality、hashCode、copy 等 Data Class 能力所以 Dart 3.13 的 Primary Constructors 目标就是先解决第一个问题在进行一次代码表达方式的压缩。实际上 Primary Constructor 的核心其实是 Declaring Parameter Primary Constructor 最简单的理解就是把构造函数挪到 class headerclass Point(int x, int y);不过这里其实有个比较容易忽略的问题这段代码里的int x和int y只是 constructor parameter它们不会自动变成字段真正关键的是 Dart 3.13 引入的 declaring parameterclass Point( final int x, final int y, );这里的final同时表达两层信息constructor parameter和final instance field也就是等价于class Point { final int x; final int y; ​ Point(this.x, this.y); }如果字段需要可变那就用varclass Point( var int x, var int y, );这时候实际上等价于class Point { int x; int y; Point(this.x, this.y); }所以其实 Dart 团队实际上把 primary constructor parameter 分成了几种角色class Example( final int a, var int b, int c, );其中final int a parameter final fieldvar int b parameter mutable fieldint c 普通 parameter这点非常重要因为 Primary Constructor 没有规定「header 里的所有参数都是字段」参数还是可以只参与初始化计算比如class Rectangle( final double width, final double height, double scale, ) { final double area width * height * scale; }这里width和height会成为 instance fieldscale只存在于构造阶段。所以实际上可能理解上会比过去稍微复杂一点点官方 specification 甚至增加了一套新的 scope叫primary initializer scope让普通 primary constructor parameter 可以被字段 initializer 使用比如以前class DeltaPoint { final int x; final int y; DeltaPoint(this.x, int delta) : y x delta; }现在可以写class DeltaPoint( final int x, int delta, ) { final int y x delta; }这里delta没有成为字段但字段 initializer 可以直接读取。也就是很多以前只能放在 constructor initializer list 里的简单计算现在甚至可以直接回到字段声明旁边。那为什么 Dart 不直接照抄 Kotlin 就好呢这套东西明显就有 Kotlin 的影子啊比如 Kotlinclass Point( val x: Int, val y: Int )Dartclass Point( final int x, final int y, );实际上 Dart 团队在这个问题上确实纠结和挺久就是到底应该从「字段」推导 constructor还是从「constructor parameter」推导字段。比如 Swift 更倾向于另外一个方向「先声明 fields」 然后「compiler 生成 memberwise initializer」 。然后 Dart 最终选择了 「先声明 constructor API」 然后「部分 parameter 顺便生成 fields」 原因其实也很直接因为 Constructor 本身经常就是一个公开 API开发者可能需要更精确控制named / positional required / optional default value parameter order constructor name const这些东西都天然属于 constructor signature 如果从 fields 自动推导 constructor就会有更多问题需要处理比如字段顺序怎么算哪些字段应该 exposed哪些是 named parameter哪些 required默认值放在哪里所以 Dart 最后认为让开发者明确写 constructor signature再用var/final标记哪些参数同时产生字段组合起来更自然。这也是为什么 Dart 的 Primary Constructor 看起来比某些语言稍微“重”一点的原因它没有追求class Point(int x, int y)自动猜测x和y是字段你必须明确写class Point(final int x, final int y);或者class Point(var int x, var int y);字段的存在和可变性需要都直接暴露在源码里。当然实际上这里还有一个必须提的就是this {}Dart 团队说的是syntactic cliff语法悬崖。比如最开始有一个很简单的 classclass FormatterOptions({ final int indent 0, final int pageWidth 80, });但是后来需求慢慢增加变成了class FormatterOptions({ final int indent 0, final int pageWidth 80, final bool followLinks false, final bool setExitIfChanged false, });然后有一天你突然需要在 constructor 里打印一行 log如果 Primary Constructor 只支持「没有 body 的简单 class」你可能就需要把整个 class 改回class FormatterOptions { final int indent; final int pageWidth; final bool followLinks; final bool setExitIfChanged; FormatterOptions({ this.indent 0, this.pageWidth 80, this.followLinks false, this.setExitIfChanged false, }) { log.write(Created options.); } }语义只增加了一行log.write(...)但是实际上 Git diff 会突然变成十几二十行这就是 syntactic cliff一个很小的功能变化让代码突然从一种简洁语法掉进另一套非常冗长的语法。所以 Dart 给 Primary Constructor 又设计了一块 constructor bodyclass FormatterOptions({ final int indent 0, final int pageWidth 80, }) { this { log.write(Created options.); } }这里的this就是 Primary Constructor 的 body如果需要 initializer list同样可以写class Point( final int x, final int y, ) { this : assert(x 0); }甚至你可以写成class B( int x, int y, {required final String s2}, ) extends A { final String s1; this : s1 y.toString(), super.someName(x 1); }所以 Primary Constructor 没有被限制成「只能写 DTO」你同样可以处理parameters fields default values assert initializer list super constructor call constructor body这也是 Dart 团队为什么花了这么久设计它如果只是做class Point(final int x, final int y);其实没有那么复杂。不过 Primary Constructor 确实带来了一个问题有了 Primary Constructor 之后这个 constructor 在语义上真的是 primary官方 specification 有一条很重要的规则一个 class 如果拥有 primary constructor那么其他 generative constructor 必须最终 redirect 到这个 primary constructor。原因和前面提到的字段 initializer scope 有直接关系比如class C( int value, ) { final int result value * 2; }这里result value * 2成立的前提是每次创建C时一定都会执行这个 primary constructor如果同时允许C.other()完全绕过 primary constructor那么value从哪里来就没办法定义了。所以 Dart 强制所有 generative constructor 都经过 primary constructor这也是 Primary Constructor 和「普通 constructor 的另一种写法」之间真正存在的结构差异。如果一个 class 本来就有很多地位平等、初始化路径完全不同的 constructor那么继续使用传统 in-body constructor 反而更清楚。Dart 团队也明确说并没有打算让 Primary Constructors 取代所有 constructor对于 constructor 很多、class header 已经很复杂或者核心 constructor 是 private 的情况传统写法才更合理。当然其实 Dart 3.13 其实也改了普通 Constructor 的写法从 Dart 3.13 开始普通 constructor 其实可以不再重复 class name比如以前是class AnimatedFractionallySizedBox { AnimatedFractionallySizedBox(); AnimatedFractionallySizedBox.create(); factory AnimatedFractionallySizedBox.fromJson() { ... } }然后现在可以写class AnimatedFractionallySizedBox { new(); new create(); factory fromJson() { ... } }从对应关系可以看出来Old Dart 3.13 ClassName() new() ClassName.name() new name() const ClassName() const new() const ClassName.name() const new name() factory ClassName() factory() factory ClassName.name() factory name()这项变化其实反而很 Dart因为传统 C / Java / C# 风格 constructor 最大的问题就是class SomeExtremelyLongClassName { SomeExtremelyLongClassName(); }class name 在上下文里已经非常明确再写一遍基本没有提供额外信息而且这个问题在 Dart 将来准备做的 static extension members 里会更加麻烦比如class SomeClass {} typedef OtherName SomeClass; extension on OtherName { // constructor? }如果 extension 将来允许增加 constructor那么这里到底应该写OtherName()还是SomeClass()这就会出现 typedef identity、封装和名称解析问题所以 Dart 团队最后选择直接把这个历史包袱拆掉new()就是 generative constructor declarationfactory()就是 factory constructor declaration。这样 constructor 就不再需要知道 class 的文本名字当然比较搞笑的是它会产生const new();这种const new第一眼看确实有点违和哈。然后整套语法如果放到 Flutter 里以前典型 Widget 会是class UserCard extends StatelessWidget { const UserCard({ super.key, required this.name, required this.avatarUrl, this.showBadge false, }); final String name; final String avatarUrl; final bool showBadge; override Widget build(BuildContext context) { ... } }如果改成 Primary Constructor 风格可以写成类似class const UserCard({ super.key, required final String name, required final String avatarUrl, final bool showBadge false, }) extends StatelessWidget { override Widget build(BuildContext context) { ... } }这里super.key继续是 super parameter而三个finalparameter 直接产生 fields官方 specification 明确支持 super parametersclass A(final int a); class B(super.a) extends A;更有意思的是阅读的时候 class 的「输入 API 保存的状态」集中到了顶部class UserCard({ super.key, required final String name, required final String avatarUrl, final bool showBadge false, }) extends StatelessWidget {看到这一段基本就知道这个 Widget 保存了什么状态所以实际上这个语法糖的价值也不只是减少字符数而已它还能让代码更直接地暴露意图。同时 Dart 官方也明确这将是未来 Dart 的主流风格Dart 3.13 甚至开始通过 lint 推动这种新风格也就是从官方配套来看 Dart 官方显然没有把 Primary Constructors 当成一个「想用就用的边缘语法」状态这里 Dart 3.13 一次加入了六个相关 lintempty_container_bodies initialize_in_field_declaration unnecessary_const_in_enum_constructor unnecessary_primary_constructor_body unnecessary_type_name_in_constructor use_declaring_parameters这些 lint 都围绕一个方向让代码向更短的 rimary constructor 表达方式迁移IDE 还直接增加了Convert to primary constructor Convert to in-body constructor Convert to declaring parameter Move initialization to the field declaration特别值得注意的是unnecessary_type_name_in_constructor这个 lint 会认为class C { C(); C.name(); }应该改成class C { new(); new name(); }官方文档甚至直接把旧写法标成 BAD、新写法标成 GOOD也就是所以从长期趋势看new()constructor declaration 大概率会慢慢成为 Dart 官方推荐风格所以你不要觉得这个只是加了一个新语法实际上这很可能是一次断代的开始。另外还有两个升级到 Dart 3.13 时值得注意的小坑因为 Primary Constructors 虽然官方强调没有增加新的运行时 semantics但因为语法空间发生了变化还是产生了少量 source compatibility 问题。这里第一个是final parameter过去 Dart 可能有人会写void foo(final int value) { ... }这里会把final当作「不允许 parameter 被重新赋值」的语法而 Dart 3.13 以后final和var在 parameter 上会被赋予了 declaring parameter 的特殊含义所以普通 function parameter 上使用它们大概率会成为 compile-time error。这个坑是真的坑如果团队希望继续限制 parameter assignment官方建议使用parameter_assignmentslint。另一个比较冷门的情况是factory() {} 以前如果你恰好定义了一个没有显式 return type、名字就叫factory的 methodDart 3.13 的 parser 可能会把它理解成 factory constructor。当然普通 Flutter 项目里这两种情况一般都不算高频但是还是需要注意下。所以 Dart 3.13 实际上是另一次 null safety 变动的开始估计再有两个版本 Dart 可能就会有全新的断档了所以还是有必要关注下的。

相关新闻

从TUI到原生UI:复杂工具开发的技术选型与架构演进

从TUI到原生UI:复杂工具开发的技术选型与架构演进

在开发工具和系统管理领域,命令行工具(CLI)和文本用户界面(TUI)长期以来是开发者和运维人员的首选。它们轻量、高效,能够通过脚本实现自动化,并且在远程服务器上通过 SSH 即可操作。然而&#x…

2026/8/25 6:16:50 阅读更多 →
国内先进封装公司哪家好的核心要点与实践经验

国内先进封装公司哪家好的核心要点与实践经验

在2.5D 封装工艺企业采购真空甲酸炉领域,技术不断创新,推动了产业的快速发展。 对于国内先进封装公司哪家好这一技术热点,业内专家提出了多种解决方案。 从市场表现来看,无锡 2.5D 集成电路封装厂商相关产品的需求持续增长。 2.5D…

2026/8/25 6:16:50 阅读更多 →
AI Agent工具误调用优化:从根因分析到工程实践

AI Agent工具误调用优化:从根因分析到工程实践

这次我们来看一个面试中经常被问到的问题:Agent工具误调用怎么优化?这不仅是面试官喜欢考察的点,也是AI Agent在实际落地时最头疼的稳定性问题之一。一个Agent系统,如果频繁调用错误的工具、返回无关结果,不仅浪费算力…

2026/8/25 6:16:50 阅读更多 →

最新新闻

从数组到矩阵:掌握二维数据操作的核心思维与实战技巧

从数组到矩阵:掌握二维数据操作的核心思维与实战技巧

你是不是经常在刷算法题时,看到“矩阵”、“二维数组”就头疼?或者在实际项目中,面对一个游戏地图、一个Excel表格数据,明明感觉逻辑很简单,却总在索引越界、行列转换上栽跟头?很多人把“数组”和“矩阵”混…

2026/8/25 7:05:09 阅读更多 →
Docker部署宝塔面板:实现环境隔离与纯净运维的完整指南

Docker部署宝塔面板:实现环境隔离与纯净运维的完整指南

如果你是一名开发者,正在寻找一种既能享受宝塔面板的便捷可视化操作,又能保持服务器环境纯净、易于迁移和版本控制的方法,那么这篇文章就是为你准备的。 传统的服务器运维,尤其是Web服务部署,常常陷入一个两难境地&am…

2026/8/25 7:05:08 阅读更多 →
数据库迁移工具从单机作业走向云端协同

数据库迁移工具从单机作业走向云端协同

文章目录一、单机版迁移工具为什么容易遇到协同瓶颈二、KDMS 的“云端服务”分别解决什么问题1. 云:把评估从一份报告变成项目底账2. 端:在现场完成数据采集,而不是搬走生产数据3. 服务:把 DBA 经验变成在线可复用的支持三、三者怎…

2026/8/25 7:05:08 阅读更多 →
ISV选型指南——零代码/低代码/渐进式IDE,到底该选哪个?

ISV选型指南——零代码/低代码/渐进式IDE,到底该选哪个?

摘要:IDC 数据显示,2025 上半年国内低代码/零代码市场规模已达 21.1 亿元,同比涨 21.4%。Gartner 预测 2026 年超 70% 的企业新业务应用将用低代码/无代码技术搭建。但信通院调研同时指出,企业选型踩坑率高达 67%,其中…

2026/8/25 7:05:08 阅读更多 →
别让 AI 靠“记忆”回答:给团队资料加上查证能力的 RAG 入门

别让 AI 靠“记忆”回答:给团队资料加上查证能力的 RAG 入门

原文链接 让 AI 先翻资料再开口:RAG 知识库问答实战 很多人第一次接触 RAG(Retrieval-Augmented Generation,检索增强生成)时,会把它理解成: “把公司文档喂给 AI,让它记住。” 这不够准确。 …

2026/8/25 7:05:08 阅读更多 →
VR线上展厅:定义、趋势与标杆实践

VR线上展厅:定义、趋势与标杆实践

VR线上展厅是融合三维建模、VR虚拟现实、全景漫游、实时交互等技术的数字化展示空间。与传统图片和视频不同,VR线上展厅让用户通过一个链接即可“走进去”——在三维空间中自由行走、切换视角、与展品互动,无需下载任何软件,在电脑、手机、平…

2026/8/25 7:04:08 阅读更多 →

日新闻

洛谷 P7912:[CSP-J 2021 T4] 小熊的果篮 ← 双向链表

洛谷 P7912:[CSP-J 2021 T4] 小熊的果篮 ← 双向链表

【题目来源】 https://www.luogu.com.cn/problem/P7912 【题目描述】 小熊的水果店里摆放着一排 n 个水果。每个水果只可能是苹果或桔子,从左到右依次用正整数 1,2,…,n 编号。连续排在一起的同一种水果称为一个“块”。小熊要把这一排水果挑到若干个果篮里&#x…

2026/8/25 0:00:34 阅读更多 →
Transformers.js 网页端图像抠图实战:零后端 3 行代码返回透明 PNG

Transformers.js 网页端图像抠图实战:零后端 3 行代码返回透明 PNG

Transformers.js 网页端图像抠图实战:零后端 3 行代码返回透明 PNG 【免费下载链接】transformers.js State-of-the-art Machine Learning for the web. Run 🤗 Transformers directly in your browser, with no need for a server! 项目地址: https:/…

2026/8/25 0:00:34 阅读更多 →
数学建模竞赛论文写作指南:从模型构建到学术表达的核心技能

数学建模竞赛论文写作指南:从模型构建到学术表达的核心技能

1. 项目概述:从“会做”到“会写”的竞赛核心跃迁“全国大学生数学建模竞赛”,这个名字对理工科学生来说,分量极重。每年,无数团队在三天三夜的时间里,为一个开放性问题绞尽脑汁,从建立模型、求解算法到编程…

2026/8/25 0:00:34 阅读更多 →

周新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/25 3:38:12 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/25 3:38:18 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/25 3:38:23 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/24 20:22:44 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/23 12:10:44 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/24 11:20:22 阅读更多 →