news 2026/4/26 16:07:10

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
拒绝“面条代码”!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 代码!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/24 0:46:54

JavaWeb企业级开发---JavaScript

记录在听黑马课的时候的笔记以及课堂上练习的代码&#xff0c;文章图源于我在听课的时候所截的屏&#xff0c;所以有些不清晰&#xff0c;请见谅。下面是课程链接&#xff0c;可点击自行跳转。 【黑马程序员JavaWeb开发教程&#xff0c;实现javaweb企业开发全流程&#xff08;…

作者头像 李华
网站建设 2026/4/24 19:22:17

微信小程序_WXML

图片&#xff1a;等比例填充&#xff08;头像&#xff09;&#xff1a;mode“aspectFill”<image src"{{userInfo ? userInfo.avatarUrl :/images/1.png}}" mode"aspectFill"></image>

作者头像 李华
网站建设 2026/4/25 17:52:57

Springboot连锁家政保洁管理系统03zmn(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。

系统程序文件列表项目功能&#xff1a;分店管理员,用户,保洁员,通知信息,独立服务,团队服务,独立服务信息,团队服务信息,独立服务订单,团队服务订单,团队派单,完成订单,独立服务取消,团队服务取消开题报告内容基于SpringBoot的连锁家政保洁管理系统开题报告一、研究背景与意义研…

作者头像 李华
网站建设 2026/4/23 13:09:10

Redis原理篇-Dict的rehash

** 不管是扩容还是收缩&#xff0c;必定会创建新的哈希表&#xff0c;导致哈希表的size和sizemask变化&#xff0c;而key的查询与sizemask有关。因此必须对哈希表中的每一个key重新计算索引&#xff0c;插入新的哈希表&#xff0c;这个过程称为rehash。过程是这样的&#xff1a…

作者头像 李华
网站建设 2026/4/25 11:21:59

计算机考研408【计算机网络】核心知识点总结

计算机网络作为考研408的重要组成部分&#xff0c;占总分约25分&#xff0c;由选择题和综合应用题构成。掌握计算机网络的基本概念、原理和方法是备考的关键 &#xff0c;尤其要理解OSI参考模型与TCP/IP模型的对应关系&#xff0c;以及各层协议的工作原理。本文将系统梳理计算机…

作者头像 李华
网站建设 2026/4/25 0:45:55

vue基于Spring Boot的公务员考试交流平台的应用和研究_729q3563

目录具体实现截图项目介绍论文大纲核心代码部分展示项目运行指导结论源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作具体实现截图 本系统&#xff08;程序源码数据库调试部署讲解&#xff09;同时还支持java、ThinkPHP、Node.js、Spring B…

作者头像 李华