当前位置: 首页 > news >正文

拒绝“面条代码”!Flutter 校园项目的“三层架构”实战

拒绝“面条代码”!Flutter 校园项目的“三层架构”实战

作为一名计算机专业的学生,你是否遇到过这种情况:
为了赶课程设计或hackathon,在 StatefulWidget 的 build 方法里直接写 Dio.get ,把网络请求、JSON解析、UI更新全都堆在一起。结果项目刚写到一半,代码就乱成了“意大利面”,改一个接口字段要在几百行代码里找半天,队友看了想打人。

最近重构校园跑腿 App 时,我痛定思痛,引入了三层架构(Repository + ViewModel + UI)的思想,配合 GetX 状态管理,终于把代码理顺了。这篇文章,我想分享这套适合大学生项目的**“清爽开发模式”**。

一、 什么是“三层架构”?

简单来说,就是把我们的代码像切蛋糕一样分成三块,各司其职:

  1. 数据层 (Repository/Model):只管“拿数据”。负责对接后端 API,解析 JSON,或者读写本地缓存。它不知道谁在调用它。
  2. 逻辑层 (ViewModel/Controller):只管“处理数据”。负责接收 UI 的指令,调用数据层,处理业务逻辑(比如判断登录状态),然后通知 UI 更新。
  3. 表现层 (UI/View):只管“展示”。它不包含复杂逻辑,只负责把 ViewModel 给的数据画在屏幕上,并在用户点击按钮时通知 ViewModel。

二、 代码实战:校园登录功能的“整容”过程

下面我用一个最常见的**“校园账号登录”**功能,展示如何用这套架构重写代码。

第一步:定义数据模型 (Model)

先定义好数据的结构,这里使用 json_serializable 插件自动生成(强烈推荐,别手写 fromJson )。

dart

// models/user.dart
import ‘package:json_annotation/json_annotation.dart’;

part ‘user.g.dart’;

@JsonSerializable()
class User {
final String studentId;
final String name;
final String token;

User({required this.studentId, required this.name, required this.token});

// 自动生成的代码
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

第二步:数据仓库 (Repository)

专门处理网络请求,这里我使用 Dio 。

dart

// repositories/auth_repository.dart
import ‘package:dio/dio.dart’;
import ‘…/models/user.dart’;

class AuthRepository {
final Dio _dio = Dio();

// 登录请求,只关心参数和返回值,不关心UI
Future login(String id, String password) async {
try {
final response = await _dio.post(
“https://api.campus.com/login”,
data: {“student_id”: id, “password”: password},
);
// 直接返回模型对象,让调用者省心
return User.fromJson(response.data[‘data’]);
} catch (e) {
// 抛出异常,让上层处理
throw Exception(“登录失败: $e”);
}
}
}

第三步:业务逻辑 (ViewModel/Controller)

这是核心,我使用 GetxController 。它像一个中间人,隔离了 UI 和数据。

dart

// controllers/login_controller.dart
import ‘package:get/get.dart’;
import ‘…/repositories/auth_repository.dart’;
import ‘…/models/user.dart’;

class LoginController extends GetxController {
// 1. 依赖注入仓库
final AuthRepository _repo = AuthRepository();

// 2. 定义可观测状态(UI 可以监听这些变量)
var isLoading = false.obs;
var errorMessage = “”.obs;

// 3. 业务方法(UI 调用这个方法)
Future handleLogin(String id, String pwd) async {
isLoading.value = true;
errorMessage.value = “”;

try { User user = await _repo.login(id, pwd); // 登录成功后的逻辑:保存 Token、跳转页面 GetStorage().write('token', user.token); Get.offAllNamed('/home'); // 路由跳转 } catch (e) { errorMessage.value = e.toString(); } finally { isLoading.value = false; }

}
}

第四步:清爽的 UI 层

UI 层现在变得非常干净,没有任何 async/await 网络代码。

dart

// pages/login_page.dart
import ‘package:flutter/material.dart’;
import ‘package:get/get.dart’;
import ‘…/controllers/login_controller.dart’;

class LoginPage extends StatelessWidget {
// 获取控制器
final LoginController _controller = Get.put(LoginController());

@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
TextField(
decoration: InputDecoration(labelText: “学号”),
// … 省略控制器绑定
),
TextField(
decoration: InputDecoration(labelText: “密码”),
obscureText: true,
),
SizedBox(height: 20),
// 监听状态变化
Obx(() => _controller.isLoading.value
? CircularProgressIndicator()
: ElevatedButton(
onPressed: () => _controller.handleLogin(“2023001”, “123456”),
child: Text(“登录”)
)
),
// 错误提示
Obx(() => Text(
_controller.errorMessage.value,
style: TextStyle(color: Colors.red),
))
],
),
),
);
}
}

三、 这样做的好处(为什么要折腾?)

  1. 代码复用:如果我在“个人中心”也需要用到登录接口,直接调用 AuthRepository 即可,不用复制粘贴代码。
  2. 便于测试:如果后端接口挂了,我想测试 UI 表现,只需要在 ViewModel 里模拟一个返回数据,完全不用动 UI 代码。
  3. 面试亮点:当面试官问你“如何处理网络请求”时,如果你能说出“我会分层处理,使用 Repository 模式隔离数据源”,这比说“我直接在 initState 里写 Dio.get ”要高级得多。

四、 给同学们的建议

对于校园项目,我们不需要追求 Google 官方那套庞大的 BLoC 模式(学习曲线太陡),GetX + 简单的 Repository 分层 是目前性价比最高的方案。

下次做课设或大作业时,试着把网络请求抽离出来,你会发现你的代码结构会变得像教科书一样漂亮。

结语:
技术的成长往往不是看你会多少炫酷的特效,而是看你写的代码是否“高内聚、低耦合”。希望这篇文章能帮你摆脱“代码洁癖”的困扰,写出让自己和队友都舒服的 Flutter 代码!

http://www.jsqmd.com/news/89099/

相关文章:

  • 2025.12 北京集训 数学专题
  • 力扣1965-丢失信息的雇员
  • 提示词工程技巧-要 “角色化”,而非 “观点化” 学习
  • Anthropic文章-打造高性能智能体 学习笔记
  • 英语_阅读_Happiness_待读
  • 英语_阅读_Arrest Report Sheet_待读
  • AI在测试中的应用:现状与未来
  • 张量并行 (Tensor Parallelism, TP) 深度解析
  • ‌面试技巧:拿下测试岗位的秘诀
  • Linux修改网络配置和IP地址
  • 三步走截取长图
  • JavaWeb企业级开发---JavaScript
  • 网络安全异想天开(不定期更新)
  • 串口通讯的android 封装开箱即用!提供源代码!
  • 测试左移:构建软件质量的早期防线
  • 力扣1264-页面推荐
  • 抖音uid匹配电话
  • Springboot连锁火锅店餐饮管理系统h2dg0(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • [SWPUCTF 2018]SimplePHP
  • Flutter 多端落地实战:Web 与桌面应用的性能优化、SEO 与用户体验全攻略
  • 系统启动和DNS
  • Springboot连锁家政保洁管理系统03zmn(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • Flutter 测试全栈指南:从单元测试到黄金路径验证的工程化实践
  • 本凡码农引领杭州小程序开发解决方案赋能企业创新与发展
  • FlutterOpenHarmony商城App标签选择组件开发
  • Springboot连锁药店进销存业务系统98i85(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 前端与数据库交互
  • 《CAPL脚本实现CANOE工具 Bus-Off自动恢复(含重试机制)》
  • OP-TEE HelloWorld 文件保护实战:把一份 `helloworld.txt` 交给 Secure World 保管(EKB → PTA → CA)
  • download https://apkpure.com/cn/