在这里插入图片描述

本文聚焦于开源鸿蒙(OpenHarmony)平台下,基于Flutter框架开发的城市井盖地图APP中“风险图层”功能的实战实现,核心围绕风险点数据模型设计、风险图层状态管理、高级组件开发等核心环节展开,完整还原从数据建模到交互实现的全流程。

一、风险数据模型设计:结构化存储风险信息

风险评估的核心是数据的结构化表达,我们首先定义RiskFactor数据模型,用于精准存储每一项风险因素的详细评估信息:

class RiskFactor {
  final String id;
  final String name;
  final String description;
  final double weight;
  final double value;
  final RiskFactorType type;
  final DateTime assessedAt;
  final String? assessedBy;

  const RiskFactor({
    required this.id,
    required this.name,
    required this.description,
    required this.weight,
    required this.value,
    required this.type,
    required this.assessedAt,
    this.assessedBy,
  });
}
模型设计要点:
  1. 核心字段全覆盖:包含id(唯一标识)、name(风险名称)、description(描述)等基础信息;
  2. 量化评估字段:weight(权重)和value(评分)支持风险值的量化计算;
  3. 溯源字段:assessedAt(评估时间)、assessedBy(评估人)满足溯源管理需求;
  4. 类型枚举关联:通过RiskFactorType枚举限定风险类型,避免数据混乱。

为了实现风险数据的分类管理,我们定义三类枚举,分别对应风险等级、图层类型、风险因素类型:

enum RiskLevel {
  low,
  medium,
  high,
  critical,
}

enum RiskLayerType {
  heat,
  density,
  cluster,
  prediction,
}

enum RiskFactorType {
  structural,
  environmental,
  operational,
  maintenance,
}
枚举设计价值:
  1. RiskLevel:将风险划分为低/中/高/紧急四档,适配巡检优先级管理;
  2. RiskLayerType:定义热力图/密度图/聚类图/预测图四种图层类型,满足不同可视化需求;
  3. RiskFactorType:按结构/环境/运营/维护维度分类风险因素,便于针对性分析。

二、风险图层状态管理:基于Provider实现数据驱动

状态管理是交互功能的核心,我们使用Provider + ChangeNotifier实现风险图层数据的统一管理,先定义核心状态类:

class RiskLayerProvider extends ChangeNotifier {
  List<RiskLayer> _layers = [];
  List<RiskPoint> _allRiskPoints = [];
  List<RiskPoint> _filteredRiskPoints = [];
  Set<RiskLevel> _selectedRiskLevels = {RiskLevel.low, RiskLevel.medium, RiskLevel.high, RiskLevel.critical};
  bool _loading = false;
  String? _error;
  DateTime? _lastRefreshTime;

  List<RiskLayer> get layers => _layers;
  List<RiskPoint> get allRiskPoints => _allRiskPoints;
  List<RiskPoint> get filteredRiskPoints => _filteredRiskPoints;
}
状态设计思路:
  1. 分层存储数据:_layers(图层列表)、_allRiskPoints(全量风险点)、_filteredRiskPoints(筛选后风险点)分离,降低耦合;
  2. 筛选条件存储:_selectedRiskLevels用Set存储选中的风险等级,支持多条件筛选;
  3. 状态标识字段:_loading(加载状态)、_error(错误信息)、_lastRefreshTime(刷新时间)提升交互体验。

核心的loadRiskLayers方法实现数据加载逻辑,包含加载状态管理和异常捕获:

  Future<void> loadRiskLayers() async {
    _loading = true;
    _error = null;
    notifyListeners();

    try {
      await Future.delayed(const Duration(seconds: 1));
      
      _layers = _generateMockLayers();
      _allRiskPoints = _generateMockRiskPoints();
      _applyRiskLevelFilter();
      _lastRefreshTime = DateTime.now();
      
      _loading = false;
      notifyListeners();
    } catch (e) {
      _error = e.toString();
      _loading = false;
      notifyListeners();
    }
  }
加载逻辑要点:
  1. 状态前置更新:加载前先置_loading = true并通知刷新,保证UI及时响应;
  2. 模拟异步加载:通过Future.delayed模拟网络请求,适配真实场景;
  3. 数据生成与筛选:调用模拟数据方法后,立即执行筛选逻辑,保证数据一致性;
  4. 异常捕获:捕获加载过程中的错误,通过_error字段反馈给UI。

实现风险等级的切换与全选功能,支撑筛选交互:

  void toggleRiskLevel(RiskLevel level) {
    if (_selectedRiskLevels.contains(level)) {
      _selectedRiskLevels.remove(level);
    } else {
      _selectedRiskLevels.add(level);
    }
    _applyRiskLevelFilter();
    notifyListeners();
  }

  void setAllRiskLevels(bool selected) {
    if (selected) {
      _selectedRiskLevels = Set.from(RiskLevel.values);
    } else {
      _selectedRiskLevels.clear();
    }
    _applyRiskLevelFilter();
    notifyListeners();
  }
交互逻辑设计:
  1. toggleRiskLevel:单个风险等级的选中/取消,操作后立即执行筛选并刷新UI;
  2. setAllRiskLevels:一键全选/全取消,简化批量操作流程;
  3. 筛选逻辑复用:所有状态变更后都调用_applyRiskLevelFilter,保证筛选逻辑唯一入口。

筛选核心方法_applyRiskLevelFilter,实现风险点的精准过滤:

  void _applyRiskLevelFilter() {
    _filteredRiskPoints = _allRiskPoints.where((point) {
      return _selectedRiskLevels.contains(point.riskLevel);
    }).toList();
  }
筛选逻辑说明:
  1. 基于where方法遍历全量风险点,匹配选中的风险等级;
  2. 结果转为列表存储到_filteredRiskPoints,供UI层读取;
  3. 纯数据逻辑,与UI解耦,便于后续扩展多维度筛选。

模拟图层数据生成方法,为功能测试提供基础数据:

  List<RiskLayer> _generateMockLayers() {
    return [
      RiskLayer(
        id: 'layer_heat',
        name: '风险热力图',
        description: '基于风险值的热力分布图层',
        riskLevel: RiskLevel.medium,
        riskScore: 65.0,
        riskPoints: [],
        layerType: RiskLayerType.heat,
        isVisible: true,
        opacity: 0.7,
        overlayColor: Colors.red.withOpacity(0.5),
        createdAt: DateTime.now(),
      ),
    ];
  }
模拟数据设计:
  1. 包含图层核心属性:ID、名称、描述、风险等级、可视化参数(透明度、颜色)等;
  2. 预设默认状态:如isVisible: true默认显示热力图,贴近实际使用场景;
  3. 简化数据量:单图层示例降低代码冗余,实际可扩展多图层配置。

风险点模拟生成方法,批量生成带随机属性的风险点数据:

  List<RiskPoint> _generateMockRiskPoints() {
    final districts = ['东城区', '西城区', '南城区', '北城区', '高新区'];
    final rng = Random(42);
    
    return List.generate(30, (index) {
      final riskValue = rng.nextDouble();
      final riskLevel = _getRiskLevel(riskValue);
      
      return RiskPoint(
        id: 'RISK_POINT_${index.toString().padLeft(3, '0')}',
        name: '风险点${index + 1}',
        latitude: 39.9042 + (rng.nextDouble() - 0.5) * 0.1,
        longitude: 116.4074 + (rng.nextDouble() - 0.5) * 0.1,
        riskValue: riskValue,
        riskLevel: riskLevel,
      );
    });
  }
风险点生成要点:
  1. 区域随机分配:基于预设片区列表,均匀分配风险点所属区域;
  2. 坐标随机偏移:在固定经纬度基础上小幅偏移,模拟真实地理位置;
  3. 风险值与等级关联:通过_getRiskLevel将随机风险值映射为等级,保证数据逻辑一致;
  4. 固定随机种子:Random(42)保证每次生成的数据一致,便于测试。

风险因素模拟生成方法,为每个风险点补充详细评估维度:

  List<RiskFactor> _generateMockRiskFactors(Random rng) {
    return [
      RiskFactor(
        id: 'factor_1',
        name: '结构完整性',
        description: '井盖结构完整性评估',
        weight: 0.4,
        value: 0.3 + rng.nextDouble() * 0.4,
        type: RiskFactorType.structural,
        assessedAt: DateTime.now(),
        assessedBy: '技术员${rng.nextInt(5) + 1}',
      ),
    ];
  }
风险因素设计:
  1. 权重分配合理:结构完整性权重0.4,体现核心风险维度的重要性;
  2. 评分区间可控:0.3 + rng.nextDouble() * 0.4限定评分在0.3~0.7之间,符合中风险区间特征;
  3. 评估人随机分配:模拟不同技术员的评估结果,贴近实际巡检场景。

风险等级映射方法,实现数值到等级的标准化转换:

  RiskLevel _getRiskLevel(double riskValue) {
    if (riskValue >= 0.8) return RiskLevel.critical;
    if (riskValue >= 0.6) return RiskLevel.high;
    if (riskValue >= 0.3) return RiskLevel.medium;
    return RiskLevel.low;
  }
等级映射规则:
  1. 阈值分层清晰:0.8(紧急)、0.6(高)、0.3(中)、<0.3(低),适配巡检优先级;
  2. 顺序判断逻辑:从高阈值到低阈值判断,避免逻辑漏洞;
  3. 规则统一管理:单独封装方法,后续阈值调整只需修改此处。

三、高级风险图层组件:可视化与交互一体化

创建高级风险图层组件,整合状态管理与UI展示:

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

  
  State<AdvancedRiskLayerWidget> createState() => _AdvancedRiskLayerWidgetState();
}

class _AdvancedRiskLayerWidgetState extends State<AdvancedRiskLayerWidget> {
  
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      final provider = Provider.of<RiskLayerProvider>(context, listen: false);
      provider.loadRiskLayers();
    });
  }
}
组件初始化要点:
  1. 延迟加载数据:通过addPostFrameCallback保证Widget渲染完成后再加载数据,避免上下文异常;
  2. 非监听模式获取Provider:listen: false避免不必要的重建,提升性能;
  3. 初始化触发加载:组件创建时自动加载数据,保证首次进入有数据展示。

组件核心构建方法,实现加载状态、空数据、正常数据的UI适配:

  
  Widget build(BuildContext context) {
    return Consumer<RiskLayerProvider>(
      builder: (context, provider, child) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('风险图层'),
            actions: [
              IconButton(
                onPressed: () => _showLayerDialog(context, provider),
                icon: const Icon(Icons.layers),
              ),
              IconButton(
                onPressed: provider.refreshData,
                icon: const Icon(Icons.refresh),
              ),
            ],
          ),
          body: provider.loading
              ? const Center(child: CircularProgressIndicator())
              : provider.filteredRiskPoints.isEmpty
                  ? const Center(child: Text('暂无风险数据'))
                  : _buildContent(context, provider),
        );
      },
    );
  }
UI构建逻辑:
  1. Consumer监听状态:实时响应Provider的状态变更,自动重建UI;
  2. 加载状态展示:CircularProgressIndicator提示用户数据加载中;
  3. 空数据兜底:无筛选结果时显示友好提示,提升用户体验;
  4. 功能型AppBar:包含图层管理和数据刷新按钮,操作入口清晰。

内容区域构建方法,整合概览、筛选、列表等核心模块:

  Widget _buildContent(BuildContext context, RiskLayerProvider provider) {
    return RefreshIndicator(
      onRefresh: provider.refreshData,
      child: Column(
        children: [
          _buildSummaryCard(context, provider),
          const SizedBox(height: 8),
          _buildRiskLevelFilter(context, provider),
          const SizedBox(height: 8),
          _buildLayerToggle(context, provider),
          const SizedBox(height: 8),
          Expanded(
            child: _buildRiskPointList(context, provider),
          ),
        ],
      ),
    );
  }
布局设计要点:
  1. 下拉刷新支持:RefreshIndicator包裹内容,实现数据手动刷新;
  2. 垂直布局分层:概览卡片→筛选栏→图层切换→风险点列表,符合用户视觉流;
  3. 间距合理:SizedBox(height: 8)分隔模块,提升UI层次感;
  4. 列表占满剩余空间:Expanded保证列表自适应屏幕高度。

风险概览卡片组件,可视化展示核心统计指标:

  Widget _buildSummaryCard(BuildContext context, RiskLayerProvider provider) {
    final totalPoints = provider.allRiskPoints.length;
    final filteredPoints = provider.filteredRiskPoints.length;
    final highRiskPoints = provider.filteredRiskPoints.where((p) => p.riskLevel == RiskLevel.high || p.riskLevel == RiskLevel.critical).length;
    
    return Card(
      margin: const EdgeInsets.all(16),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            Text('风险概览', style: Theme.of(context).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold)),
            const SizedBox(height: 12),
          ],
        ),
      ),
    );
  }
概览卡片设计:
  1. 核心指标提取:总风险点、筛选结果、高风险数量,直击业务核心;
  2. 视觉样式优化:卡片包裹+内边距,提升质感;
  3. 标题样式强化:加粗+大号字体,突出模块标题;
  4. 数据实时计算:基于Provider数据动态计算,保证数据准确性。

四、实战场景:按风险等级筛选井盖(核心业务功能)

1. 这个功能解决什么问题

巡检现场需要“按风险等级”快速筛选井盖,核心痛点与解决方案:

  • 高风险(>=70%):优先处理,保障公共安全;
  • 中风险(30%~70%):关注跟进,制定巡检计划;
  • 低风险(<30%):定期巡检,降低人力成本;
  • 动态切换:随时调整筛选条件,适配现场巡检需求;
  • 列表展示:关键信息与风险标签,快速定位问题井盖。

该页面是典型的“多选过滤”场景,是FilterChip + where组合的最佳实践示例。

2. 相关文件一览
  • lib/feature_pages.dartRiskLayerPage):核心页面逻辑,包含筛选与列表展示;
  • lib/mock_data.dartbuildMockCovers):模拟风险点数据,支撑功能测试。
3. 风险等级状态管理

RiskLayerPage通过三个布尔值管理风险等级的选中状态,简化状态逻辑:

class _RiskLayerPageState extends State<RiskLayerPage> {
  bool high = true;
  bool mid = true;
  bool low = true;
}
状态设计说明:
  1. 默认全选:初始状态high/mid/low均为true,展示所有风险点;
  2. 单状态单职责:每个布尔值对应一个风险档位,逻辑清晰,便于维护;
  3. 局部状态管理:仅在页面内生效,无需全局状态,降低复杂度。
4. 过滤逻辑实现

核心过滤逻辑基于where方法实现,状态变更时自动重新计算:

final all = buildMockCovers();
final list = all.where((e) {
  if (e.risk >= 0.7) return high;
  if (e.risk >= 0.3) return mid;
  return low;
}).toList(growable: false);
过滤逻辑解析:
  1. 先获取全量数据:buildMockCovers()生成模拟风险点列表;
  2. 条件判断顺序:从高阈值到低阈值,避免逻辑覆盖;
  3. 状态关联筛选:通过布尔值控制对应风险等级的显示/隐藏;
  4. 不可变列表:growable: false创建不可变列表,提升性能。
5. FilterChip 交互实现

页面顶部通过Wrap + FilterChip实现风险等级的多选交互:

Padding(
  padding: const EdgeInsets.all(12),
  child: Wrap(
    spacing: 8,
    children: [
      FilterChip(
        selected: high,
        onSelected: (v) => setState(() => high = v),
        label: const Text('高风险(>=70%)'),
      ),
      FilterChip(
        selected: mid,
        onSelected: (v) => setState(() => mid = v),
        label: const Text('中风险(30~70%)'),
      ),
    ],
  ),
)
交互设计要点:
  1. Wrap布局:空间不足时自动换行,适配不同屏幕尺寸;
  2. 选中状态关联:selected绑定对应布尔值,视觉反馈选中状态;
  3. 状态更新:onSelected通过setState更新状态,触发UI重建;
  4. 标签清晰:标注风险阈值,用户可明确知晓筛选范围。
6. 列表项 UI 设计

通过ListView.separated展示筛选后的风险点列表,优化视觉体验:

ListView.separated(
  itemCount: list.length,
  separatorBuilder: (_, __) => const Divider(height: 1),
  itemBuilder: (context, i) {
    final c = list[i];
    return ListTile(
      leading: Icon(Icons.place_rounded, color: _riskColor(c.risk)),
      title: Text(c.code),
      subtitle: Text('${c.district} · ${c.address}'),
      trailing: Text(_riskLabel(c.risk)),
    );
  },
)
列表设计要点:
  1. 分割线优化:separatorBuilder添加分割线,区分列表项;
  2. 图标颜色映射:leading图标颜色随风险等级变化,视觉直观;
  3. 核心信息展示:标题(编号)、副标题(区域+地址)、尾部(风险标签);
  4. 数据绑定:直接使用筛选后的列表数据,保证展示与筛选结果一致。
7. 风险颜色映射

统一维护风险值与颜色的映射关系,保证视觉一致性:

Color _riskColor(double risk) {
  if (risk >= 0.7) return Colors.red;
  if (risk >= 0.3) return Colors.orange;
  return Colors.green;
}
颜色映射规则:
  1. 高风险(红):警示性强,突出紧急处理需求;
  2. 中风险(橙):提醒关注,介于警示与常规之间;
  3. 低风险(绿):常规状态,无视觉警示;
  4. 单点维护:所有UI的风险颜色均调用此方法,便于统一调整。
8. 风险标签映射

将数值型风险值转换为文字标签,提升可读性:

String _riskLabel(double risk) {
  if (risk >= 0.7) return '高风险';
  if (risk >= 0.3) return '中风险';
  return '低风险';
}
标签设计说明:
  1. 与颜色映射阈值一致:保证文字与颜色标签的逻辑统一;
  2. 简洁易懂:使用用户易理解的中文标签,降低认知成本;
  3. 复用性强:列表、详情页等场景均可调用,保证表述一致。
9. 小结

“风险图层”页面实现了:

  • 风险等级多选过滤(FilterChip):直观的筛选交互,适配现场巡检操作习惯;
  • 动态列表过滤(where + setState):状态驱动的筛选逻辑,保证数据实时性;
  • 风险颜色与标签统一映射:视觉与文字标签的一致性,提升用户体验;
  • 分割线与图标视觉优化:细节设计提升UI质感,降低信息认知成本。

后续如果要接入真实地图 SDK,只需把列表替换成地图图层,过滤逻辑完全不需要改动。


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

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐