Flutter for OpenHarmony 实战:built_collection 全链路不可变集合模型

在这里插入图片描述

前言

在真实的业务场景下,我们更多地是在处理列表(List)、集合(Set)和映射(Map)。普通的 Dart 集合是可变的,这意味着当你在多个服务或页面间传递一个 List 时,任何一个地方的修改都可能引发难以排查的侧漏异常(Side Effects)。

built_collection 专门为解决这类问题而生。它将“不可变性”延伸到了集合层面,为 HarmonyOS NEXT 上的健壮软件架构提供了最后一块拼图。


一、 为什么在鸿蒙开发中推崇不可变集合?

1.1 彻底消灭状态错乱

在常规开发中, Service 内部的一个 sort() 可能会直接修改 UI 层引用的内存对象。built_collection 确认集合对象一旦创建即不可修改,任何变更操作都会明确地产生一个新指针。

1.2 物理级支持“深度相等”

BuiltList 实现的是全成员递归比对。这在 HarmonyOS NEXT 的高频刷新 UI 中极具价值:如果内容一致,即便实例不同,UI 框架也可判定无须重绘。


二、 技术内幕:BuiltCollection 的核心机制

2.1 影子变异模式

当你调用 rebuild 时,它会临时创建一个可变的 Builder。在这个封闭的瞬时环境里进行集合操作,完成后立刻“冻结”并输出新的不可变实例。


三、 集成指南

3.1 添加依赖

dependencies:
  built_collection: ^5.1.1

四、 核心关键技术分解

4.1 基础转换与重建 (rebuild)

这是最基础的操作,通过 rebuild 实现“写时拷贝”。

📂 示例文件:lib/built-collection/built_basic_4_1.dart

// 💡 快速转换:[].toBuiltList()
final builtInts = [1, 2, 3].toBuiltList();

// 💡 核心:修改必须通过 rebuild
// 提供 Builder (b),修改后生成全新实例
final updatedList = builtInts.rebuild((b) => b
  ..add(4)
  ..remove(1));

在这里插入图片描述

4.2 深度嵌套集合处理

处理 Map 嵌套 List 的场景,提供链式更新能力。

📂 示例文件:lib/built-collection/built_nested_4_2.dart

final stats = BuiltMap<String, BuiltList<int>>({
  'CPU': BuiltList([80, 85]),
});

// 使用 updateValue 定位并更新嵌套集合
final newStats = stats.rebuild((b) => b
  ..updateValue('CPU', (list) => list.rebuild((l) => l.add(90)))
);

在这里插入图片描述


五、 完整示例:鸿蒙不可变集合实验室

这是一个集成度最高的工程化示例,模拟了“系统监控日志流水”。它展示了如何通过 BuiltList 配合 BuiltMap 构建一个高性能、可预测的数据流。

import 'package:flutter/material.dart';
import 'package:built_collection/built_collection.dart';

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

  
  State<BuiltCollectionDemo> createState() => _BuiltCollectionDemoState();
}

class _BuiltCollectionDemoState extends State<BuiltCollectionDemo> {
  // 💡 1. 定义初始不可变列表 (BuiltList)
  // 在大型鸿蒙项目中,这种不可变性确保了数据在被多个 Service 引用时不会被意外篡改。
  BuiltList<String> _logs = BuiltList<String>([
    '系统启动成功 [HarmonyOS NEXT]',
    '正在检测网络状态...',
    '初始化不可变集合实验室...',
  ]);

  // 💡 2. 模拟嵌套复杂数据结构 (BuiltMap)
  BuiltMap<String, BuiltList<int>> _stats = BuiltMap<String, BuiltList<int>>({
    'CPU负载': BuiltList<int>([12, 15, 18]),
    '内存使用': BuiltList<int>([256, 260]),
  });

  void _addLog() {
    setState(() {
      // 💡 核心操作:rebuild。
      // 它不会修改旧列表,而是通过 Builder 生成一个新的 BuiltList。
      // 这种“写时拷贝(Copy-on-write)”模式是处理复杂状态流的最佳实践。
      _logs = _logs.rebuild((b) => b
        ..insert(0,
            '👉 新任务探测: ${DateTime.now().hour}:${DateTime.now().minute}:${DateTime.now().second}')
        ..take(50)); // 保持最近 50 条

      // 💡 演示嵌套修改
      _stats = _stats.rebuild((b) => b
        ..updateValue(
            'CPU负载',
            (list) =>
                list.rebuild((l) => l.add(20 + (DateTime.now().second % 10)))));
    });
  }

  void _clearLogs() {
    setState(() {
      _logs = _logs.rebuild((b) => b.clear());
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('不可变集合实验室'),
        backgroundColor: const Color(0xFF007DFF), // 鸿蒙品牌蓝
        foregroundColor: Colors.white,
        actions: [
          IconButton(
            icon: const Icon(Icons.delete_sweep),
            onPressed: _clearLogs,
            tooltip: '清空日志',
          )
        ],
      ),
      body: Column(
        children: [
          _buildInfoCard(),
          const Padding(
            padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
            child: Row(
              children: [
                Icon(Icons.terminal, size: 18, color: Colors.grey),
                SizedBox(width: 8),
                Text('系统运行流水 (BuiltList)',
                    style: TextStyle(
                        color: Colors.grey, fontWeight: FontWeight.bold)),
              ],
            ),
          ),
          Expanded(
            child: _logs.isEmpty
                ? _buildEmptyState()
                : ListView.builder(
                    padding: const EdgeInsets.symmetric(horizontal: 12),
                    itemCount: _logs.length,
                    itemBuilder: (context, index) {
                      return Card(
                        elevation: 0,
                        color: Colors.grey[100],
                        margin: const EdgeInsets.only(bottom: 8),
                        child: ListTile(
                          dense: true,
                          leading: const Icon(Icons.arrow_right,
                              color: Color(0xFF007DFF)),
                          title: Text(_logs[index],
                              style: const TextStyle(fontFamily: 'monospace')),
                        ),
                      );
                    },
                  ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _addLog,
        backgroundColor: const Color(0xFF007DFF),
        label: const Text('生成新快照', style: TextStyle(color: Colors.white)),
        icon: const Icon(Icons.add_a_photo, color: Colors.white),
      ),
    );
  }

  Widget _buildInfoCard() {
    final cpuList = _stats['CPU负载']!;
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(16),
      margin: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: const Color(0xFF007DFF).withOpacity(0.05),
        borderRadius: BorderRadius.circular(12),
        border: Border.all(color: const Color(0xFF007DFF).withOpacity(0.2)),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text('嵌套数据监控 (BuiltMap)',
              style: TextStyle(fontWeight: FontWeight.bold)),
          const SizedBox(height: 12),
          Text('最近 CPU 采集周期: ${cpuList.join(", ")} %'),
          const SizedBox(height: 4),
          const Text('💡 提示: 每次修改都会产生全量新对象引用,确保在 Isolate 间安全传递。',
              style: TextStyle(fontSize: 12, color: Colors.grey)),
        ],
      ),
    );
  }

  Widget _buildEmptyState() {
    return Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(Icons.inbox, size: 64, color: Colors.grey[300]),
          const SizedBox(height: 16),
          Text('暂无流水快照', style: TextStyle(color: Colors.grey[400])),
        ],
      ),
    );
  }
}

在这里插入图片描述


六、 适配鸿蒙的避坑指南

6.1 性能权衡:避免循环 rebuild

虽然安全,但频繁调用 rebuild 会产生大量短命对象。在处理海量日志时,建议先在可变 List 中预处理。

6.2 类型隔离

BuiltList 并不是由 List 派生的。如果插件或原生层需要普通 List,必须通过 .toList() 主动转换。


七、 总结

built_collection 是对 Dart 集合功能的强有力补充。在 HarmonyOS NEXT 这个追求“全场景协作”的体系中,不可变数据是构建复杂、高频率交互系统的核心法则。


🌐 欢迎加入开源鸿蒙跨平台社区开源鸿蒙跨平台开发者社区

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐