🚀运行效果展示

在这里插入图片描述

在这里插入图片描述

Flutter框架跨平台鸿蒙开发——宿舍报修APP的开发流程

📝 前言

随着移动互联网的快速发展,跨平台开发技术已经成为移动应用开发的重要趋势。Flutter作为Google推出的开源UI工具包,以其"一次编写,处处运行"的特性,受到了广大开发者的青睐。而华为鸿蒙系统(HarmonyOS)作为新一代分布式操作系统,具有强大的跨设备能力和安全性能,为开发者提供了广阔的应用场景。

宿舍报修是高校后勤管理中的重要环节,传统的报修方式往往存在效率低、流程不透明等问题。基于Flutter和鸿蒙系统开发一款宿舍报修APP,可以有效提高报修效率,增强用户体验,实现报修流程的数字化管理。

本文将详细介绍基于Flutter框架开发跨平台鸿蒙宿舍报修APP的完整流程,包括需求分析、架构设计、核心功能实现、遇到的问题及解决方案等,希望能为相关开发者提供参考。

📱 应用介绍

功能特点

宿舍报修APP是一款面向高校学生和后勤管理人员的移动应用,主要功能包括:

  • ✅ 报修申请:学生可以提交宿舍故障报修请求,包括宿舍号、房间号、故障类型、故障描述等信息
  • ✅ 报修管理:查看所有报修请求,支持按状态筛选
  • ✅ 状态更新:后勤人员可以更新报修状态(待处理、处理中、已完成、已拒绝)
  • ✅ 详情查看:查看报修请求的详细信息
  • ✅ 报修删除:删除不需要的报修请求

技术栈

技术 版本 用途
Flutter 3.6.2 UI框架
Dart 3.6.2 编程语言
sqflite 2.3.3 本地数据库
path_provider 2.1.4 文件路径处理
cupertino_icons 1.0.8 iOS风格图标

🔄 开发流程

1. 需求分析

在开发前,首先需要明确应用的核心需求和功能范围:

角色 需求
学生 提交报修请求、查看报修状态
后勤人员 查看报修列表、更新报修状态、删除报修请求

2. 架构设计

采用经典的分层架构,将应用分为模型层、数据访问层、业务逻辑层和UI层:

UI层

业务逻辑层

数据访问层

模型层

本地数据库

3. 数据库设计

设计了一个简单的报修请求表,包含以下字段:

字段名 类型 描述
id INTEGER 主键,自增
dormitory TEXT 宿舍号
roomNumber TEXT 房间号
description TEXT 故障描述
type TEXT 故障类型
status TEXT 报修状态
createdAt TEXT 创建时间
updatedAt TEXT 更新时间
imagePath TEXT 故障图片路径(可选)
notes TEXT 备注(可选)

4. 界面设计

采用Material Design设计风格,界面简洁直观,主要包括以下页面:

  • 主页面:显示所有报修请求列表
  • 添加报修页面:表单提交新的报修请求
  • 详情页面:查看报修请求的详细信息,支持状态更新和删除

🛠️ 核心功能实现

1. 模型设计

/// 报修请求状态枚举
enum RepairStatus {
  pending,    // 待处理
  inProgress, // 处理中
  completed,  // 已完成
  rejected,   // 已拒绝
}

/// 报修请求模型类
class RepairRequest {
  int? id;
  String dormitory;        // 宿舍号
  String roomNumber;       // 房间号
  String description;      // 故障描述
  String type;             // 故障类型
  RepairStatus status;     // 报修状态
  DateTime createdAt;      // 创建时间
  DateTime? updatedAt;     // 更新时间
  String? imagePath;       // 故障图片路径
  String? notes;           // 备注

  /// 构造函数
  RepairRequest({
    this.id,
    required this.dormitory,
    required this.roomNumber,
    required this.description,
    required this.type,
    this.status = RepairStatus.pending,
    DateTime? createdAt,
    this.updatedAt,
    this.imagePath,
    this.notes,
  }) : createdAt = createdAt ?? DateTime.now();

  /// 从Map转换为RepairRequest对象
  factory RepairRequest.fromMap(Map<String, dynamic> map) {
    return RepairRequest(
      id: map['id'],
      dormitory: map['dormitory'],
      roomNumber: map['roomNumber'],
      description: map['description'],
      type: map['type'],
      status: _getStatusFromString(map['status']),
      createdAt: DateTime.parse(map['createdAt']),
      updatedAt: map['updatedAt'] != null ? DateTime.parse(map['updatedAt']) : null,
      imagePath: map['imagePath'],
      notes: map['notes'],
    );
  }

  /// 从RepairRequest对象转换为Map
  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'dormitory': dormitory,
      'roomNumber': roomNumber,
      'description': description,
      'type': type,
      'status': status.toString().split('.').last,
      'createdAt': createdAt.toIso8601String(),
      'updatedAt': updatedAt?.toIso8601String(),
      'imagePath': imagePath,
      'notes': notes,
    };
  }

  // 其他辅助方法...
}

2. 数据库设计

/// 数据库帮助类,用于处理宿舍报修数据的存储和检索
class DatabaseHelper {
  static const _databaseName = 'dorm_repair.db';
  static const _databaseVersion = 1;

  static const table = 'repair_requests';

  static const columnId = 'id';
  static const columnDormitory = 'dormitory';
  static const columnRoomNumber = 'roomNumber';
  static const columnDescription = 'description';
  static const columnType = 'type';
  static const columnStatus = 'status';
  static const columnCreatedAt = 'createdAt';
  static const columnUpdatedAt = 'updatedAt';
  static const columnImagePath = 'imagePath';
  static const columnNotes = 'notes';

  DatabaseHelper._privateConstructor();
  static final DatabaseHelper instance = DatabaseHelper._privateConstructor();

  static Database? _database;

  /// 获取数据库实例
  Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDatabase();
    return _database!;
  }

  /// 初始化数据库
  Future<Database> _initDatabase() async {
    // 初始化databaseFactory
    if (databaseFactory == null) {
      // 为桌面平台初始化sqflite_ffi
      sqfliteFfiInit();
      databaseFactory = databaseFactoryFfi;
    }
    
    String path = join(await getDatabasesPath(), _databaseName);
    return await openDatabase(
      path,
      version: _databaseVersion,
      onCreate: _onCreate,
    );
  }

  /// 创建数据库表
  Future<void> _onCreate(Database db, int version) async {
    await db.execute('''
      CREATE TABLE $table (
        $columnId INTEGER PRIMARY KEY AUTOINCREMENT,
        $columnDormitory TEXT NOT NULL,
        $columnRoomNumber TEXT NOT NULL,
        $columnDescription TEXT NOT NULL,
        $columnType TEXT NOT NULL,
        $columnStatus TEXT NOT NULL,
        $columnCreatedAt TEXT NOT NULL,
        $columnUpdatedAt TEXT,
        $columnImagePath TEXT,
        $columnNotes TEXT
      )
    ''');
  }

  // 其他数据库操作方法...
}

3. 业务逻辑层

/// 报修服务类,处理宿舍报修的业务逻辑
class RepairService {
  final DatabaseHelper _dbHelper = DatabaseHelper.instance;

  /// 创建新的报修请求
  Future<int> createRepairRequest(RepairRequest repairRequest) async {
    return await _dbHelper.insert(repairRequest);
  }

  /// 获取所有报修请求
  Future<List<RepairRequest>> getAllRepairRequests() async {
    return await _dbHelper.getAllRepairRequests();
  }

  /// 更新报修状态
  Future<bool> updateRepairStatus(int id, RepairStatus status, {String? notes}) async {
    RepairRequest? repairRequest = await getRepairRequestById(id);
    if (repairRequest == null) return false;

    repairRequest.status = status;
    repairRequest.updatedAt = DateTime.now();
    if (notes != null) {
      repairRequest.notes = notes;
    }

    return await updateRepairRequest(repairRequest);
  }

  // 其他业务逻辑方法...
}

4. UI层实现

主页面
/// 宿舍报修主页面
class RepairMainScreen extends StatefulWidget {
  const RepairMainScreen({Key? key}) : super(key: key);

  
  State<RepairMainScreen> createState() => _RepairMainScreenState();
}

class _RepairMainScreenState extends State<RepairMainScreen> {
  final RepairService _repairService = RepairService();
  List<RepairRequest> _repairRequests = [];
  bool _isLoading = true;

  
  void initState() {
    super.initState();
    _loadRepairRequests();
  }

  /// 加载所有报修请求
  Future<void> _loadRepairRequests() async {
    setState(() {
      _isLoading = true;
    });
    try {
      List<RepairRequest> requests = await _repairService.getAllRepairRequests();
      setState(() {
        _repairRequests = requests;
        _isLoading = false;
      });
    } catch (e) {
      setState(() {
        _isLoading = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('加载报修请求失败: $e')),
      );
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('宿舍报修'),
        backgroundColor: Colors.blue,
        foregroundColor: Colors.white,
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _navigateToAddRepair,
        backgroundColor: Colors.blue,
        foregroundColor: Colors.white,
        child: const Icon(Icons.add),
        tooltip: '添加报修',
      ),
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : RefreshIndicator(
              onRefresh: _refreshRepairRequests,
              child: _repairRequests.isEmpty
                  ? const Center(
                      child: Text(
                        '暂无报修请求',
                        style: TextStyle(fontSize: 18, color: Colors.grey),
                      ),
                    )
                  : ListView.builder(
                      itemCount: _repairRequests.length,
                      itemBuilder: (context, index) {
                        return _buildRepairItem(_repairRequests[index]);
                      },
                    ),
            ),
    );
  }

  // 其他UI构建方法...
}
添加报修页面
/// 添加报修页面
class AddRepairScreen extends StatefulWidget {
  const AddRepairScreen({Key? key}) : super(key: key);

  
  State<AddRepairScreen> createState() => _AddRepairScreenState();
}

class _AddRepairScreenState extends State<AddRepairScreen> {
  final RepairService _repairService = RepairService();
  final _formKey = GlobalKey<FormState>();
  
  // 表单控制器
  final TextEditingController _dormitoryController = TextEditingController();
  final TextEditingController _roomNumberController = TextEditingController();
  final TextEditingController _descriptionController = TextEditingController();
  final TextEditingController _notesController = TextEditingController();
  
  String _selectedType = '';
  bool _isSubmitting = false;

  
  void initState() {
    super.initState();
    // 初始化故障类型
    List<String> types = _repairService.getRepairTypes();
    if (types.isNotEmpty) {
      _selectedType = types.first;
    }
  }

  /// 提交表单,创建新的报修请求
  Future<void> _submitForm() async {
    if (!_formKey.currentState!.validate()) {
      return;
    }

    setState(() {
      _isSubmitting = true;
    });

    try {
      RepairRequest repairRequest = RepairRequest(
        dormitory: _dormitoryController.text.trim(),
        roomNumber: _roomNumberController.text.trim(),
        description: _descriptionController.text.trim(),
        type: _selectedType,
        notes: _notesController.text.trim().isEmpty ? null : _notesController.text.trim(),
      );

      await _repairService.createRepairRequest(repairRequest);
      
      // 返回上一页,并刷新列表
      if (mounted) {
        Navigator.pop(context, true);
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('报修请求创建成功')),
        );
      }
    } catch (e) {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('创建报修请求失败: $e')),
        );
      }
    } finally {
      setState(() {
        _isSubmitting = false;
      });
    }
  }

  // 构建UI的方法...
}

📊 流程图

应用架构流程图

RepairMainScreen

RepairService

AddRepairScreen

RepairDetailScreen

DatabaseHelper

SQLite Database

RepairRequest Model

数据流程图

数据库 业务逻辑层 应用界面 用户 数据库 业务逻辑层 应用界面 用户 提交报修请求 调用createRepairRequest() 插入数据 返回插入结果 返回操作结果 显示操作结果 查看报修列表 调用getAllRepairRequests() 查询所有数据 返回查询结果 返回报修列表 显示报修列表 更新报修状态 调用updateRepairStatus() 更新数据 返回更新结果 返回操作结果 显示操作结果

🔧 遇到的问题及解决方案

问题1:数据库初始化失败

错误信息Bad state: databaseFactory not initialized

解决方案:在初始化数据库前,添加必要的初始化代码:

// 初始化databaseFactory
if (databaseFactory == null) {
  // 为桌面平台初始化sqflite_ffi
  sqfliteFfiInit();
  databaseFactory = databaseFactoryFfi;
}

问题2:UI布局溢出

错误信息A RenderFlex overflowed by X pixels on the bottom

解决方案:优化UI布局,使用更合适的组件和约束条件,如使用Expanded组件、调整字体大小、减少组件间距等。

🎯 总结

本文详细介绍了基于Flutter框架开发跨平台鸿蒙宿舍报修APP的完整流程,包括需求分析、架构设计、核心功能实现、遇到的问题及解决方案等。通过本项目的开发,我们可以得出以下结论:

  1. Flutter框架具有强大的跨平台能力,可以快速开发出高质量的移动应用
  2. 鸿蒙系统为Flutter应用提供了良好的运行环境,支持应用的正常运行和功能实现
  3. 采用分层架构可以提高代码的可维护性和可扩展性
  4. 在开发过程中,需要注意平台差异和兼容性问题,及时解决遇到的各种问题

未来,我们可以进一步完善宿舍报修APP的功能,如添加图片上传功能、推送通知功能、用户认证功能等,提高应用的实用性和用户体验。同时,我们也可以探索更多Flutter和鸿蒙系统的结合点,开发出更多优秀的跨平台应用。

🚀 未来展望

  1. 功能扩展:添加图片上传、推送通知、用户认证等功能
  2. 性能优化:优化数据库查询、减少内存占用、提高应用响应速度
  3. 多语言支持:添加中英文等多语言支持,提高应用的国际化程度
  4. 云同步:添加云同步功能,支持多设备数据同步
  5. 数据分析:添加数据分析功能,为后勤管理提供数据支持

📚 参考资料

  1. Flutter官方文档
  2. HarmonyOS官方文档
  3. sqflite官方文档
  4. Flutter跨平台开发实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐