flutter_for_openharmony城市井盖地图app实战+工单列表实现
本文介绍了工单列表页面的设计与实现,该功能作为巡检人员的工作入口,支持状态筛选、列表展示和详情跳转等核心功能。文章详细阐述了: 功能设计要点:包括状态筛选、列表信息展示、状态视觉化标识和详情跳转等功能模块 技术实现方案: 使用Mock数据模拟工单信息,确保测试覆盖多种状态 通过PopupMenuButton实现状态筛选交互 采用ListView.separated构建工单列表,优化视觉呈现 设计状

1. 这个功能解决什么问题
工单列表是巡检人员的核心工作入口,作为日常巡检任务的核心操作载体,需满足多维度的使用需求:
- 状态筛选:支持待分派、执行中、待复核、已完成四类核心状态的精准筛选,适配巡检全流程的任务管理
- 列表展示:清晰呈现工单标题、所属片区、关联井盖编号、截止时间等核心信息,便于快速识别任务
- 状态指示:通过彩色圆点视觉化标识工单状态,无需阅读文字即可快速判断任务进度
- 详情跳转:点击列表项可直达工单详情页,满足查看任务详情、执行操作的深层需求
这个页面是典型的“列表+筛选”组合,适合做 PopupMenuButton + ListView.separated 的最佳实践示例,既符合Flutter组件设计规范,又能适配开源鸿蒙的跨平台特性。
2. 相关文件一览
lib/feature_pages.dart(WorkOrdersPage):工单列表页面的核心布局与交互逻辑实现文件lib/mock_data.dart(buildMockWorkOrders):模拟工单数据构造文件,为功能开发提供测试数据支撑
3. Mock 数据构造
lib/mock_data.dart 中提供了工单的 Mock 数据,模拟数据的设计需兼顾真实性与覆盖性:
List<WorkOrder> buildMockWorkOrders() {
const statuses = ['待分派', '执行中', '待复核', '已完成'];
const districts = ['东城区', '西城区', '南城区', '北城区', '高新区'];
return List<WorkOrder>.generate(24, (i) {
final status = statuses[i % statuses.length];
final d = districts[i % districts.length];
return WorkOrder(
id: _uuid.v4(),
title: '井盖巡检工单 #${i + 1}',
status: status,
district: d,
dueAt: DateTime.now().add(Duration(hours: (i + 1) * 6)),
coverCode: 'MH-${(1000 + (i % 18)).toString()}',
);
});
}
- 状态覆盖设计:通过
i % statuses.length循环取值,确保四类工单状态都有对应模拟数据,满足筛选功能测试需求 - 时间模拟逻辑:
dueAt采用当前时间叠加递增小时数的方式,模拟不同工单的截止时间,贴近真实业务场景 - 区域与编号设计:片区按固定数组循环分配,井盖编号按规则生成,保证数据格式统一且具备辨识度
4. 状态定义
WorkOrdersPage 中需先定义核心状态变量,为筛选逻辑提供基础支撑:
class _WorkOrdersPageState extends State<WorkOrdersPage> {
late final List<WorkOrder> _all = buildMockWorkOrders();
String _status = '全部';
}
- 数据初始化:
_all使用late final修饰,保证页面加载时仅构造一次Mock数据,避免重复初始化造成性能损耗 - 筛选默认值:
_status初始值设为“全部”,确保页面首次加载时展示所有状态的工单,符合用户默认查看全量数据的习惯
5. 状态筛选逻辑
筛选逻辑需在 build 方法中实现,保证状态变化时实时更新列表数据:
final statuses = <String>['全部', ...{for (final e in _all) e.status}];
final list = _all
.where((e) => _status == '全部' || e.status == _status)
.toList(growable: false);
- 去重处理:通过
Set集合特性对工单状态去重后,再拼接“全部”选项,确保筛选菜单无重复选项 - 过滤规则:
where方法实现核心筛选逻辑,“全部”状态下匹配所有工单,其他状态仅匹配对应工单 - 不可变列表:
toList(growable: false)创建不可变列表,减少内存占用,提升列表渲染性能
6. 状态筛选 UI
页面右上角通过 PopupMenuButton 实现状态筛选的交互入口,UI设计需兼顾易用性与直观性:
AppBar(
title: const Text('工单列表'),
actions: [
PopupMenuButton<String>(
initialValue: _status,
onSelected: (v) => setState(() => _status = v),
itemBuilder: (context) => statuses
.map((e) => PopupMenuItem(value: e, child: Text('筛选: $e')))
.toList(growable: false),
),
],
)
- 初始值展示:
initialValue绑定当前筛选状态,用户可直观看到当前筛选条件 - 状态更新:
onSelected回调中通过setState更新_status,触发页面重建与列表重新过滤 - 菜单文案设计:
PopupMenuItem文案添加“筛选:”前缀,明确菜单功能,提升交互语义清晰度
7. 状态指示器
设计 _statusDot 函数实现状态与颜色的映射,通过视觉元素强化状态识别:
Widget _statusDot(String status) {
Color c;
switch (status) {
case '待分派':
c = Colors.grey;
break;
case '执行中':
c = Colors.blue;
break;
case '待复核':
c = Colors.orange;
break;
default:
c = Colors.green;
}
return Container(
width: 10,
height: 10,
decoration: BoxDecoration(color: c, shape: BoxShape.circle),
);
}
- 颜色映射规则:按行业通用视觉规范设计,待分派(灰色)、执行中(蓝色)、待复核(橙色)、已完成(绿色)
- 视觉样式:
Container设置10px固定宽高,配合圆形形状,打造小巧醒目的状态圆点 - 扩展性:采用
switch语句实现状态匹配,后续新增状态时可快速扩展颜色配置
8. 列表项 UI
通过 ListView.separated 实现工单列表的展示,兼顾布局美观与交互体验:
ListView.separated(
itemCount: list.length,
separatorBuilder: (_, __) => const Divider(height: 1),
itemBuilder: (context, i) {
final w = list[i];
final due = DateFormat('MM-dd HH:mm').format(w.dueAt);
return ListTile(
leading: _statusDot(w.status),
title: Text(w.title),
subtitle: Text('${w.district} · 关联井盖 ${w.coverCode} · 截止 $due'),
trailing: Text(w.status),
onTap: () => Navigator.of(context).push(
MaterialPageRoute(builder: (_) => WorkOrderDetailPage(order: w)),
),
);
},
)
- 分隔线设计:
separatorBuilder添加1px高度的分割线,清晰区分列表项,提升页面整洁度 - 时间格式化:通过
DateFormat将截止时间格式化为“月-日 时:分”,符合移动端时间展示习惯 - 交互设计:
leading展示状态圆点,trailing显示状态文字,双重标识强化状态感知;onTap实现详情页跳转,传递当前工单对象 - 信息层级:标题展示工单名称,副标题聚合片区、井盖编号、截止时间,信息排布主次分明
9. 完整页面代码(核心片段)
工单列表页面的核心结构实现,整合上述所有功能模块:
class WorkOrdersPage extends StatefulWidget {
const WorkOrdersPage({super.key});
State<WorkOrdersPage> createState() => _WorkOrdersPageState();
}
class _WorkOrdersPageState extends State<WorkOrdersPage> {
late final List<WorkOrder> _all = buildMockWorkOrders();
String _status = '全部';
Widget build(BuildContext context) {
final statuses = <String>['全部', ...{for (final e in _all) e.status}];
final list = _all
.where((e) => _status == '全部' || e.status == _status)
.toList(growable: false);
return Scaffold(
appBar: AppBar(
title: const Text('工单列表'),
actions: [
PopupMenuButton<String>(
initialValue: _status,
onSelected: (v) => setState(() => _status = v),
itemBuilder: (context) => statuses
.map((e) => PopupMenuItem(value: e, child: Text('筛选: $e')))
.toList(growable: false),
),
],
),
- 页面结构:遵循Flutter经典的StatefulWidget设计模式,分离视图与状态管理
- 筛选逻辑嵌入:在
build方法中集成状态筛选逻辑,保证状态变化时实时更新列表 - 组件复用:直接复用已定义的
PopupMenuButton筛选组件,保证代码复用性
空状态处理
当筛选后无工单数据时,展示友好的空状态提示,提升用户体验:
Widget _buildEmptyState() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.assignment_outlined,
size: 64,
color: Colors.grey.shade400,
),
const SizedBox(height: 16),
Text(
'暂无工单',
style: TextStyle(
fontSize: 18,
color: Colors.grey.shade600,
fontWeight: FontWeight.w500,
),
),
- 视觉引导:使用64px的大号图标作为视觉核心,快速吸引用户注意力
- 文案层级:主文案“暂无工单”突出显示,副文案补充说明筛选条件,清晰告知用户空状态原因
- 布局设计:采用
Center+Column居中布局,保证空状态在页面中垂直水平居中,视觉协调
增强版工单列表项
设计卡片式工单列表项,提升信息展示丰富度与交互体验:
Widget _buildWorkOrderItem(WorkOrder workOrder) {
return Card(
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: InkWell(
onTap: () => onWorkOrderTap(workOrder),
onLongPress: () => onWorkOrderLongPress(workOrder),
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildWorkOrderHeader(workOrder),
const SizedBox(height: 12),
_buildWorkOrderContent(workOrder),
],
),
),
),
);
}
- 卡片布局:使用
Card组件打造立体视觉效果,8px左右边距、4px上下边距保证项间距合理 - 交互扩展:除点击跳转外,新增长按操作回调,为批量操作、更多功能提供扩展入口
- 内边距设计:16px内边距保证内容与卡片边缘有足够留白,避免文字紧贴边框
工单头部设计
工单头部整合状态、标题、优先级信息,强化核心信息展示:
Widget _buildWorkOrderHeader(WorkOrder workOrder) {
return Row(
children: [
CircleAvatar(
backgroundColor: _getStatusColor(workOrder.status).withOpacity(0.1),
child: Icon(
_getStatusIcon(workOrder.status),
color: _getStatusColor(workOrder.status),
size: 20,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
workOrder.title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
- 状态图标容器:
CircleAvatar作为状态图标载体,背景色采用状态色的低透明度版本,视觉统一 - 标题样式:16px加粗字体突出工单标题,作为头部核心信息
- 布局适配:
Expanded包裹标题区域,保证在不同屏幕宽度下标题可自适应展示
工单内容区域
内容区域展示工单描述、位置、井盖编号等关键业务信息:
Widget _buildWorkOrderContent(WorkOrder workOrder) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
workOrder.description,
style: TextStyle(
color: Colors.grey.shade700,
fontSize: 14,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 8),
Row(
children: [
Icon(
Icons.location_on,
size: 16,
color: Colors.grey.shade500,
),
const SizedBox(width: 4),
Text(
workOrder.district,
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 12,
),
),
- 描述文本处理:限制2行展示,超出部分省略,避免长文本占用过多页面空间
- 图标辅助:使用16px的位置、设置图标辅助识别信息类型,提升信息可读性
- 字体配色:采用灰色系区分次要信息,与标题、状态等核心信息形成视觉层级
工单底部区域
底部区域聚焦时间与附件信息,突出过期提醒:
Widget _buildWorkOrderFooter(WorkOrder workOrder) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(
Icons.schedule,
size: 16,
color: _isOverdue(workOrder) ? Colors.red : Colors.grey.shade500,
),
const SizedBox(width: 4),
Text(
_formatDate(workOrder.dueAt),
style: TextStyle(
color: _isOverdue(workOrder) ? Colors.red : Colors.grey.shade600,
fontSize: 12,
),
),
],
),
- 过期提醒:通过
_isOverdue方法判断工单是否过期,过期时图标与文字均改为红色,强化提醒效果 - 布局对齐:
MainAxisAlignment.spaceBetween实现时间与附件信息左右分布,布局均衡 - 时间格式化:自定义
_formatDate方法处理截止时间,提升时间展示的可读性
状态映射辅助方法
封装状态与颜色、图标、文本的映射方法,保证视觉一致性:
Color _getStatusColor(WorkOrderStatus status) {
switch (status) {
case WorkOrderStatus.pending:
return Colors.grey;
case WorkOrderStatus.assigned:
return Colors.blue;
case WorkOrderStatus.inProgress:
return Colors.orange;
case WorkOrderStatus.review:
return Colors.purple;
default:
return Colors.green;
}
}
IconData _getStatusIcon(WorkOrderStatus status) {
switch (status) {
case WorkOrderStatus.pending:
return Icons.pending;
case WorkOrderStatus.assigned:
return Icons.person_add;
case WorkOrderStatus.inProgress:
return Icons.play_arrow;
default:
return Icons.check_circle;
}
}
- 统一管理:将状态相关的视觉映射逻辑封装为独立方法,便于后续统一修改维护
- 扩展性:
switch语句预留扩展空间,新增工单状态时可快速添加对应配置 - 规范设计:图标与颜色选择贴合状态语义,如“执行中”使用播放箭头图标,符合用户认知习惯
优先级与时间辅助方法
补充优先级映射与时间处理方法,完善工单信息展示:
Color _getPriorityColor(WorkOrderPriority priority) {
switch (priority) {
case WorkOrderPriority.low:
return Colors.green;
case WorkOrderPriority.medium:
return Colors.orange;
case WorkOrderPriority.high:
return Colors.red;
default:
return Colors.purple;
}
}
bool _isOverdue(WorkOrder workOrder) {
return DateTime.now().isAfter(workOrder.dueAt) &&
workOrder.status != WorkOrderStatus.completed;
}
- 优先级配色:按行业通用标准设计,低优先级(绿色)、中优先级(橙色)、高优先级(红色)、紧急(紫色)
- 过期判断逻辑:仅判断未完成且当前时间晚于截止时间的工单,避免已完成工单误标为过期
- 复用性:独立封装的方法可在列表项、详情页等多处复用,保证逻辑一致性
13. 小结
工单列表的实现展现了 Flutter 开发的几个重要原则:
状态管理:使用 Provider 管理复杂列表状态和筛选
用户体验:下拉刷新、空状态处理、长按操作
视觉设计:颜色编码、图标使用、卡片布局
组件化:可复用的列表组件和工单项目
数据处理:多条件筛选、状态映射、时间格式化
这样的设计不仅满足了工单管理的基本需求,还为后续的功能扩展(如批量操作、高级搜索、数据导出等)提供了良好的基础架构。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐
所有评论(0)