flutter_for_openharmony城市井盖地图app实战+工单详情实现

1. 这个功能解决什么问题
工单详情是巡检人员执行井盖巡检任务的核心入口,其功能设计需贴合一线巡检场景的实际需求:
- 基础信息呈现:清晰展示工单标题、执行状态、所属片区、关联井盖编码、执行截止时间,让巡检人员快速掌握任务核心信息
- 执行步骤引导:拆解“到达现场→检查井盖→上传结果”的标准化巡检流程,降低操作门槛
- 操作功能支撑:提供“转应急”“提交复核”核心操作,适配巡检中突发情况与任务收尾场景
- 复用性设计:支持从工单列表传参进入(关联具体工单),也可无参独立运行(用于UI调试、功能演示)
这个页面是典型的“详情+操作”组合,适合做 StatelessWidget + 参数传递的最佳实践示例。
2. 相关文件一览
lib/feature_pages.dart:核心页面文件,包含WorkOrderDetailPage组件的完整实现lib/mock_data.dart:模拟数据文件,通过buildMockWorkOrders方法生成测试用工单数据
3. 参数传递设计
在Flutter中,StatelessWidget适合纯展示类页面,工单详情页无需内部状态变更,因此选择该组件类型:
class WorkOrderDetailPage extends StatelessWidget {
final WorkOrder? order;
const WorkOrderDetailPage({super.key, this.order});
}
关键设计要点:
- 定义可选参数
order,类型为WorkOrder?,允许传参或不传参两种场景 - 构造方法使用
super.key遵循Flutter组件规范,保证组件标识唯一性 - 无状态设计让页面渲染更高效,符合“展示型页面”的性能优化原则
4. 默认数据兜底
为避免无参场景下页面空白,需为工单数据设置兜底逻辑:
final w = order ?? buildMockWorkOrders().first;
该设计的核心优势:
- 独立运行能力:页面可脱离工单列表单独调试,无需依赖外部数据传递
- 开发效率提升:UI调试时无需反复模拟传参,直接查看默认数据渲染效果
- 体验保障:避免空指针导致的页面崩溃或空白,提升用户体验
5. 基础信息卡片
基础信息采用Card+Padding布局,兼顾美观与信息层级:
Card(
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(w.title,
style: Theme.of(context)
.textTheme
.titleLarge
?.copyWith(fontWeight: FontWeight.w800)),
const SizedBox(height: 8),
Text('状态: ${w.status}'),
],
),
),
)
布局与样式设计细节:
- Card组件提供阴影与圆角,区分不同信息模块,提升视觉层次感
- CrossAxisAlignment.start让文本左对齐,符合中文阅读习惯
- titleLarge样式加粗处理,突出工单标题的核心地位
- SizedBox设置8px间距,避免文本拥挤,提升可读性
- 补充片区、关联井盖、截止时间等字段时,保持相同的间距与样式规范
6. 执行步骤说明
巡检步骤采用ListTile+Divider组合,清晰展示每一步操作要求:
Card(
child: Column(
children: const [
ListTile(
leading: Icon(Icons.check_circle_outline),
title: Text('1. 到达现场'),
subtitle: Text('定位到点位后开始巡检'),
),
Divider(height: 1),
],
),
)
交互与视觉设计要点:
- leading使用勾选图标,强化“步骤清单”的视觉认知
- Divider分割线高度设为1px,弱化分割线视觉占比,保持页面简洁
- subtitle补充步骤说明,明确每一步的操作要点(如检查井盖需关注外观、沉降等)
- 步骤编号前置,符合用户对流程类信息的阅读习惯
7. 操作按钮
底部操作按钮采用Row+Expanded布局,保证按钮适配不同屏幕宽度:
Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () {
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(content: Text('已转为应急(模拟)')));
},
child: const Text('转应急'),
),
),
const SizedBox(width: 12),
],
)
按钮设计规范:
- OutlinedButton用于次要操作(转应急),FilledButton用于主要操作(提交),通过样式区分操作优先级
- Expanded让两个按钮平分宽度,保证页面布局的对称性
- 12px间距避免按钮紧挨,提升点击容错率
- SnackBar反馈操作结果,让用户明确操作是否生效(模拟接口调用后的提示)
8. 完整页面代码(核心片段)
整合上述模块,形成工单详情页核心代码结构:
class WorkOrderDetailPage extends StatelessWidget {
final WorkOrder? order;
const WorkOrderDetailPage({super.key, this.order});
Widget build(BuildContext context) {
final w = order ?? buildMockWorkOrders().first;
return Scaffold(
appBar: AppBar(title: const Text('工单详情')),
body: ListView(
padding: const EdgeInsets.all(12),
children: [
// 基础信息卡片
// 执行步骤卡片
// 操作按钮
],
),
);
}
}
页面结构设计逻辑:
- Scaffold作为页面根布局,提供AppBar与body基础结构
- ListView作为body容器,适配内容超出屏幕时的滚动需求
- 12px全局内边距,避免内容紧贴屏幕边缘,提升视觉舒适度
- 数据兜底逻辑在build方法内执行,保证每次构建都能获取有效数据
9. 工单详情数据模型
为规范工单数据管理,定义结构化的数据模型:
class WorkOrderDetail {
final String id;
final String title;
final String description;
final WorkOrderStatus status;
final String district;
const WorkOrderDetail({
required this.id,
required this.title,
required this.description,
required this.status,
required this.district,
});
}
数据模型设计原则:
- 核心字段使用
required修饰,保证数据完整性 - 枚举类型(如WorkOrderStatus)替代字符串,避免状态值不规范
- 可选字段(如completedAt、assignedTo)设为可空类型,适配不同工单状态
- 容器类型字段(如attachments、steps)设置默认空数组,避免空指针
工单步骤子模型设计,细化每一步的执行信息:
class WorkOrderStep {
final String id;
final String title;
final String description;
final WorkOrderStepStatus status;
const WorkOrderStep({
required this.id,
required this.title,
required this.description,
required this.status,
});
}
步骤模型设计要点:
- 独立的步骤ID,支持精准更新单个步骤状态
- 步骤状态枚举(pending/inProgress/completed/failed),覆盖全流程状态
- 完成时间、执行人等字段设为可空,仅在步骤完成后赋值
工单位置模型,适配地图定位场景:
class WorkOrderLocation {
final double latitude;
final double longitude;
final String address;
const WorkOrderLocation({
required this.latitude,
required this.longitude,
required this.address,
});
}
位置模型设计细节:
- 经纬度使用double类型,保证定位精度
- 地址字段存储详细位置信息,适配巡检人员的线下定位需求
- 可选的description字段,补充点位特征描述(如“交叉口东南角”)
10. 工单详情状态管理
使用Provider实现工单数据的状态管理,解耦UI与数据:
class WorkOrderDetailProvider extends ChangeNotifier {
WorkOrderDetail? _workOrder;
bool _loading = false;
String? _error;
WorkOrderDetail? get workOrder => _workOrder;
bool get loading => _loading;
String? get error => _error;
}
状态管理核心设计:
- 私有变量存储核心状态(工单数据、加载状态、错误信息)
- 公开getter方法,避免外部直接修改状态,保证数据安全性
- ChangeNotifier混入,支持状态变更时通知UI刷新
加载工单详情的核心方法,模拟异步请求流程:
Future<void> loadWorkOrderDetail(String workOrderId) async {
_loading = true;
_error = null;
notifyListeners();
try {
await Future.delayed(const Duration(seconds: 1));
_workOrder = _generateMockWorkOrderDetail(workOrderId);
_loading = false;
notifyListeners();
} catch (e) {
_error = e.toString();
_loading = false;
notifyListeners();
}
}
异步方法设计规范:
- 开始请求时设置loading为true,清空错误信息,通知UI显示加载状态
- Future.delayed模拟网络请求延迟,贴合真实场景
- try-catch捕获异常,设置错误信息,保证页面稳定性
- 每次状态变更后调用notifyListeners,触发UI刷新
更新工单状态的方法,处理状态变更逻辑:
Future<void> updateWorkOrderStatus(WorkOrderStatus newStatus) async {
if (_workOrder == null || _updating) return;
_updating = true;
notifyListeners();
try {
await Future.delayed(const Duration(milliseconds: 500));
_workOrder = _workOrder!.copyWith(status: newStatus);
_updating = false;
notifyListeners();
} catch (e) {
_error = e.toString();
_updating = false;
notifyListeners();
}
}
状态更新设计要点:
- 前置判断避免重复请求(_updating为true时直接返回)
- 500ms延迟模拟接口响应,避免操作反馈过快
- copyWith方法更新状态,保证不可变数据规范
- 异常处理保证更新失败时恢复状态,提示错误信息
完成工单步骤的方法,精准更新单个步骤:
Future<void> completeStep(String stepId, Map<String, dynamic> results) async {
if (_workOrder == null || _updating) return;
_updating = true;
notifyListeners();
try {
final updatedSteps = _workOrder!.steps.map((step) {
if (step.id == stepId) {
return step.copyWith(status: WorkOrderStepStatus.completed);
}
return step;
}).toList();
_workOrder = _workOrder!.copyWith(steps: updatedSteps);
_updating = false;
notifyListeners();
} catch (e) {
_error = e.toString();
_updating = false;
notifyListeners();
}
}
步骤更新核心逻辑:
- map遍历步骤列表,匹配目标步骤ID后更新状态
- copyWith方法保证原数据不被修改,符合Flutter不可变数据设计理念
- 支持传入results参数,存储步骤执行结果(如检查记录、照片链接)
模拟数据生成方法,用于开发调试:
WorkOrderDetail _generateMockWorkOrderDetail(String workOrderId) {
return WorkOrderDetail(
id: workOrderId,
title: '井盖巡检工单 #$workOrderId',
status: WorkOrderStatus.inProgress,
district: '东城区',
coverCode: 'MH-1001',
);
}
模拟数据设计要点:
- 动态生成工单标题(拼接ID),区分不同测试工单
- 默认状态设为“执行中”,贴合巡检场景的常见状态
- 固定片区与井盖编码,保证测试数据的一致性
11. 高级工单详情组件
构建功能更丰富的高级详情组件,提升用户体验:
class AdvancedWorkOrderDetailWidget extends StatelessWidget {
final WorkOrderDetail workOrder;
final Function(WorkOrderStatus) onStatusUpdate;
const AdvancedWorkOrderDetailWidget({
super.key,
required this.workOrder,
required this.onStatusUpdate,
});
}
高级组件设计原则:
- 接收工单数据与回调函数,实现UI与逻辑解耦
- 无状态设计,状态变更通过父组件回调处理
- 泛化的回调参数(WorkOrderStatus),支持扩展更多状态操作
组件构建方法,拆分多个子模块:
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeader(context),
const SizedBox(height: 24),
_buildBasicInfo(context),
],
),
);
}
布局设计要点:
- SingleChildScrollView适配长内容滚动,避免内容溢出
- 16px全局内边距,比基础版更大的间距,提升高级组件的视觉舒适度
- 24px模块间距,区分不同信息板块,增强页面呼吸感
- CrossAxisAlignment.start保证所有模块左对齐,统一视觉风格
头部模块设计,突出核心信息:
Widget _buildHeader(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
CircleAvatar(
backgroundColor: _getStatusColor(workOrder.status).withOpacity(0.1),
child: Icon(_getStatusIcon(workOrder.status)),
),
const SizedBox(width: 12),
Expanded(child: Text(workOrder.title)),
],
),
),
);
}
头部设计细节:
- CircleAvatar承载状态图标,通过颜色编码区分不同状态
- 状态图标与标题的12px间距,保证视觉平衡
- Expanded包裹标题,避免长标题溢出
- Card组件提升头部模块的视觉层级,突出核心信息
状态颜色映射方法,统一视觉规范:
Color _getStatusColor(WorkOrderStatus status) {
switch (status) {
case WorkOrderStatus.pending:
return Colors.grey;
case WorkOrderStatus.inProgress:
return Colors.orange;
case WorkOrderStatus.completed:
return Colors.green;
default:
return Colors.grey;
}
}
颜色设计规范:
- 待处理(pending):灰色,代表未开始
- 执行中(inProgress):橙色,代表进行中
- 已完成(completed):绿色,代表成功完成
- 统一的颜色映射方法,保证全页面状态颜色一致
基本信息模块,标准化展示字段:
Widget _buildBasicInfo(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
_buildInfoRow('片区', workOrder.district),
_buildInfoRow('关联井盖', workOrder.coverCode),
],
),
),
);
}
信息行设计要点:
- 统一的_infoRow方法,保证所有字段展示样式一致
- Card组件包裹,区分基础信息与其他模块
- 16px内边距,保证文本与容器边缘的距离
信息行通用组件,标准化字段展示:
Widget _buildInfoRow(String label, String value) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
children: [
SizedBox(width: 80, child: Text(label)),
const SizedBox(width: 16),
Expanded(child: Text(value)),
],
),
);
}
信息行布局规范:
- 标签固定80px宽度,保证所有标签对齐
- 16px标签与值的间距,避免拥挤
- Expanded包裹值文本,适配长文本换行
- 12px底部间距,区分不同字段行
位置信息模块,适配地图展示:
Widget _buildLocationInfo(BuildContext context) {
if (workOrder.location == null) return const SizedBox.shrink();
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
_buildInfoRow('地址', workOrder.location!.address),
_buildInfoRow('坐标', '${workOrder.location!.latitude}, ${workOrder.location!.longitude}'),
],
),
),
);
}
位置模块设计要点:
- 前置判断位置是否为空,避免空指针
- 地址与坐标分开展示,满足不同场景的查看需求
- 地图占位容器(200px高度),预留地图集成接口
步骤列表模块,可视化展示执行进度:
Widget _buildSteps(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: workOrder.steps.map((step) {
return _buildStepItem(context, step);
}).toList(),
),
),
);
}
步骤列表设计逻辑:
- map遍历步骤数据,生成单个步骤项
- Card组件包裹,区分步骤模块与其他信息
- 16px内边距,保证步骤项与容器边缘的距离
单个步骤项设计,展示状态与操作:
Widget _buildStepItem(BuildContext context, WorkOrderStep step) {
return Container(
margin: const EdgeInsets.only(bottom: 16),
child: Row(
children: [
CircleAvatar(
radius: 16,
backgroundColor: _getStepStatusColor(step.status),
child: Text(step.id.substring(5)),
),
const SizedBox(width: 12),
Expanded(child: Text(step.title)),
],
),
);
}
步骤项设计细节:
- 圆形头像展示步骤编号,通过背景色区分状态
- 16px头像半径,保证视觉突出且不占过多空间
- 12px头像与标题间距,提升视觉平衡
- 16px底部外边距,区分不同步骤项
操作按钮模块,适配状态变更:
Widget _buildActions(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () => _showEmergencyDialog(context),
child: const Text('转应急'),
),
),
const SizedBox(width: 12),
],
),
),
);
}
操作按钮设计要点:
- 按钮宽度平分,保证布局对称
- 点击事件触发对话框,避免误操作
- OutlinedButton/FilledButton区分操作优先级
- 12px按钮间距,提升点击容错率
对话框组件,确认关键操作:
void _showEmergencyDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('转为应急工单'),
content: const Text('确定要将此工单转为应急工单吗?'),
actions: [
TextButton(onPressed: () => Navigator.pop(context), child: const Text('取消')),
TextButton(onPressed: () => onStatusUpdate(WorkOrderStatus.assigned), child: const Text('确定')),
],
),
);
}
对话框设计规范:
- 明确的标题与内容,告知用户操作后果
- 取消/确定按钮对称布局,符合用户操作习惯
- 确定按钮触发状态更新回调,完成业务逻辑
12. 小结
工单详情的实现展现了 Flutter 开发的几个重要原则:
数据模型:复杂的工单详情结构和步骤管理
状态管理:使用 Provider 管理详情状态和更新
用户体验:加载状态、确认对话框、步骤追踪
组件化设计:可复用的详情组件和步骤项目
交互设计:状态更新、步骤完成、附件管理
这样的设计不仅满足了工单详情展示的基本需求,还为后续的功能扩展(如实时协作、多媒体支持、智能推荐等)提供了良好的基础架构。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐

所有评论(0)