Flutter 项目标准分层目录(repository/dto/page,对齐后端思想)

Flutter 标准分层目录(对齐 SpringBoot 思想)

思路参考后端分层:Controller(业务) / Repository(数据) / DTO(模型) / UI(页面) ,避免所有代码堆在lib下,Java团队很容易理解。

选用:Provider 做状态管理(入门友好,企业内部项目够用,不引入过重Bloc)

适用场景:车辆列表、实时告警、工单、登录、报表,对接SpringBoot HTTP / WebSocket

复制代码
lib/
├── main.dart                # 程序入口,全局初始化、路由注册
├── common/                  # 公共基础(类似common包)
│   ├── constants.dart       # 常量:后端接口地址、超时时间、枚举
│   ├── http_client.dart     # 统一http请求封装(拦截器、token、异常捕获,相当于Spring的RestTemplate封装)
│   ├── ws_client.dart       # WebSocket全局客户端(接收车辆告警推送)
│   └── utils/               # 工具类:日期格式化、hex转换、字典映射
├── dto/                     # 数据传输对象【重点!和后端SpringBoot DTO一一对应】
│   ├── bus_dto.dart         # 车辆DTO:busId, plateNo, lat, lon, status
│   ├── alarm_dto.dart       # 告警DTO:alarmId, busId, alarmType, alarmTime, content
│   └── login_dto.dart       # 登录请求/响应DTO
├── repository/              # 数据仓库层(Repository,DAO层)
│   ├── bus_repository.dart  # 车辆数据:getBusList()、getBusDetail()
│   ├── alarm_repository.dart# 告警数据:getAlarmList()、subscribeAlarm()(websocket订阅)
│   └── auth_repository.dart # 登录、刷新token
├── provider/                # 状态层(业务状态,类似后端service,给页面提供数据)
│   ├── bus_provider.dart
│   ├── alarm_provider.dart  # 实时告警状态,页面监听自动刷新UI
│   └── auth_provider.dart   # 登录状态、token保存
├── pages/                   # UI页面层(Controller视图层)
│   ├── login/
│   │   └── login_page.dart
│   ├── home/
│   │   └── home_page.dart   # 首页:车辆概览
│   ├── bus/
│   │   ├── bus_list_page.dart  # 车辆列表页
│   │   └── bus_detail_page.dart# 单车详情
│   └── alarm/
│       ├── alarm_list_page.dart # 告警列表
│       └── alarm_detail_page.dart
├── widgets/                 # 公共组件(复用UI控件,封装自定义组件)
│   ├── bus_card.dart        # 车辆卡片组件,多处页面复用
│   ├── alarm_item.dart      # 告警列表条目组件
│   └── common_empty.dart    # 空数据占位组件
└── routes/                  # 路由管理(页面跳转,统一注册)
    └── app_routes.dart

分层职责(类比SpringBoot,方便团队理解)

Flutter 分层 SpringBoot 对应模块 职责
DTO DTO/Entity 定义JSON实体,前后端字段一一对应
Repository Mapper + DAO 只做数据获取:http请求、websocket收发,不处理业务逻辑
Provider Service 调用Repository,维护页面状态、缓存数据,业务逻辑在这里
Pages Controller UI页面,监听Provider状态,渲染页面、绑定点击事件
common/http_client RestTemplate/Feign 统一请求封装,token拦截、统一错误处理

代码片段示例(分层演示)

1. pubspec.yaml 依赖

复制代码
name: bus_operation
description: 公交运维APP
version: 1.0.0+1

environment:
  sdk: '>=3.4.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
  http: ^1.2.1
  provider: ^6.1.1
  web_socket_channel: ^2.4.0
  shared_preferences: ^2.2.3 # 本地存储token
  convert: ^3.1.1 # 可选,hex/编码处理(你们车机报文会用到)

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^3.0.0

flutter:
  uses-material-design: true

执行拉包:

复制代码
flutter pub get

2. dto/alarm_dto.dart(和后端JSON对齐)

复制代码
class AlarmDto {
  final String alarmId;
  final String busId;
  final String plateNo;
  final int alarmType;
  final String content;
  final String alarmTime;

  AlarmDto({
    required this.alarmId,
    required this.busId,
    required this.plateNo,
    required this.alarmType,
    required this.content,
    required this.alarmTime,
  });

  // json -> 对象,类似Jackson反序列化
  factory AlarmDto.fromJson(Map<String, dynamic> json) {
    return AlarmDto(
      alarmId: json['alarmId'],
      busId: json['busId'],
      plateNo: json['plateNo'],
      alarmType: json['alarmType'],
      content: json['content'],
      alarmTime: json['alarmTime'],
    );
  }
}

3. repository/alarm_repository.dart(DAO层,只拿数据)

复制代码
import 'package:http/http.dart' as http;
import 'dart:convert';
import '../dto/alarm_dto.dart';
import '../common/http_client.dart';

class AlarmRepository {
  final HttpClient _http = HttpClient();

  // 查询历史告警列表
  Future<List<AlarmDto>> getAlarmList() async {
    final resp = await _http.get("/api/alarm/list");
    List<dynamic> list = resp['data'];
    return list.map((item) => AlarmDto.fromJson(item)).toList();
  }
}

4. provider/alarm_provider.dart(Service层,业务+状态)

复制代码
import 'package:flutter/foundation.dart';
import '../dto/alarm_dto.dart';
import '../repository/alarm_repository.dart';

class AlarmProvider extends ChangeNotifier {
  final AlarmRepository _repo = AlarmRepository();
  List<AlarmDto> alarmList = [];
  bool loading = false;

  // 加载告警列表
  Future<void> loadAlarmList() async {
    loading = true;
    notifyListeners(); // 通知UI刷新,类似前端发布事件
    try {
      alarmList = await _repo.getAlarmList();
    } catch (e) {
      debugPrint("加载告警失败:$e");
    } finally {
      loading = false;
      notifyListeners();
    }
  }

  // 新增实时告警(WebSocket收到推送调用)
  void addRealTimeAlarm(AlarmDto alarm) {
    alarmList.insert(0, alarm); // 新告警放最顶部
    notifyListeners();
  }
}

5. pages/alarm/alarm_list_page.dart(UI页面)

复制代码
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../provider/alarm_provider.dart';
import '../../widgets/alarm_item.dart';

class AlarmListPage extends StatefulWidget {
  const AlarmListPage({super.key});

  @override
  State<AlarmListPage> createState() => _AlarmListPageState();
}

class _AlarmListPageState extends State<AlarmListPage> {
  @override
  void initState() {
    super.initState();
    // 页面初始化加载数据
    Provider.of<AlarmProvider>(context, listen: false).loadAlarmList();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("车辆告警列表")),
      body: Consumer<AlarmProvider>(
        builder: (ctx, alarmProvider, child) {
          if (alarmProvider.loading) {
            return const Center(child: CircularProgressIndicator());
          }
          return ListView.builder(
            itemCount: alarmProvider.alarmList.length,
            itemBuilder: (ctx, index) {
              var alarm = alarmProvider.alarmList[index];
              return AlarmItem(alarm: alarm);
            },
          );
        },
      ),
    );
  }
}

6. common/http_client.dart 全局Http封装(拦截token,统一异常)

复制代码
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'constants.dart';

class HttpClient {
  final String baseUrl = Constants.baseUrl;

  Future<Map<String, dynamic>> get(String path) async {
    final uri = Uri.parse("$baseUrl$path");
    final resp = await http.get(uri, headers: {
      "Content-Type": "application/json",
      "token": "xxx", // 从auth provider读取token
    });
    if (resp.statusCode == 200) {
      return jsonDecode(resp.body);
    } else {
      throw Exception("接口请求异常");
    }
  }
}

7. widgets/alarm_item.dart 复用UI组件

复制代码
import 'package:flutter/material.dart';
import '../../dto/alarm_dto.dart';

class AlarmItem extends StatelessWidget {
  final AlarmDto alarm;
  const AlarmItem({super.key, required this.alarm});

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.symmetric(horizontal:12, vertical:6),
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text("车牌:${alarm.plateNo}",style: TextStyle(fontSize:16,fontWeight: FontWeight.bold)),
            SizedBox(height:4),
            Text("告警内容:${alarm.content}"),
            Text("告警时间:${alarm.alarmTime}"),
          ],
        ),
      ),
    );
  }
}

main.dart 入口,全局注册Provider

复制代码
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'provider/alarm_provider.dart';
import 'pages/alarm/alarm_list_page.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => AlarmProvider()),
      ],
      child: MaterialApp(
        title: '公交运维',
        theme: ThemeData(primarySwatch: Colors.blue),
        home: const AlarmListPage(),
      ),
    );
  }
}

开发规范建议(适配Java团队)

  1. DTO字段名和后端JSON完全一致,减少前后端联调坑;
  2. Repository 禁止写UI相关代码,只负责网络/websocket;
  3. Provider只管理状态、业务逻辑,不写复杂Widget;
  4. 页面组件拆到widgets目录,不要在Page里面写一大堆嵌套布局;
  5. WebSocket放在common全局单例,收到消息调用provider.addRealTimeAlarm(),推送实时告警。
相关推荐
程序员老刘2 天前
跨平台开发地图 | 2026年10月
flutter·ai编程·客户端
林瞅瞅2 天前
Flutter 开发环境从零搭建与调试记录
flutter
FungLeo2 天前
成为全栈·Flutter App 篇·Markdown 编辑器:编辑、预览与离开保护
flutter·移动开发·markdown编辑器·表单设计·成为全栈·草稿保存
FungLeo2 天前
成为全栈·Flutter App 篇·会员中心:资料、密码、通知与私有数据缓存
flutter·dart·身份认证·数据隔离·成为全栈·会员中心
穆梓兰煊3 天前
Flutter端云协同推理架构解析
flutter·github
FungLeo3 天前
成为全栈·Flutter App 篇·内置 WebView:网页历史、App 返回与外链安全
flutter·网络安全·移动开发·webview·路由管理·成为全栈
m0_738185823 天前
Flutter 鸿蒙化实战:volume_controller 适配 OpenHarmony,系统音量控制
flutter·华为·harmonyos·鸿蒙
FungLeo3 天前
成为全栈·Flutter App 篇·Flutter Markdown 阅读器:支持范围与渲染边界
flutter·markdown·dart·代码高亮·成为全栈·移动阅读