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

Flutter 三方库 undo 的鸿蒙化实战 - 命令模式支持下的状态撤销与重做架构

前言

在开发 OpenHarmony 上的富交互应用(如绘图工具、表单编辑器或低代码平台)时,为用户提供极其顺滑的“撤销 (Undo)”与“重做 (Redo)”功能是提升产品“专业感”的关键。

如果单纯依靠手动备份状态,不仅代码逻辑会变得异常混乱,内存管理也将失控。undo 库基于经典的“命令模式(Command Pattern)”设计,提供了一套极其轻量、健壮且无依赖的状态回溯引擎方案。

一、原理分析 / 概念介绍

1.1 基础原理

undo 的核心思想是将每一个“修改操作”封装为一个 Change 对象。这个对象包含了三个核心要素:

  • 旧值 (Old Value):修改前的快照。
  • 正向执行逻辑 (Execute):如何将旧值变为新值。
  • 反向执行逻辑 (Undo):如何从新值变回旧值。

所有的 Change 对象被有序存储在 ChangeStack 中,通过移动内部指针来实现撤销与重做。

执行 undo()

执行 redo()

用户操作 (Action)

打包成 Change 对象

推入 ChangeStack 栈顶

指向前一个 Change 且执行反向逻辑

指向后一个 Change 且执行正向逻辑

1.2 核心业务优势

  1. 增量内存开销:只存储变化的补丁(Delta),而不是像全量状态镜像(State Snapshots)那样占用庞大内存。
  2. 命令模式解耦:将 UI 操作逻辑与数据变更记忆逻辑彻底分离,使业务代码更易于维护。
  3. 支持批处理 (Group):可以将多个细微的操作(如拖拽过程中的每一次位移)打包成一个原子组,用户只需点击一次撤销即可回退整个动作。

二、鸿蒙基础指导

2.1 适配情况

  1. 是否原生支持?:100% 支持。该库为纯 Dart 实现,无平台限制。
  2. 稳定性表现:在鸿蒙多窗口、折叠屏等复杂 UI 重建环境下,通过将 ChangeStack 挂载在持久层,可确保操作历史不因重建而丢失。

2.2 适配代码引入

在项目的 pubspec.yaml 中增加依赖:

dependencies:
  undo: ^2.0.0

三、核心 API / 组件详解

3.1 核心控制器接口

组件 功能说明 核心方法示例
ChangeStack 历史记录栈:存储所有变更并提供撤销/重做入口。 final stack = ChangeStack();
Change 变更单体:定义单个原子操作。 Change(old, execute, undo)
stack.undo() 回退一步 if(stack.canUndo) stack.undo();
stack.redo() 重前进一步 if(stack.canRedo) stack.redo();
stack.addGroup() 开启事务组 将多步合并为一步撤销。

3.2 基础使用示例

import 'package:undo/undo.dart';

class EditorManager {
  final stack = ChangeStack();
  String content = "";

  void updateContent(String newContent) {
    // 将变更逻辑交给 Stack 管理
    stack.add(Change(
      content, // 记录旧值
      () => content = newContent, // 正向:赋值新内容
      (old) => content = old as String, // 反向:还原旧内容
    ));
  }
}

四、典型应用场景

4.1 画布与图形编辑

在 OpenHarmony 平板绘图应用中,每一次图形平移或缩放都作为一个 Change 推入栈。通过 addGroup 功能,可以实现“从按下到松开”这一系列坐标变化只作为一次撤销单元。

4.2 复杂表单修改与保护

在大型业务表单中,当用户误删或误填了多项内容,提供全局撤销按钮,配合 undo 库的 canUndo 属性动态显示操作提示。

五、OpenHarmony 平台适配挑战

5.1 复杂对象的深拷贝问题

由于 undo 依赖 oldValue 进行回退。
强烈建议:对于复杂的数据对象(如 Map 或自定义 Model),在推入 Change 之前请务必确保存储的是“不可变快照”或者执行了“深度克隆”,否则引用类型的改变会导致撤销逻辑失效。

六、综合实战演示

如下构建 UndoLabPage.dart,展示一个具有撤销能力的计数器与列表:

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

class UndoLabPage extends StatefulWidget {
  const UndoLabPage({Key? key}) : super(key: key);

  
  State<UndoLabPage> createState() => _UndoLabPageState();
}

class _UndoLabPageState extends State<UndoLabPage> {
  final _stack = ChangeStack();
  int _counter = 0;

  void _increment() {
    _stack.add(Change(
      _counter,
      () => setState(() => _counter++),
      (v) => setState(() => _counter = v as int),
    ));
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('增强型撤销/重做控制器'),
        actions: [
          IconButton(onPressed: _stack.canUndo ? () => _stack.undo() : null, icon: const Icon(Icons.undo)),
          IconButton(onPressed: _stack.canRedo ? () => _stack.redo() : null, icon: const Icon(Icons.redo)),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('当前计数(支持撤销记录):'),
            Text('$_counter', style: const TextStyle(fontSize: 48, fontWeight: FontWeight.bold)),
            const SizedBox(height: 20),
            ElevatedButton(onPressed: _increment, child: const Text("增加 +1")),
          ],
        ),
      ),
    );
  }
}

七、总结

undo 库不仅是一套简单的历史记录工具,它背后代表的“命令模式”是构建高性能交互应用的架构基础。通过在 OpenHarmony 开发中深度结合这套方案,开发者可以低成本地为复杂业务流程提供完美的容错能力与时光倒流体验。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐