搜索插件、代码仓与 APP 分类

插件详情v5.0.0

diffutil_dart

将两个列表之间的差异计算为一系列编辑操作。例如,用于在无需维护 StatefulWidget 的情况下隐式动画化 Flutter 列表。

AndroidiOSWebmacOSWindowsLinux
近 30 天下载
21.9万
喜欢数
104
Pub 分数
160

安装插件

flutter pub add diffutil_dart

插件介绍

基于插件公开信息、社区数据与项目使用情况整理的选型参考。

diffutil_dart 计算两个列表之间的差异,并生成一系列编辑操作(插入、删除、移动),以将一个列表转换为另一个列表。这使得在 Flutter 列表中实现高效、隐式的动画成为可能,而无需手动管理 StatefulWidget 小部件中的状态。它特别适用于使用动态数据更新 UI 时,以最少的重建次数提升性能和响应速度。该包轻量且兼容 Flutter 支持的所有主要平台。

适合场景

  • 在 Flutter 中动画化列表更新
  • 高效同步列表更改
  • 减少不必要的小部件重建
  • 实现平滑滚动过渡
  • 更新动态数据源
  • 简化列表的状态管理

主要特点

  • 生成精确的编辑操作
  • 支持插入、删除和移动操作
  • 最小化重建以提升性能
  • 跨平台兼容性
  • 轻量且易于集成
  • 无需手动状态管理

适合对象

  • 使用 Flutter 的动态列表的开发者
  • 注重流畅UI动画的团队
  • 频繁更新列表的应用
  • 那些希望减少列表处理样板代码的人
  • 追求高性能列表渲染的项目

使用建议

  • 需要理解编辑操作逻辑
  • 最适合简单的列表结构
  • 可能不会优化复杂的嵌套数据
  • 不适合非列表数据类型
  • 仅限列表到列表的差异比较
  • 默认情况下不处理深度相等

原始 README

以下为关联源码仓库的 README 原文快照,最新内容请访问源码仓库。

diffutil.dart

Pub Package Build Status codecov

Calculate the difference between two lists.

Heavily inspired bei Android's DiffUtil class, the code was adopted for Dart.

Uses Myers algorithm internally. For an explanation of how the algorithm works internally, for details I recommend this great series of articles: https://blog.jcoglan.com/2017/02/12/the-myers-diff-algorithm-part-1/

What is this good for?

There are often situations, where an app displays a list of items, which are fetched from an external data source like a server endpoint or a database, and updates are not sent as delta, but as a whole new list.

It can be useful to take the old list and the new list and calculate the difference between those two, for example when animating the insertion and removal of new items in the displayed list (See diffutil_sliverlist).

This package does exactly that: It takes two lists and calculates the difference (or to be more accurate: edit script) between those two lists as list of Insert, Remove, Change (only when using custom delegates) and Move (when enabled) operations.

Usage

Calculating diffs:

Simple usage:

final diffResult = calculateListDiff<int>([1, 2 ,3], [1, 3, 4]);

Custom equality:

final diffResult = calculateListDiff<YourClassWithId>(oldList, newList, (o1, o2) => o1.id == o2.id);

If you don't want to use plain old Dart lists (for example if you're using built_value or kt.dart), and don't want to convert your custom list to standard lists, you can use the calculateDiff function and implement your own DiffDelegate easily.

Or use calculateCustomListDiff and CustomListDiffDelegate.

Move detection is disabled by default.

Using the result:

Call .getUpdates() on the diffResult to get a List of DiffUpdate objects. These are sealed classes of type Insert, Remove, Change or Move. Move operations are only calculated if calculateListDiff was called with detectMoves: true

The order of these operations matters! They describe how to turn the previous list into the new list, when applied in the order as they are returned.

  for (final update in diffResult.getUpdates())
    update.when(
      insert: (pos, count) => print("inserted $count on $pos"),
      remove: (pos, count) => print("removed $count on $pos"),
      change: (pos, payload) => print("changed on $pos with payload $payload"),
      move: (from, to) => print("move $from to $to"),
    );

By default, Insert and Remove Operations are batched. (e.g. multiple consecutive inserts or removes are represented by a single Insert/Remove object with a count field > 1). If you want to turn off edit script batching, call getUpdates(batch: false). This means, every Insert and Remove operation will have a count of 1 and the edit script of [] and [1, 2] will be

[Insert(position: 0, count : 1), Insert(position: 0, count :1 )]

instead of

[Insert(position: 0, count: 2)]

Updates with data

If you need the concrete items that have been inserted/removed/changed/moved, call getUpdatesWithData().


     diffutil.calculateListDiff([1, 2, 3], [1, 0, 3]).getUpdatesWithData();

returns

      [
        DataRemove(position: 1, data: 2),
        DataInsert(position: 1, data: 0)
      ];

The result of getUpdatesWithData() cannot be batched. You can use the resulting Iterable<DataDiffUpdate> like this:

 for (final update in updates) {
      update.when(
        insert: (pos, data) => print('insert $pos $data'),
        remove: (pos, data) => print('remove $pos $data'),
        change: (pos, oldData, newData) => print('change on $pos from $oldData to $newData'),
        move: (from, to, data) => print('move $data from $from to $to'),
      );
    }

Note that if you implement you own DiffDelegate and call calculateDiff() directly, the DiffDelegate also needs to implement IndexableItemDiffDelegate if you want to call getUpdatesWithData().

Change Operations

For data objects that have some meaningful identity (like an unique id field), you can enable the calculation of Change operations. For this, you need to implement your own DiffDelegate and override the areItemsTheSame method.

Let's say we have a class called DataObject which is defined like this:


class DataObject {
  final int id;
  final int? payload;

  DataObject({required this.id, this.payload});
}

The delegate can be implemented like this:

class DataObjectListDiff extends diffutil.ListDiffDelegate<DataObject> {
  DataObjectListDiff(List<DataObject> oldList, List<DataObject> newList)
      : super(oldList, newList);

  @override
  bool areContentsTheSame(int oldItemPosition, int newItemPosition) {
    return equalityChecker(oldList[oldItemPosition], newList[newItemPosition]);
  }

  @override
  bool areItemsTheSame(int oldItemPosition, int newItemPosition) {
    return oldList[oldItemPosition].id == newList[newItemPosition].id;
  }
}

With this, Change Operations are emitted if the id of an object stays the same between oldList and newList, but it's data changes.

Performance metrics:

Same as Android's DiffUtil:

  • O(N) space
  • O(N + D^2) time performance where D is the length of the edit script.
  • additional O(N^2) time where N is the total number of added and removed items if move detection is enabled

The edit script is the smallest set of operations needed to transform the first list into the second list.

所属分类

源码仓库

使用此插件的项目