一、为什么 Flutter 网络层"容易写烂"?
大多数 Flutter 项目的网络层长这样:
Dart
final res = await api.getUser();
if (res.code == 0) {
use(res.data);
} else {
showToast(res.msg);
}
问题是:
- ❌ 成功 / 失败 / 异常 混在一起
- ❌ data / msg 到处判空
- ❌ UI 层逻辑膨胀
- ❌ 无法强制处理异常
本质问题是:
返回值没有被建模,只是一个"松散结构"
二、正确的思路:用 sealed 建模"结果"
Kotlin 世界早就这么干了
Kotlin
sealed class Result<out T> {
data class Success<T>(val data: T): Result<T>()
data class Error(val msg: String): Result<Nothing>()
}
Dart 中的等价写法(推荐)
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 message;
Failure(this.message);
}
class ExceptionResult<T> extends ApiResult<T> {
final Object error;
ExceptionResult(this.error);
}
三、网络层的正确打开方式
1️⃣ 网络请求统一返回 ApiResult<T>
Dart
Future<ApiResult<User>> getUser() async {
try {
final res = await dio.get('/user');
if (res.data['code'] == 0) {
return Success(User.fromJson(res.data['data']));
} else {
return Failure(res.data['msg']);
}
} catch (e) {
return ExceptionResult(e);
}
}
2️⃣ UI 层用 switch(极其清晰)
Dart
final result = await getUser();
switch (result) {
case Success(:final data):
showUser(data);
case Failure(:final message):
showToast(message);
case ExceptionResult(:final error):
showError(error);
}
✅ 无 null
✅ 无 magic number
✅ 没有 if-else 地狱
✅ 编译期安全
四、这种设计的核心优势
| 优点 | 说明 |
|---|---|
| 类型安全 | 不可能误用 data |
| 结构清晰 | 状态即结构 |
| 易维护 | 新增状态不影响旧代码 |
| 强约束 | UI 必须处理所有情况 |
五、总结一句话
网络层的本质不是"请求数据",而是"描述结果状态"。
sealed 正是为此而生。