Flutter + 开源鸿蒙实战|城市智慧停车管理系统 Day6 全局组件封装+意见反馈+系统设置+代码重构+bug修复+细节调优

Flutter + 开源鸿蒙实战|城市智慧停车管理系统 Day6 全局组件封装+意见反馈+系统设置+代码重构+bug修复+细节调优

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

html 复制代码
<!-- Schema.org 结构化数据 -->
<script type="application/ld+json">
{
  "@context":"https://schema.org",
  "type":"BlogPosting",
  "headline":"Flutter+开源鸿蒙实战 城市智慧停车管理系统Day6 全局组件封装+意见反馈+系统设置+代码重构+bug修复+细节调优",
  "author":{"type":"Person","name":"鸿蒙跨端开发者"},
  "publisher":{"type":"Organization","name":"开源鸿蒙技术社区"},
  "datePublished":"2026-05-09",
  "description":"商业级非校园实战项目Day6,详细讲解全局通用组件深度封装、意见反馈页面完整开发、系统设置全功能实现、代码整体重构优化、全量遗留bug修复、多端适配细节调优,让项目代码更规范、功能更稳定、体验更流畅,适配鸿蒙手机平板,毕设可直接真机演示、源码提交",
  "keywords":"Flutter,开源鸿蒙,OpenHarmony,智慧停车,全局组件封装,意见反馈,系统设置,代码重构,bug修复,细节调优"
}
</script>

一、前言

哈喽小伙伴们,我们继续深耕城市智慧停车管理系统,全程聚焦城市商圈、小区、市政停车场真实商用场景,无任何校园同质化内容,致力于打造一套"功能完整、代码规范、体验流畅、部署可用"的商业级跨平台项目,无论是毕设答辩、作品集展示,还是面试项目提交,都能脱颖而出。

首先,我们用最详细的方式复盘前5天的完整开发进度,帮大家精准衔接今日开发内容,避免遗漏关键知识点:

  • Day1:完成项目从零初始化,搭建企业级分层架构(core、controller、models、pages、utils、widgets),集成全套必备第三方库(高德地图、定位、权限、本地缓存、网络请求、屏幕适配、GetX状态管理),搭建底部导航栏和五大主页面空白骨架,封装通用工具类,完成项目底层基座搭建,为后续所有开发奠定基础。
  • Day2:重点落地地图与定位核心能力,配置高德地图Key并完成初始化,实现实时定位功能(适配鸿蒙动态权限),在地图上标注附近车场点位并实现点击弹窗,搭建首页搜索栏实现车场名称模糊搜索,封装车场实体模型和通用车场卡片组件,配置全局路由表,完成"定位→查询→筛选"的核心链路。
  • Day3:聚焦停车核心业务闭环,开发车场详情页完整布局(包含车场信息、收费规则、车位网格),定义车位三种状态(空闲/占用/已预约)并封装车位组件,实现车位预约逻辑(锁定车位、禁止重复预约),编写阶梯式停车计时计费算法(区分普通用户/会员折扣,毕设核心亮点),集成高德路线导航功能,实现常用车场本地缓存持久化,完成"详情→预约→计时→计费→导航→缓存"的业务升级。
  • Day4:落地订单与支付业务,定义4种订单状态枚举(停车中/待缴费/已完成/超时未缴),完善订单实体模型,实现停车结束自动生成唯一订单号、在线缴费模拟弹窗(缴费后更新订单状态),搭建我的订单页面(支持多标签筛选、下拉刷新),封装全局订单卡片组件(按状态区分颜色),开发会员中心页面,完善个人中心功能并实现退出登录+缓存清理,完成"订单生成→状态管理→在线缴费→订单查询→会员权益"的商业闭环。
  • Day5:重点优化用户体验、合规性与部署能力,开发停车超时提醒弹窗(超时额外计费提示),封装全局主题控制器实现深浅色模式一键切换,搭建完整登录页面(账号密码登录+记住登录本地缓存),开发首次启动隐私协议弹窗(符合鸿蒙上架规范),完成全局UI精细化美化(统一按钮、输入框、卡片样式),配置鸿蒙打包签名生成hap安装包,让项目达到商用级可交付标准。

来到 Day6 ,这是项目开发的"收尾优化阶段"------我们不再新增全新业务逻辑,而是将重点放在「代码规范、功能稳定、体验流畅、细节完善」上。今天的开发内容虽然不涉及新业务,但却是毕设拿高分、项目能落地的关键:通过全局组件深度封装,让代码更简洁、可复用性更强;开发意见反馈和系统设置页面,完善产品功能闭环;对前期代码进行整体重构,修复所有遗留bug,优化多端适配细节,让项目从"能用"真正升级为"好用、稳定、规范"。

今日Day6 核心开发任务(逐项详细落地,重点讲清逻辑)

  1. 深度封装全局通用组件(按钮、输入框、弹窗、加载组件、空页面),统一样式、提升复用性,减少代码冗余;
  2. 开发意见反馈页面(输入反馈内容、选择反馈类型、提交反馈、本地缓存反馈记录),完善产品交互闭环;
  3. 开发系统设置页面(清除缓存、关于我们、版本信息、联系方式、隐私政策跳转),满足商用产品规范;
  4. 对前期代码进行整体重构(优化目录结构、简化冗余代码、统一命名规范、分离业务逻辑与UI);
  5. 排查并修复前5天遗留的所有bug(布局错乱、交互异常、数据同步问题、权限问题等);
  6. 多端适配细节调优(平板布局适配、字体自适应、按钮间距优化、弹窗位置适配);
  7. 新增全局异常捕获,避免APP闪退,提升系统稳定性;
  8. 整理Day6开发重点、优化思路与新手避坑方案(详细讲解)。

二、版块1:全局通用组件深度封装(重点,毕设加分项)

文字讲解(详细版)

前5天开发中,我们写了很多重复的UI组件(比如按钮、输入框、弹窗),不仅代码冗余,而且后期修改样式时,需要逐个页面修改,效率极低。今天我们将这些高频使用的组件进行深度封装,统一样式、统一交互,后续所有页面直接调用封装好的组件即可,既减少代码量,又保证全APP视觉风格统一,这也是企业级开发的标准做法,毕设中体现这一点,能让评委看到你的工程化思维。

本次封装的组件均放在widgets目录下,按功能分类,每个组件单独创建文件,方便后期维护和复用,具体封装6个核心通用组件,每个组件都详细讲解设计思路和实现逻辑:

1.1 通用按钮组件(全局复用,支持多种样式)

文字讲解

通用按钮分为三种样式:主按钮(用于核心操作,如登录、缴费)、次要按钮(用于次要操作,如取消、返回)、文本按钮(用于辅助操作,如忘记密码、查看详情),统一配置圆角、字体大小、间距、水波纹效果,支持自定义颜色、文字、点击事件,适配鸿蒙多端屏幕,避免每个页面重复写按钮样式。

dart 复制代码
// widgets/common_button.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../core/style/app_style.dart';

// 按钮类型枚举
enum ButtonType { primary, secondary, text }

class CommonButton extends StatelessWidget {
  // 按钮文字
  final String text;
  // 点击事件
  final VoidCallback onTap;
  // 按钮类型
  final ButtonType type;
  // 是否禁用
  final bool isDisabled;

  // 构造函数,必填参数+可选参数,默认值合理配置
  const CommonButton({
    super.key,
    required this.text,
    required this.onTap,
    this.type = ButtonType.primary,
    this.isDisabled = false,
  });

  @override
  Widget build(BuildContext context) {
    // 根据按钮类型配置样式
    Color bgColor = _getBgColor();
    Color textColor = _getTextColor();
    double height = 48.h; // 统一按钮高度,适配多端

    return Opacity(
      // 禁用状态透明度降低
      opacity: isDisabled ? 0.6 : 1.0,
      child: ElevatedButton(
        onPressed: isDisabled ? null : onTap,
        style: ElevatedButton.styleFrom(
          backgroundColor: bgColor,
          minimumSize: Size(double.infinity, height), // 宽度自适应父容器
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(AppStyle.radius), // 全局统一圆角
            side: type == ButtonType.secondary 
                ? BorderSide(color: AppStyle.borderColor) 
                : BorderSide.none,
          ),
          splashFactory: InkRipple.splashFactory, // 统一水波纹效果
          elevation: type == ButtonType.primary ? AppStyle.shadowElevation : 0,
        ),
        child: Text(
          text,
          style: TextStyle(
            fontSize: 15.sp,
            color: textColor,
            fontWeight: type == ButtonType.primary ? FontWeight.w500 : FontWeight.normal,
          ),
        ),
      ),
    );
  }

  // 私有方法:根据按钮类型获取背景色
  Color _getBgColor() {
    switch (type) {
      case ButtonType.primary:
        return AppStyle.primaryColor; // 全局主色
      case ButtonType.secondary:
        return Colors.white; // 次要按钮白色背景
      case ButtonType.text:
        return Colors.transparent; // 文本按钮透明背景
    }
  }

  // 私有方法:根据按钮类型获取文字色
  Color _getTextColor() {
    switch (type) {
      case ButtonType.primary:
        return Colors.white;
      case ButtonType.secondary:
        return AppStyle.primaryColor;
      case ButtonType.text:
        return AppStyle.primaryColor;
    }
  }
}
文字讲解(使用方法)

封装完成后,后续所有页面直接调用CommonButton,无需重复写样式,示例如下:

dart 复制代码
// 主按钮(登录、缴费)
CommonButton(
  text: "立即登录",
  onTap: () => doLogin(),
  type: ButtonType.primary,
),

// 次要按钮(取消)
CommonButton(
  text: "取消",
  onTap: () => Get.back(),
  type: ButtonType.secondary,
),

// 文本按钮(忘记密码)
CommonButton(
  text: "忘记密码?",
  onTap: () => Get.toNamed(RoutePath.forgetPwd),
  type: ButtonType.text,
),

1.2 通用输入框组件(全局复用,支持密码隐藏、占位提示)

文字讲解

输入框是项目中高频使用的组件(登录、搜索、反馈),统一封装输入框样式(圆角、边框、间距、提示文字),支持密码隐藏/显示、清除按钮、输入校验、自定义前缀图标,解决前期输入框样式不统一、键盘遮挡、输入异常等问题,适配鸿蒙手机/平板的输入交互。

dart 复制代码
// widgets/common_input.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../core/style/app_style.dart';

class CommonInput extends StatelessWidget {
  // 输入控制器
  final TextEditingController controller;
  // 占位提示文字
  final String hintText;
  // 前缀图标
  final IconData? prefixIcon;
  // 是否是密码输入
  final bool obscureText;
  // 输入校验回调
  final Function(String)? onChanged;
  // 输入键盘类型
  final TextInputType keyboardType;

  const CommonInput({
    super.key,
    required this.controller,
    required this.hintText,
    this.prefixIcon,
    this.obscureText = false,
    this.onChanged,
    this.keyboardType = TextInputType.text,
  });

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: controller,
      obscureText: obscureText,
      keyboardType: keyboardType,
      onChanged: onChanged,
      style: TextStyle(fontSize: 14.sp, color: Colors.black87),
      decoration: InputDecoration(
        // 输入框内边距,统一间距
        contentPadding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 14.h),
        // 占位提示样式
        hintText: hintText,
        hintStyle: TextStyle(fontSize: 14.sp, color: Colors.grey[400]),
        // 前缀图标
        prefixIcon: prefixIcon != null 
            ? Icon(prefixIcon, color: Colors.grey[400], size: 20.w) 
            : null,
        // 输入框边框(正常、聚焦、错误状态)
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(AppStyle.radius),
          borderSide: BorderSide(color: AppStyle.borderColor, width: 1.w),
        ),
        focusedBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(AppStyle.radius),
          borderSide: BorderSide(color: AppStyle.primaryColor, width: 1.5.w),
        ),
        errorBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(AppStyle.radius),
          borderSide: BorderSide(color: Colors.red, width: 1.w),
        ),
        // 清除按钮(输入内容后显示)
        suffixIcon: controller.text.isNotEmpty
            ? IconButton(
                icon: Icon(Icons.clear, size: 18.w, color: Colors.grey[400]),
                onPressed: () => controller.clear(),
              )
            : null,
        // 取消默认内边距,统一样式
        isDense: true,
      ),
    );
  }
}
文字讲解(使用方法)
dart 复制代码
// 账号输入框
CommonInput(
  controller: userCtrl,
  hintText: "请输入账号",
  prefixIcon: Icons.person,
  keyboardType: TextInputType.text,
  onChanged: (value) => checkInput(),
),

// 密码输入框
CommonInput(
  controller: pwdCtrl,
  hintText: "请输入密码",
  prefixIcon: Icons.lock,
  obscureText: true,
  onChanged: (value) => checkInput(),
),

// 搜索输入框
CommonInput(
  controller: searchCtrl,
  hintText: "搜索车场名称",
  prefixIcon: Icons.search,
  onChanged: (value) => controller.searchPark(value),
),

1.3 通用弹窗组件(全局复用,统一样式)

文字讲解

前5天开发中,我们写了多种弹窗(预约弹窗、缴费弹窗、超时提醒弹窗),样式不统一、代码重复。今天封装通用弹窗,支持标题、内容、确认/取消按钮自定义,统一弹窗大小、圆角、字体样式,适配鸿蒙多端弹窗位置,避免重复写AlertDialog代码,同时优化弹窗弹出/关闭动画,提升用户体验。

dart 复制代码
// widgets/common_dialog.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'common_button.dart';
import '../core/style/app_style.dart';

class CommonDialog extends StatelessWidget {
  // 弹窗标题
  final String title;
  // 弹窗内容
  final Widget content;
  // 确认按钮文字
  final String confirmText;
  // 取消按钮文字(可选,不传则不显示取消按钮)
  final String? cancelText;
  // 确认按钮点击事件
  final VoidCallback onConfirm;
  // 取消按钮点击事件(可选)
  final VoidCallback? onCancel;

  const CommonDialog({
    super.key,
    required this.title,
    required this.content,
    required this.confirmText,
    this.cancelText,
    required this.onConfirm,
    this.onCancel,
  });

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      // 弹窗圆角,统一全局样式
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(AppStyle.radius),
      ),
      // 弹窗内边距
      padding: EdgeInsets.all(20.w),
      // 弹窗标题
      title: Text(
        title,
        style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
        textAlign: TextAlign.center,
      ),
      // 弹窗内容(支持自定义Widget,灵活适配不同场景)
      content: content,
      // 弹窗按钮区域
      actions: [
        // 取消按钮(可选)
        if (cancelText != null)
          Expanded(
            child: CommonButton(
              text: cancelText!,
              onTap: onCancel ?? () => Get.back(),
              type: ButtonType.secondary,
            ),
          ),
        // 确认按钮
        Expanded(
          child: CommonButton(
            text: confirmText,
            onTap: onConfirm,
            type: ButtonType.primary,
          ),
        ),
      ],
      // 按钮间距
      actionsPadding: EdgeInsets.only(top: 10.h, bottom: 5.h, left: 20.w, right: 20.w),
      actionsAlignment: MainAxisAlignment.spaceBetween,
    );
  }
}
文字讲解(使用方法)
dart 复制代码
// 预约确认弹窗(复用通用弹窗)
Get.dialog(
  CommonDialog(
    title: "车位预约确认",
    content: Text("确认预约${slotId}号车位?预约后15分钟内未到达将自动取消", style: TextStyle(fontSize: 14.sp)),
    confirmText: "确认预约",
    cancelText: "取消",
    onConfirm: () => controller.bookSlot(slotId, parkName),
    onCancel: () => Get.back(),
  ),
);

// 缴费弹窗(复用通用弹窗)
Get.dialog(
  CommonDialog(
    title: "停车缴费",
    content: Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Text("订单号:${order.orderNo}", style: TextStyle(fontSize: 14.sp)),
        SizedBox(height: 8.h),
        Text("应付金额:${order.fee.toStringAsFixed(2)} 元", style: TextStyle(fontSize: 14.sp)),
        Text(order.isVip ? "已享会员8折优惠" : "普通用户", style: TextStyle(fontSize: 13.sp, color: Colors.grey[600])),
      ],
    ),
    confirmText: "确认缴费",
    cancelText: "取消",
    onConfirm: () => controller.payOrder(order),
  ),
);

1.4 通用加载组件(全局复用,适配多端)

文字讲解

加载组件用于页面初始化、数据请求、操作执行时的加载提示,统一加载样式(圆形进度条+提示文字),支持自定义提示文字,适配鸿蒙手机/平板的屏幕尺寸,避免每个页面重复写CircularProgressIndicator,同时优化加载动画的流畅度,提升用户体验。

dart 复制代码
// widgets/common_loading.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

class CommonLoading extends StatelessWidget {
  // 加载提示文字(可选,默认"加载中...")
  final String text;

  const CommonLoading({super.key, this.text = "加载中..."});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          // 圆形进度条,统一大小和颜色
          CircularProgressIndicator(
            strokeWidth: 3.w,
            valueColor: AlwaysStoppedAnimation<Color>(Theme.of(context).primaryColor),
          ),
          SizedBox(height: 12.h),
          // 加载提示文字,统一字体样式
          Text(
            text,
            style: TextStyle(fontSize: 14.sp, color: Colors.grey[600]),
          ),
        ],
      ),
    );
  }
}
文字讲解(使用方法)
dart 复制代码
// 页面加载中
body: CommonLoading(text: "正在获取车场数据..."),

// 定位加载中
if (controller.isLocating.value)
  CommonLoading(text: "正在定位中..."),

// 缴费加载中
if (controller.isPaying.value)
  CommonLoading(text: "正在处理缴费..."),

1.5 通用空页面组件(全局复用,统一样式)

文字讲解

空页面用于订单为空、搜索无结果、反馈记录为空等场景,统一空页面样式(图标+提示文字),支持自定义图标和提示文字,避免每个页面重复写空页面布局,提升全APP视觉一致性,适配鸿蒙多端屏幕。

dart 复制代码
// widgets/common_empty.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

class CommonEmpty extends StatelessWidget {
  // 空页面图标
  final IconData icon;
  // 空页面提示文字
  final String text;

  const CommonEmpty({super.key, required this.icon, required this.text});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          // 空页面图标,统一大小和颜色
          Icon(
            icon,
            size: 60.w,
            color: Colors.grey[300],
          ),
          SizedBox(height: 15.h),
          // 提示文字,统一字体样式
          Text(
            text,
            style: TextStyle(fontSize: 14.sp, color: Colors.grey[500]),
          ),
        ],
      ),
    );
  }
}
文字讲解(使用方法)
dart 复制代码
// 订单为空
if (controller.filterOrderList.isEmpty)
  CommonEmpty(icon: Icons.receipt_long_outlined, text: "暂无停车订单"),

// 搜索无结果
if (controller.filterParkList.isEmpty)
  CommonEmpty(icon: Icons.search_off, text: "未找到相关车场"),

// 反馈记录为空
if (controller.feedbackList.isEmpty)
  CommonEmpty(icon: Icons.comment_outlined, text: "暂无反馈记录"),

1.6 通用标题组件(全局复用,统一页面标题样式)

文字讲解

前5天开发中,每个页面的AppBar标题样式不统一,今天封装通用标题组件,统一标题字体、大小、对齐方式,支持自定义右侧图标和点击事件,适配鸿蒙多端AppBar布局,减少重复代码。

dart 复制代码
// widgets/common_app_bar.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

class CommonAppBar extends AppBar {
  // 标题文字
  final String titleText;
  // 右侧图标(可选)
  final IconData? rightIcon;
  // 右侧图标点击事件(可选)
  final VoidCallback? onRightTap;

  CommonAppBar({
    super.key,
    required this.titleText,
    this.rightIcon,
    this.onRightTap,
  }) : super(
          title: Text(
            titleText,
            style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold),
          ),
          centerTitle: true, // 标题居中
          elevation: 1, // 统一阴影高度
          actions: [
            if (rightIcon != null)
              IconButton(
                icon: Icon(rightIcon, size: 22.w),
                onPressed: onRightTap,
              ),
          ],
        );
}
文字讲解(使用方法)
dart 复制代码
// 普通页面标题
appBar: CommonAppBar(titleText: "我的订单"),

// 带右侧图标的标题(如设置页面)
appBar: CommonAppBar(
  titleText: "系统设置",
  rightIcon: Icons.save,
  onRightTap: () => saveSetting(),
),

文字讲解(组件封装总结)

以上6个通用组件,覆盖了项目中90%的高频UI场景,封装后有3个核心优势:

  1. 代码复用:无需重复写相同样式的组件,减少代码冗余,后期修改样式只需修改封装组件,全局自动生效;
  2. 视觉统一:全APP按钮、输入框、弹窗等样式一致,提升产品专业度,毕设观感更好;
  3. 维护便捷:组件单独分类,后期新增功能、修复bug,只需修改对应组件文件,降低维护成本。

三、版块2:意见反馈页面完整开发(详细版)

文字讲解(详细版)

意见反馈是商用APP必备的功能,用于收集用户使用过程中的问题、建议,提升产品体验,同时也是毕设中体现"产品思维"的加分项。今天我们开发完整的意见反馈页面,包含「反馈类型选择、反馈内容输入、提交反馈、查看反馈记录」四大功能,同时实现反馈记录本地缓存(重启APP不丢失),适配鸿蒙多端布局,交互流畅、逻辑完整。

2.1 反馈实体模型定义

文字讲解

首先定义反馈实体模型,记录反馈的核心信息:反馈ID、反馈类型、反馈内容、提交时间,为后续缓存和展示提供数据支撑,字段设计贴合真实商用场景。

dart 复制代码
// models/feedback_model.dart
class FeedbackModel {
  // 反馈唯一ID(时间戳+随机数,避免重复)
  final String feedbackId;
  // 反馈类型(问题反馈/功能建议/其他)
  final String type;
  // 反馈内容
  final String content;
  // 提交时间
  final String time;

  FeedbackModel({
    required this.feedbackId,
    required this.type,
    required this.content,
    required this.time,
  });
}

2.2 反馈控制器封装(处理逻辑)

文字讲解

创建FeedbackController,用于管理反馈类型选择、反馈内容、反馈记录列表、本地缓存逻辑,采用GetX响应式管理,确保UI实时刷新,逻辑与UI分离,符合企业级开发规范。

dart 复制代码
// controller/feedback_controller.dart
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
import '../models/feedback_model.dart';

class FeedbackController extends GetxController {
  static FeedbackController get to => Get.find();

  // 反馈类型列表(问题反馈/功能建议/其他)
  final List<String> feedbackTypes = ["问题反馈", "功能建议", "其他"];
  // 选中的反馈类型(默认第一个)
  final RxString selectedType = "问题反馈".obs;
  // 反馈内容控制器
  final RxString feedbackContent = "".obs;
  // 反馈记录列表(响应式)
  final RxList<FeedbackModel> feedbackList = <FeedbackModel>[].obs;

  @override
  void onInit() {
    super.onInit();
    // 初始化时读取本地缓存的反馈记录
    getFeedbackList();
  }

  // 选择反馈类型
  void selectType(String type) {
    selectedType.value = type;
  }

  // 提交反馈
  Future<void> submitFeedback() async {
    if (feedbackContent.value.trim().isEmpty) {
      Get.showSnackbar(const GetSnackBar(
        message: "请输入反馈内容",
        duration: Duration(seconds: 2),
      ));
      return;
    }

    // 生成唯一反馈ID(时间戳+随机数)
    String feedbackId = DateTime.now().millisecondsSinceEpoch.toString() +
        Random().nextInt(999).toString();
    // 格式化提交时间
    String time = DateTime.now().toString().split(" ")[0] +
        " " +
        DateTime.now().toString().split(" ")[1].substring(0, 5);
    // 创建反馈实体
    FeedbackModel feedback = FeedbackModel(
      feedbackId: feedbackId,
      type: selectedType.value,
      content: feedbackContent.value.trim(),
      time: time,
    );

    // 添加到反馈列表
    feedbackList.add(feedback);
    // 保存到本地缓存
    await saveFeedbackList();
    // 清空输入框
    feedbackContent.value = "";
    // 提示提交成功
    Get.showSnackbar(const GetSnackBar(
      message: "反馈提交成功,感谢您的建议!",
      duration: Duration(seconds: 2),
    ));
  }

  // 保存反馈记录到本地缓存(SharedPreferences)
  Future<void> saveFeedbackList() async {
    final sp = await SharedPreferences.getInstance();
    // 将反馈列表转为JSON字符串(SharedPreferences不支持直接保存对象列表)
    List<String> feedbackJsonList = feedbackList
        .map((feedback) => jsonEncode({
              "feedbackId": feedback.feedbackId,
              "type": feedback.type,
              "content": feedback.content,
              "time": feedback.time,
            }))
        .toList();
    await sp.setStringList("feedback_list", feedbackJsonList);
  }

  // 从本地缓存读取反馈记录
  Future<void> getFeedbackList() async {
    final sp = await SharedPreferences.getInstance();
    List<String>? feedbackJsonList = sp.getStringList("feedback_list");
    if (feedbackJsonList != null && feedbackJsonList.isNotEmpty) {
      // 将JSON字符串转为FeedbackModel列表
      feedbackList.assignAll(feedbackJsonList
          .map((json) => FeedbackModel(
                feedbackId: jsonDecode(json)["feedbackId"],
                type: jsonDecode(json)["type"],
                content: jsonDecode(json)["content"],
                time: jsonDecode(json)["time"],
              ))
          .toList());
    }
  }
}

2.3 意见反馈页面布局实现

文字讲解

意见反馈页面分为三个部分:顶部标题栏、中间反馈输入区(类型选择+内容输入)、底部提交按钮,同时添加反馈记录列表(下拉可查看历史反馈),适配鸿蒙手机/平板,布局规整、交互流畅,输入内容时键盘自动避让,避免遮挡。

dart 复制代码
// pages/feedback_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import '../controller/feedback_controller.dart';
import '../widgets/common_app_bar.dart';
import '../widgets/common_button.dart';
import '../widgets/common_input.dart';
import '../widgets/common_empty.dart';

class FeedbackPage extends GetView<FeedbackController> {
  FeedbackPage({super.key});

  @override
  Widget build(BuildContext context) {
    // 初始化控制器
    Get.putIfAbsent(() => FeedbackController());
    return Scaffold(
      appBar: const CommonAppBar(titleText: "意见反馈"),
      body: SingleChildScrollView(
        padding: EdgeInsets.all(15.w),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 反馈类型选择
            Text("反馈类型", style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.w500)),
            SizedBox(height: 10.h),
            // 类型选择横向列表
            Obx(() => SingleChildScrollView(
                  scrollDirection: Axis.horizontal,
                  child: Row(
                    children: controller.feedbackTypes.map((type) {
                      bool isSelected = controller.selectedType.value == type;
                      return Padding(
                        padding: EdgeInsets.only(right: 10.w),
                        child: InkWell(
                          onTap: () => controller.selectType(type),
                          child: Container(
                            padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 8.h),
                            decoration: BoxDecoration(
                              color: isSelected ? Colors.blueGrey[100] : Colors.white,
                              border: Border.all(
                                color: isSelected ? Colors.blueGrey : Colors.grey[300],
                                width: 1.w,
                              ),
                              borderRadius: BorderRadius.circular(20.r),
                            ),
                            child: Text(
                              type,
                              style: TextStyle(
                                fontSize: 14.sp,
                                color: isSelected ? Colors.blueGrey : Colors.grey[600],
                              ),
                            ),
                          ),
                        ),
                      );
                    }).toList(),
                  ),
                )),
            SizedBox(height: 20.h),
            // 反馈内容输入
            Text("反馈内容", style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.w500)),
            SizedBox(height: 10.h),
            // 多行输入框
            Container(
              padding: EdgeInsets.all(15.w),
              decoration: BoxDecoration(
                border: Border.all(color: Colors.grey[300], width: 1.w),
                borderRadius: BorderRadius.circular(10.r),
              ),
              child: TextField(
                maxLines: 5,
                style: TextStyle(fontSize: 14.sp),
                decoration: InputDecoration(
                  hintText: "请输入您的问题或建议...",
                  hintStyle: TextStyle(fontSize: 14.sp, color: Colors.grey[400]),
                  border: InputBorder.none,
                ),
                onChanged: (value) => controller.feedbackContent.value = value,
              ),
            ),
            SizedBox(height: 20.h),
            // 提交按钮
            CommonButton(
              text: "提交反馈",
              onTap: () => controller.submitFeedback(),
              type: ButtonType.primary,
            ),
            SizedBox(height: 30.h),
            // 历史反馈记录标题
            Text("历史反馈", style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.w500)),
            SizedBox(height: 10.h),
            // 历史反馈列表
            Obx(() {
              if (controller.feedbackList.isEmpty) {
                // 空页面
                return CommonEmpty(
                  icon: Icons.comment_outlined,
                  text: "暂无反馈记录",
                );
              } else {
                // 反馈列表
                return ListView.builder(
                  shrinkWrap: true,
                  physics: const NeverScrollableScrollPhysics(),
                  itemCount: controller.feedbackList.length,
                  itemBuilder: (context, index) {
                    FeedbackModel feedback = controller.feedbackList[index];
                    return Card(
                      elevation: 1,
                      borderRadius: BorderRadius.circular(10.r),
                      margin: EdgeInsets.only(bottom: 10.h),
                      child: Padding(
                        padding: EdgeInsets.all(15.w),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            // 反馈类型+时间
                            Row(
                              mainAxisAlignment: MainAxisAlignment.spaceBetween,
                              children: [
                                Text(
                                  feedback.type,
                                  style: TextStyle(
                                    fontSize: 14.sp,
                                    color: Colors.blueGrey,
                                    fontWeight: FontWeight.w500,
                                  ),
                                ),
                                Text(
                                  feedback.time,
                                  style: TextStyle(
                                    fontSize: 12.sp,
                                    color: Colors.grey[500],
                                  ),
                                ),
                              ],
                            ),
                            SizedBox(height: 8.h),
                            // 反馈内容
                            Text(
                              feedback.content,
                              style: TextStyle(fontSize: 14.sp, color: Colors.black87),
                            ),
                          ],
                        ),
                      ),
                    );
                  },
                );
              }
            }),
          ],
        ),
      ),
    );
  }
}

文字讲解(反馈页面功能总结)

意见反馈页面实现了完整的交互逻辑,核心亮点:

  1. 反馈类型可选择,满足不同用户反馈场景;
  2. 反馈内容支持多行输入,适配长文本反馈;
  3. 提交反馈后自动保存到本地缓存,重启APP不丢失;
  4. 支持查看历史反馈记录,展示反馈类型、内容、提交时间;
  5. 有完善的提示逻辑(未输入内容提示、提交成功提示);
  6. 适配鸿蒙手机/平板,布局无错乱、键盘无遮挡。

四、版块3:系统设置页面开发(详细版)

文字讲解(详细版)

系统设置页面是商用APP的必备页面,用于展示版本信息、提供清除缓存、关于我们、隐私政策等功能,提升产品的完整性和用户体验。今天我们开发完整的系统设置页面,功能齐全、布局规范,适配鸿蒙多端,同时实现清除缓存功能(核心亮点),符合毕设项目的完整性要求。

3.1 系统设置页面布局实现

文字讲解

系统设置页面采用列表布局(ListView),每个功能项为一个ListTile,包含图标、标题、副标题(可选)、右侧箭头/开关,布局清晰、交互直观,适配鸿蒙多端屏幕,统一视觉风格,与个人中心页面风格保持一致。

dart 复制代码
// pages/setting_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../controller/theme_controller.dart';
import '../widgets/common_app_bar.dart';
import '../core/constant/version.dart';

class SettingPage extends StatelessWidget {
  SettingPage({super.key});

  // 主题控制器
  final ThemeController themeController = Get.find<ThemeController>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: CommonAppBar(
        titleText: "系统设置",
      ),
      body: ListView(
        children: [
          // 深色模式切换
          Obx(() => SwitchListTile(
                leading: Icon(Icons.dark_mode, size: 22.w, color: Colors.blueGrey),
                title: Text("深色模式", style: TextStyle(fontSize: 15.sp)),
                subtitle: Text(
                  themeController.isDarkMode.value ? "当前为深色模式" : "当前为浅色模式",
                  style: TextStyle(fontSize: 12.sp, color: Colors.grey[500]),
                ),
                value: themeController.isDarkMode.value,
                onChanged: (val) => themeController.toggleTheme(),
                activeColor: Colors.blueGrey,
              )),
          // 分割线
          Divider(height: 1.h, color: Colors.grey[200]),
          // 清除缓存
          ListTile(
            leading: Icon(Icons.delete_outline, size: 22.w, color: Colors.blueGrey),
            title: Text("清除缓存", style: TextStyle(fontSize: 15.sp)),
            subtitle: Text("清除本地缓存的订单、反馈、登录信息", style: TextStyle(fontSize: 12.sp, color: Colors.grey[500])),
            trailing: const Icon(Icons.arrow_forward_ios, size: 16),
            onTap: () => clearCache(),
          ),
          // 分割线
          Divider(height: 1.h, color: Colors.grey[200]),
          // 版本信息
          ListTile(
            leading: Icon(Icons.info_outline, size: 22.w, color: Colors.blueGrey),
            title: Text("版本信息", style: TextStyle(fontSize: 15.sp)),
            subtitle: Text("当前版本:${Version.appVersion}", style: TextStyle(fontSize: 12.sp, color: Colors.grey[500])),
            trailing: const Icon(Icons.arrow_forward_ios, size: 16),
            onTap: () => showVersionInfo(),
          ),
          // 分割线
          Divider(height: 1.h, color: Colors.grey[200]),
          // 关于我们
          ListTile(
            leading: Icon(Icons.person_outline, size: 22.w, color: Colors.blueGrey),
            title: Text("关于我们", style: TextStyle(fontSize: 15.sp)),
            subtitle: Text("城市智慧停车管理系统 © 2026", style: TextStyle(fontSize: 12.sp, color: Colors.grey[500])),
            trailing: const Icon(Icons.arrow_forward_ios, size: 16),
            onTap: () => showAboutUs(),
          ),
          // 分割线
          Divider(height: 1.h, color: Colors.grey[200]),
          // 隐私政策
          ListTile(
            leading: Icon(Icons.privacy_tip_outlined, size: 22.w, color: Colors.blueGrey),
            title: Text("隐私政策", style: TextStyle(fontSize: 15.sp)),
            subtitle: Text("查看应用隐私政策与用户协议", style: TextStyle(fontSize: 12.sp, color: Colors.grey[500])),
            trailing: const Icon(Icons.arrow_forward_ios, size: 16),
            onTap: () => Get.toNamed(RoutePath.privacy),
          ),
        ],
      ),
    );
  }

  // 清除缓存功能
  Future<void> clearCache() async {
    Get.dialog(
      AlertDialog(
        title: const Text("清除缓存"),
        content: const Text("确认清除所有本地缓存吗?清除后登录信息、订单记录、反馈记录将丢失。"),
        actions: [
          TextButton(
            onPressed: () => Get.back(),
            child: const Text("取消"),
          ),
          TextButton(
            onPressed: () async {
              // 清空SharedPreferences所有缓存
              final sp = await SharedPreferences.getInstance();
              await sp.clear();
              // 提示清除成功
              Get.back();
              Get.showSnackbar(const GetSnackBar(
                message: "缓存清除成功",
                duration: Duration(seconds: 2),
              ));
            },
            child: const Text("确认"),
          ),
        ],
      ),
    );
  }

  // 版本信息弹窗
  void showVersionInfo() {
    Get.dialog(
      AlertDialog(
        title: const Text("版本信息"),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text("当前版本:${Version.appVersion}"),
            const SizedBox(height: 8),
            Text("更新内容:优化用户体验,修复已知bug,提升系统稳定性。"),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () => Get.back(),
            child: const Text("确定"),
          ),
        ],
      ),
    );
  }

  // 关于我们弹窗
  void showAboutUs() {
    Get.dialog(
      AlertDialog(
        title: const Text("关于我们"),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text("城市智慧停车管理系统"),
            const SizedBox(height: 8),
            Text("致力于为用户提供便捷、高效的停车服务,覆盖城市商圈、小区、市政停车场,实现一站式停车解决方案。"),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () => Get.back(),
            child: const Text("确定"),
          ),
        ],
      ),
    );
  }
}

3.2 版本常量定义

文字讲解

统一管理APP版本号,放在core/constant目录下,后期更新版本只需修改此处,全局自动生效,避免硬编码导致的修改繁琐问题。

dart 复制代码
// core/constant/version.dart
class Version {
  // APP版本号(语义化版本:主版本.次版本.修订版本)
  static const String appVersion = "1.0.0";
}

文字讲解(系统设置页面功能总结)

系统设置页面包含5个核心功能,完全贴合商用APP规范:

  1. 深色模式切换:与全局主题控制器联动,一键切换深浅色;
  2. 清除缓存:清空本地所有缓存(登录信息、订单、反馈),有确认弹窗,避免误操作;
  3. 版本信息:展示当前APP版本号和更新内容;
  4. 关于我们:展示APP简介和版权信息;
  5. 隐私政策:跳转至隐私政策页面(后续可补充隐私政策详情)。

所有功能交互流畅,适配鸿蒙多端,布局规整,提升了项目的完整性和专业度。

五、版块4:代码整体重构优化(详细版)

文字讲解(详细版)

前5天的开发的过程中,我们重点关注功能实现,部分代码存在冗余、命名不规范、逻辑与UI未分离、目录结构不清晰等问题,这些问题在毕设答辩中会被评委扣分,也不符合企业级开发规范。今天我们对整个项目的代码进行整体重构优化,重点解决以下问题,让代码更规范、更简洁、更易维护。

4.1 目录结构优化

文字讲解

优化后的目录结构更清晰,按功能分类更细致,新增constant目录(存放常量)、style目录(存放全局样式),删除冗余文件,调整文件位置,让后期查找和维护更便捷,符合企业级开发的目录规范。

优化后的目录结构:

复制代码
lib/
├── core/                 # 全局核心
│   ├── constant/         # 全局常量(版本、路由、配置)
│   │   ├── route_path.dart  # 路由常量
│   │   └── version.dart     # 版本常量
│   ├── route/            # 路由配置
│   │   └── route_config.dart # 路由表配置
│   ├── style/            # 全局样式(颜色、圆角、间距)
│   │   └── app_style.dart    # 全局样式常量
│   └── utils/            # 全局工具类
│       ├── permission_util.dart # 权限工具
│       ├── sp_util.dart         # 缓存工具
│       └── toast_util.dart      # 提示工具
├── controller/           # GetX控制器(按功能分类)
│   ├── auth_controller.dart    # 认证控制器(登录、会员)
│   ├── feedback_controller.dart # 反馈控制器
│   ├── park_controller.dart    # 车场控制器
│   ├── theme_controller.dart   # 主题控制器
│   └── main_controller.dart    # 主页面控制器
├── models/               # 数据实体模型
│   ├── feedback_model.dart     # 反馈模型
│   ├── order_model.dart        # 订单模型
│   ├── park_model.dart         # 车场模型
│   ├── slot_model.dart         # 车位模型
│   └── user_model.dart         # 用户模型
├── pages/                # 业务页面(按功能分类)
│   ├── auth/             # 认证相关页面
│   │   └── login_page.dart     # 登录页面
│   ├── feedback/         # 反馈相关页面
│   │   └── feedback_page.dart  # 意见反馈页面
│   ├── park/             # 车场相关页面
│   │   ├── home_page.dart      # 首页
│   │   ├── park_detail_page.dart # 车场详情页
│   │   └── park_page.dart      # 车场列表页
│   ├── order/            # 订单相关页面
│   │   └── order_page.dart     # 我的订单页面
│   ├── mine/             # 个人中心相关页面
│   │   ├── mine_page.dart      # 个人中心
│   │   ├── vip_page.dart       # 会员中心
│   │   └── setting_page.dart   # 系统设置页面
│   └── main_page.dart    # 主页面(底部导航)
├── widgets/              # 全局通用组件
│   ├── common_app_bar.dart     # 通用标题组件
│   ├── common_button.dart      # 通用按钮组件
│   ├── common_dialog.dart      # 通用弹窗组件
│   ├── common_empty.dart       # 通用空页面组件
│   ├── common_input.dart       # 通用输入框组件
│   └── common_loading.dart     # 通用加载组件
└── main.dart             # 项目入口

4.2 全局样式常量封装

文字讲解

前5天的开发中,颜色、圆角、间距、阴影等样式都是硬编码,后期修改样式时需要逐个页面修改,效率极低。今天我们将所有全局样式封装到app_style.dart中,统一管理,所有页面直接调用常量,修改时只需修改一处,全局自动生效。

dart 复制代码
// core/style/app_style.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

class AppStyle {
  // 全局主色(商务蓝灰)
  static const Color primaryColor = Color(0xFF5D69B1);
  // 全局边框色
  static const Color borderColor = Color(0xFFE5E7EB);
  // 全局阴影高度
  static const double shadowElevation = 2.0;
  // 全局圆角(统一为8r)
  static final double radius = 8.r;
  // 全局内边距(统一为15w)
  static final double padding = 15.w;
  // 全局间距(小间距8h,大间距15h)
  static final double spacingSmall = 8.h;
  static final double spacingLarge = 15.h;
}
文字讲解(使用方法)

所有页面的样式,全部调用AppStyle中的常量,示例如下:

dart 复制代码
// 按钮圆角
borderRadius: BorderRadius.circular(AppStyle.radius),

// 内边距
padding: EdgeInsets.all(AppStyle.padding),

// 间距
SizedBox(height: AppStyle.spacingLarge),

// 主色
color: AppStyle.primaryColor,

4.3 冗余代码精简

文字讲解

精简前5天开发中的冗余代码,重点优化以下几点:

  1. 重复的UI代码:全部替换为封装好的通用组件(如按钮、输入框、弹窗);
  2. 冗余的逻辑代码:将重复的逻辑(如缓存读取、权限申请)提取到工具类,全局复用;
  3. 无用代码:删除注释掉的代码、未使用的变量和方法;
  4. 重复的导入语句:统一导入路径,删除未使用的导入。

示例优化(精简前vs精简后):

dart 复制代码
// 精简前(重复写按钮样式)
ElevatedButton(
  onPressed: () => doLogin(),
  style: ElevatedButton.styleFrom(
    backgroundColor: Color(0xFF5D69B1),
    minimumSize: Size(double.infinity, 48.h),
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.r)),
  ),
  child: Text("立即登录", style: TextStyle(fontSize: 15.sp, color: Colors.white)),
),

// 精简后(调用通用按钮组件)
CommonButton(
  text: "立即登录",
  onTap: () => doLogin(),
  type: ButtonType.primary,
),

4.4 命名规范统一

文字讲解

统一项目中所有变量、方法、类、文件的命名规范,符合Flutter开发标准,提升代码可读性,避免命名混乱,具体规范如下:

  1. 类名:采用帕斯卡命名法(PascalCase),如CommonButton、ParkController;
  2. 方法名、变量名:采用驼峰命名法(camelCase),如submitFeedback、selectedType;
  3. 常量名:采用全大写+下划线,如APP_VERSION、PRIMARY_COLOR;
  4. 文件命名:采用小写+下划线,如common_button.dart、park_controller.dart;
  5. 注释规范:关键方法、复杂逻辑添加详细注释,说明功能、参数、返回值。

4.5 业务逻辑与 UI 分离

文字讲解(详细版)

前 5 天部分页面把业务计算、缓存读写、状态判断、接口请求直接写在页面 build 里,UI 和逻辑混在一起,代码臃肿、可读性差、后期维护困难。重构后严格遵循 GetX 分层思想:

pages 页面层:只负责渲染 UI、接收用户点击、调用控制器方法,不写任何业务逻辑;

controller 控制层:负责所有业务逻辑(计时、计费、预约、缴费、缓存读写、数据处理);

utils 工具层:通用工具(权限、缓存、Toast、格式转换);

models 模型层:只定义数据结构,不做逻辑处理。

以停车计时为例,重构前逻辑写在页面内,重构后全部放入 ParkController,页面只调用:

dart

// 页面只触发,不写逻辑

controller.startTimer(park);

控制器内部完整处理计时、费用计算、状态更新,实现解耦,毕设答辩时可以重点讲解 "分层解耦、高内聚低耦合",体现软件工程思维。

4.6 全局工具类统一整合

文字讲解

将分散在各个页面的工具方法统一抽离到utils文件夹,封装成独立工具类,全局调用,减少重复代码:

权限工具 PermissionUtil:统一封装定位、存储、相机权限申请;

缓存工具 SpUtil:统一封装增删改查,简化 SharedPreferences 调用;

Toast 工具 ToastUtil:统一提示样式、时长、位置;

格式工具 FormatUtil:时间格式化、费用保留两位小数、距离格式化。

示例统一缓存工具:

dart

// utils/sp_util.dart

class SpUtil {

static late SharedPreferences _sp;

static Future init() async {

_sp = await SharedPreferences.getInstance();

}

static Future setString(String key, String value) async {

await _sp.setString(key, value);

}

static String getString(String key) {

return _sp.getString(key) ?? "";

}

static Future clear() async {

await _sp.clear();

}

}

后续所有缓存操作直接调用SpUtil.setString(),不再重复实例化,代码更简洁。

六、版块 5:全量遗留 Bug 排查与修复(详细版)

文字讲解

前 5 天快速迭代开发,积累了大量细节 bug,在真机、平板、深色模式下容易暴露,今天一次性全部修复,保证项目稳定可用,毕设真机演示不翻车。下面逐条列出问题、原因、修复方案,全部为停车项目真实高频 bug。

5.1 地图相关 Bug 修复

高德地图深色模式不跟随全局主题

原因:原生地图控件不受 Flutter 主题控制;

修复:通过高德 SDK 设置夜间模式,监听 ThemeController 自动切换地图样式。

点位标注在平板大屏偏移

原因:硬编码图标大小;

修复:使用MediaQuery动态适配图标尺寸。

定位频繁跳动、卡顿

原因:未停止定位监听;

修复:页面销毁时销毁定位实例,释放资源。

5.2 订单 / 计费逻辑 Bug 修复

停车时长跨天计费异常

原因:按小时简单累加,未判断 24 小时封顶;

修复:计费算法增加if(hour>24) hour=24,实现单日封顶。

会员折扣切换不实时生效

原因:会员状态未用.obs响应式;

修复:isVip改为RxBool,实时监听更新费用。

订单重复生成

原因:结束停车按钮可多次点击;

修复:增加isStopping锁,防止重复提交。

5.3 UI 与适配 Bug 修复

GridView 车位在平板列数错乱

原因:固定 crossAxisCount;

修复:动态计算:宽度 > 600 为 6 列,手机 4 列。

深色模式输入框、卡片颜色不统一

原因:部分颜色硬编码;

修复:全部引用Theme.of(context).colorScheme。

键盘遮挡反馈、登录输入框

原因:页面未嵌套 SingleChildScrollView;

修复:所有长表单页面统一包裹滚动布局。

5.4 缓存与权限 Bug 修复

退出登录后常用车场未清空

原因:只清空用户信息,未清空车场缓存;

修复:logout 时同时删除recent_park缓存 key。

鸿蒙部分机型权限弹窗不弹出

原因:未适配鸿蒙权限策略;

修复:增加手动跳转系统设置开启权限逻辑。

七、版块 6:多端适配深度调优(手机 + 平板 + 鸿蒙横屏)

文字讲解

毕设加分关键就是多端适配能力,今天对全页面做精细化适配,实现:

手机竖屏:紧凑布局,按钮、间距适配小屏;

平板横屏:自动拓宽布局,网格自动变多列;

鸿蒙折叠屏:自适应窗口大小;

字体自动缩放,不出现文字溢出。

6.1 动态布局封装工具

dart

// utils/adapt_util.dart

class AdaptUtil {

// 判断是否平板

static bool isTablet(BuildContext context) {

return MediaQuery.of(context).size.width > 600;

}

// 获取网格列数

static int getGridCrossCount(BuildContext context) {

return isTablet(context) ? 6 : 4;

}

}

车位、车场列表直接调用,一行实现多端自动切换。

6.2 间距、内边距差异化适配

手机:padding:15.w

平板:自动放大为20.w

通过AdaptUtil统一控制,不用每个页面单独写。

6.3 横屏适配导航与地图

横屏时地图占比扩大,搜索栏横向拉长,底部导航改为侧边导航,提升大屏体验。

八、版块 7:全局异常捕获与稳定性优化

文字讲解

商用 APP 必须防止闪退,今天加入全局异常捕获,捕获未处理异常、网络异常、地图加载异常,避免 APP 直接崩溃。

7.1 main.dart 全局异常捕获

dart

void main() {

FlutterError.onError = (details) {

debugPrint("全局异常:${details.exception}");

};

runApp(const MyApp());

}

7.2 网络异常统一处理

封装 dio 请求拦截器,网络失败统一提示 "网络异常,请检查网络",避免空白页面。

7.3 空数据兜底处理

所有列表为空、定位失败、地图加载失败,统一显示CommonEmpty组件,不再空白白屏。

九、版块 8:Day6 新手高频问题详解(超详细)

问题 1:封装通用组件后样式不生效?

解答:必须全部使用flutter_screenutil适配单位;全局样式常量统一导入;主题切换必须用 Obx 包裹组件。

问题 2:意见反馈缓存重启丢失?

解答:json 序列化 / 反序列化字段必须一一对应;SharedPreferences 保存 list 时要用setStringList;key 名称全程一致。

问题 3:清除缓存后部分页面闪退?

解答:清除后要延迟跳转首页;控制器数据清空顺序:先清空缓存→清空响应式数据→路由跳转;避免异步未完成就退出。

问题 4:平板适配后布局挤压?

解答:GridView 必须开启shrinkWrap:true、physics:NeverScrollableScrollPhysics;动态列数严格按宽度判断。

问题 5:高德地图深色模式切换无效?

解答:必须调用高德原生夜间模式接口;不能只修改 Flutter 背景色;鸿蒙部分机型需要重启地图实例。

十、Day6 开发总结(完整版)

今天 Day6 我们完成项目工业化收尾全流程,是从 "学生练手项目" 升级为 "商业级可交付产品" 的关键一天,所有优化点完全对标企业开发标准:

深度封装 6 大全局通用组件(按钮、输入框、弹窗、加载、空页面、标题),大幅减少代码冗余,全 APP 视觉统一;

完整开发意见反馈模块,支持多类型反馈、多行输入、历史记录、本地持久化,完善产品闭环;

开发系统设置页面,包含深色模式、清除缓存、版本信息、关于我们、隐私政策,符合上架规范;

重构整体项目目录结构、统一全局样式、精简冗余代码、规范命名、实现 UI 与业务逻辑彻底解耦;

全量排查并修复前 5 天所有遗留 bug(地图、计费、适配、缓存、权限),项目稳定性大幅提升;

深度优化鸿蒙多端适配,手机 / 平板 / 横屏自动适配,毕设展示极具竞争力;

加入全局异常捕获、网络异常处理、空页面兜底,实现商用级稳定性。

至此,城市智慧停车管理系统已具备:地图定位 --- 车场查询 --- 车位预约 --- 停车计时 --- 阶梯计费 --- 导航 --- 订单缴费 --- 会员权益 --- 登录权限 --- 主题切换 --- 隐私合规 --- 反馈设置 --- 多端适配全链路能力,代码规范、功能完整、体验流畅、可直接打包真机运行,毕设、面试、作品集都属于高分优质项目。

十一、下期 Day7 预告(最终完结篇)

Day7 为项目最终闭环,完成:最终细节调优、鸿蒙真机完整演示、毕设答辩项目总结、全套源码结构梳理、可扩展方向规划、打包发布最终配置,整套实战项目正式完结。

相关推荐
七牛开发者1 小时前
开源项目观察|ds4:本地 Agent 推理,不只是把模型跑起来
人工智能·redis·算法·开源
OurBMC社区1 小时前
玩转OurBMC第二十六期:OpenBMC固件远程更新原理与实践(下)
开源·开放原子·ourbmc
xmdy58661 小时前
Flutter + 开源鸿蒙实战|城市智慧停车管理系统 Day5 超时提醒弹窗+深浅色全局主题+登录页面+记住登录+隐私协议+全局UI美化+鸿蒙打包配置
flutter·开源·harmonyos
javajenius1 小时前
Chroma:AI应用的开源向量数据基础设施
人工智能·其他·开源
廖松洋(Alina)2 小时前
01环境搭建与项目创建-鸿蒙PC端Electron开发
华为·electron·开源·harmonyos·鸿蒙
三声三视2 小时前
Electron鸿蒙桌面应用打包部署完全指南(含自动更新)
前端·electron·前端框架·harmonyos·鸿蒙·桌面端
W蘭2 小时前
Flutter从入门到实战-02-Flutter框架核心
flutter
三声三视2 小时前
Electron for 鸿蒙PC:用 Node-API 打通原生系统调用,告别“Web 孤岛
electron·harmonyos·桌面应用·鸿蒙
该昵称用户已存在2 小时前
能碳融合・数据驱动:MyEMS 开源能源管理系统的碳核算与能效优化一体化路径
开源·能源