------ 从 Kotlin 思维到 Dart 设计哲学
如果你是从 Kotlin / Java 转到 Flutter,
一定会在某个时刻困惑过:
❓ 为什么 Flutter 的构造函数几乎全是
{}?❓ 为什么 Dart 要区分「位置参数」和「命名参数」?
❓ 为什么 Widget 看起来像配置文件?
这篇文章,我们从 语言设计 → API 设计 → 工程实践,把这些问题一次讲透。
一、问题的本质:Kotlin 和 Dart 的设计哲学不同
我们先看一段你很熟的 Kotlin 代码:
Kotlin
class ApiResult(
val code: Int,
val msg: String?,
val data: Any?
)
调用时你可以这样:
Kotlin
ApiResult(0, "ok", data)
ApiResult(code = 0, msg = "ok")
👉 Kotlin 的特点是:
- 参数本质都是 位置参数
- 是否写名字,由调用方决定
- 语言给你自由
而 Dart 不一样。
二、Dart 的核心差异:参数在「定义时」就被分成两类
Dart 把参数明确分成:
① 位置参数(Positional)
Dart
class A {
final int x;
final int y;
A(this.x, this.y);
}
调用:
A(1, 2);
✔ 简洁
❌ 不可读
❌ 参数一多就容易写错
② 命名参数(Named)
Dart
class B {
final int x;
final int y;
B({required this.x, required this.y});
}
调用:
B(x: 1, y: 2);
✔ 清晰
✔ 可维护
✔ 顺序无关
👉 这就是你看到 {} 的真正含义:命名参数
三、Kotlin vs Dart:本质对比(核心表)
| 维度 | Kotlin | Dart |
|---|---|---|
| 参数类型 | 只有一种 | 分为位置 / 命名 |
| 是否支持命名调用 | ✅ | ✅ |
| 是否强制命名 | ❌ | ✅(命名参数) |
| API 自由度 | 高 | 受约束 |
| 可读性 | 中 | 非常高 |
👉 Kotlin 给自由,Dart 给规范。
四、为什么 Flutter 强烈偏向命名参数?
你看 Flutter 源码就知道:
Dart
Container(
width: 100,
height: 50,
color: Colors.red,
child: Text("Hello"),
)
如果用位置参数:
Container(100, 50, Colors.red, Text("Hello"))
问题立刻出现:
- ❌ 参数意义不清
- ❌ 顺序容易写错
- ❌ 新增参数会破坏旧代码
- ❌ 可读性极差
五、什么时候用「位置参数」?什么时候用「命名参数」?
✅ 使用【位置参数】的场景
-
参数很少(1~2 个)
-
含义非常明确
-
不会再扩展
例如:
Dart
Offset(10, 20)
Duration(seconds: 2)
EdgeInsets.all(8)
✅ 必须使用【命名参数】的场景
- 参数 ≥ 2
- 参数可选
- 有默认值
- 业务对象
- UI 组件
- 网络模型
例如:
Dart
ApiResult(
code: 0,
msg: "ok",
data: user,
);
六、这就是 Flutter API 的设计原则(重点)
✅ 位置参数用于"值"
✅ 命名参数用于"语义"
换句话说:
-
值 → 顺序重要
-
语义 → 名字重要
七、结合 sealed:现代 Flutter 的最佳实践
当你写网络层时,最推荐的结构是:
Dart
sealed class ApiResult<T> {}
class Success<T> extends ApiResult<T> {
final T data;
Success(this.data);
}
class Failure<T> extends ApiResult<T> {
final String msg;
Failure(this.msg);
}
使用:
Dart
switch (result) {
case Success(:final data):
use(data);
case Failure(:final msg):
showError(msg);
}
👉 这和 Kotlin 的 sealed class + when 是完全同一思想。
八、最终总结(你可以直接背下来)
✅ 一句话总结
Kotlin 用自由换灵活,
Dart 用约束换可读。
✅ 再浓缩一句
Dart 的
{}不是语法负担,而是 API 设计工具。
================================================================
补充:Flutter API 设计反例合集(你一定踩过)
❌ 反例 1:用 Map 传递一切
Future<Map<String, dynamic>> getUser()
if (res['code'] == 0) {
print(res['data']['name']);
}
问题:
- ❌ 无类型提示
- ❌ 容易写错 key
- ❌ IDE 无法检查
- ❌ 重构成本极高
❌ 反例 2:一个类塞所有状态
class Result {
int code;
String? msg;
Object? data;
}
问题:
- data/msg 永远有一个是 null
- 语义不清晰
- 容易误用
❌ 反例 3:位置参数过多
Widget buildItem(String title, int type, bool show, Color color)
调用:
buildItem("a", 2, true, Colors.red);
你一个月后根本不知道这些参数是干嘛的。
✅ 正确做法总结
| 场景 | 推荐 |
|---|---|
| 多状态返回 | sealed |
| UI 构造 | 命名参数 |
| 网络层 | Result<T> |
| 配置对象 | const + final |
| 业务模型 | 不可变对象 |
🎯 经验总结一句话
如果一个类需要大量注释才能看懂,它的 API 设计一定有问题。