Skip to content

Commit

Permalink
Add equality operator to compare items
Browse files Browse the repository at this point in the history
  • Loading branch information
cp-sneha-s committed Aug 4, 2024
1 parent 5730ac4 commit fda873b
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 103 deletions.
27 changes: 4 additions & 23 deletions example/lib/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,7 @@ import 'package:example/utils/item_tile.dart';
import 'package:flutter/material.dart';
import 'package:animated_reorderable_list/animated_reorderable_list.dart';

class User {
final String name;
final int index;

User({required this.name, required this.index});

// To prevent unnecessary animations when updating items in a list, it's essential to correctly implement the == operator and hashCode for your list item class.
// This allows the list to recognize items with the same data as equal, even if they are different instances.

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is User && other.index == index;
}

@override
int get hashCode => index.hashCode;
}
import 'model/user_model.dart';

class HomePage extends StatefulWidget {
const HomePage({super.key});
Expand All @@ -43,10 +26,8 @@ class _HomePageState extends State<HomePage> {

void insert() {
addedNumber += 1;

setState(() {
list.insert(
list.length, User(name: "User $addedNumber", index: addedNumber));
list.insert(2, User(name: "User $addedNumber", index: addedNumber));
});
}

Expand Down Expand Up @@ -174,7 +155,7 @@ class _HomePageState extends State<HomePage> {
scrollDirection: Axis.vertical,
itemBuilder: (BuildContext context, int index) {
return ItemCard(
key: ValueKey(list[index]),
key: ValueKey(list[index].index),
index: list[index].index);
},
sliverGridDelegate:
Expand Down Expand Up @@ -223,7 +204,7 @@ class _HomePageState extends State<HomePage> {
items: list,
itemBuilder: (BuildContext context, int index) {
return ItemTile(
key: Key(list[index].name),
key: ValueKey(list[index].index),
index: list[index].index);
},
enterTransition: animations,
Expand Down
6 changes: 6 additions & 0 deletions example/lib/model/user_model.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class User {
final String name;
final int index;

User({required this.name, required this.index});
}
11 changes: 9 additions & 2 deletions lib/src/animated_gridview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ class AnimatedGridView<E extends Object> extends StatelessWidget {
/// transition for the widget that is built.
final AnimatedWidgetBuilder? removeItemBuilder;

/// A function that compares two items to determine whether they are the same.
final bool Function(E a, E b)? isSameItem;

const AnimatedGridView(
{Key? key,
required this.items,
Expand All @@ -161,7 +164,9 @@ class AnimatedGridView<E extends Object> extends StatelessWidget {
this.dragStartBehavior = DragStartBehavior.start,
this.clipBehavior = Clip.hardEdge,
this.insertItemBuilder,
this.removeItemBuilder})
this.removeItemBuilder,
this.isSameItem
})
: super(key: key);

@override
Expand Down Expand Up @@ -190,7 +195,9 @@ class AnimatedGridView<E extends Object> extends StatelessWidget {
exitTransition: exitTransition,
scrollDirection: scrollDirection,
insertItemBuilder: insertItemBuilder,
removeItemBuilder: removeItemBuilder),
removeItemBuilder: removeItemBuilder,
isSameItem: isSameItem
),
),
]);
}
Expand Down
50 changes: 28 additions & 22 deletions lib/src/animated_listview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -134,28 +134,33 @@ class AnimatedListView<E extends Object> extends StatelessWidget {
/// transition for the widget that is built.
final AnimatedWidgetBuilder? removeItemBuilder;

const AnimatedListView(
{Key? key,
required this.items,
required this.itemBuilder,
this.enterTransition,
this.exitTransition,
this.insertDuration,
this.removeDuration,
this.scrollDirection = Axis.vertical,
this.padding,
this.reverse = false,
this.controller,
this.primary,
this.physics,
this.scrollBehavior,
this.restorationId,
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
this.dragStartBehavior = DragStartBehavior.start,
this.clipBehavior = Clip.hardEdge,
this.insertItemBuilder,
this.removeItemBuilder})
: super(key: key);

/// A function that compares two items to determine whether they are the same.
final bool Function(E a, E b)? isSameItem;

const AnimatedListView({
Key? key,
required this.items,
required this.itemBuilder,
this.enterTransition,
this.exitTransition,
this.insertDuration,
this.removeDuration,
this.scrollDirection = Axis.vertical,
this.padding,
this.reverse = false,
this.controller,
this.primary,
this.physics,
this.scrollBehavior,
this.restorationId,
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
this.dragStartBehavior = DragStartBehavior.start,
this.clipBehavior = Clip.hardEdge,
this.insertItemBuilder,
this.removeItemBuilder,
this.isSameItem,
}) : super(key: key);

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -183,6 +188,7 @@ class AnimatedListView<E extends Object> extends StatelessWidget {
scrollDirection: scrollDirection,
insertItemBuilder: insertItemBuilder,
removeItemBuilder: removeItemBuilder,
isSameItem: isSameItem,
),
),
]);
Expand Down
5 changes: 5 additions & 0 deletions lib/src/animated_reorderable_gridview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ class AnimatedReorderableGridView<E extends Object> extends StatelessWidget {
/// Whether the items can be dragged by long pressing on them.
final bool longPressDraggable;

/// A function that compares two items to determine whether they are the same.
final bool Function(E a, E b)? isSameItem;

const AnimatedReorderableGridView({
Key? key,
required this.items,
Expand Down Expand Up @@ -206,6 +209,7 @@ class AnimatedReorderableGridView<E extends Object> extends StatelessWidget {
this.longPressDraggable = true,
this.insertItemBuilder,
this.removeItemBuilder,
this.isSameItem
}) : super(key: key);

@override
Expand Down Expand Up @@ -240,6 +244,7 @@ class AnimatedReorderableGridView<E extends Object> extends StatelessWidget {
insertItemBuilder: insertItemBuilder,
removeItemBuilder: removeItemBuilder,
longPressDraggable: longPressDraggable,
isSameItem: isSameItem,
),
),
]);
Expand Down
67 changes: 33 additions & 34 deletions lib/src/animated_reorderable_listview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import 'builder/motion_list_impl.dart';
/// callback serves as a "proxy" (a substitute) for the item in the list. The proxy is
/// created with the original list item as its child.
class AnimatedReorderableListView<E extends Object> extends StatefulWidget {
class AnimatedReorderableListView<E extends Object> extends StatelessWidget {
/// The current list of items that this[AnimatedReorderableListView] should represent.
final List<E> items;

Expand Down Expand Up @@ -188,8 +188,12 @@ class AnimatedReorderableListView<E extends Object> extends StatefulWidget {
/// transition for the widget that is built.
final AnimatedWidgetBuilder? removeItemBuilder;

/// Whether the items can be dragged by long pressing on them.
final bool longPressDraggable;

/// A function that compares two items to determine whether they are the same.
final bool Function(E a, E b)? isSameItem;

const AnimatedReorderableListView({
Key? key,
required this.items,
Expand Down Expand Up @@ -217,47 +221,42 @@ class AnimatedReorderableListView<E extends Object> extends StatefulWidget {
this.insertItemBuilder,
this.removeItemBuilder,
this.longPressDraggable = true,
this.isSameItem,
}) : super(key: key);

@override
State<AnimatedReorderableListView<E>> createState() =>
_AnimatedReorderableListViewState<E>();
}

class _AnimatedReorderableListViewState<E extends Object>
extends State<AnimatedReorderableListView<E>> {
@override
Widget build(BuildContext context) {
return CustomScrollView(
scrollDirection: widget.scrollDirection,
reverse: widget.reverse,
controller: widget.controller,
primary: widget.primary,
physics: widget.physics,
scrollBehavior: widget.scrollBehavior,
restorationId: widget.restorationId,
keyboardDismissBehavior: widget.keyboardDismissBehavior,
dragStartBehavior: widget.dragStartBehavior,
clipBehavior: widget.clipBehavior,
scrollDirection: scrollDirection,
reverse: reverse,
controller: controller,
primary: primary,
physics: physics,
scrollBehavior: scrollBehavior,
restorationId: restorationId,
keyboardDismissBehavior: keyboardDismissBehavior,
dragStartBehavior: dragStartBehavior,
clipBehavior: clipBehavior,
slivers: [
SliverPadding(
padding: widget.padding ?? EdgeInsets.zero,
padding: padding ?? EdgeInsets.zero,
sliver: MotionListImpl(
items: widget.items,
itemBuilder: widget.itemBuilder,
enterTransition: widget.enterTransition,
exitTransition: widget.exitTransition,
insertDuration: widget.insertDuration,
removeDuration: widget.removeDuration,
onReorder: widget.onReorder,
onReorderStart: widget.onReorderStart,
onReorderEnd: widget.onReorderEnd,
proxyDecorator: widget.proxyDecorator,
buildDefaultDragHandles: widget.buildDefaultDragHandles,
scrollDirection: widget.scrollDirection,
insertItemBuilder: widget.insertItemBuilder,
removeItemBuilder: widget.removeItemBuilder,
longPressDraggable: widget.longPressDraggable,
items: items,
itemBuilder: itemBuilder,
enterTransition: enterTransition,
exitTransition: exitTransition,
insertDuration: insertDuration,
removeDuration: removeDuration,
onReorder: onReorder,
onReorderStart: onReorderStart,
onReorderEnd: onReorderEnd,
proxyDecorator: proxyDecorator,
buildDefaultDragHandles: buildDefaultDragHandles,
scrollDirection: scrollDirection,
insertItemBuilder: insertItemBuilder,
removeItemBuilder: removeItemBuilder,
longPressDraggable: longPressDraggable,
isSameItem: isSameItem,
),
),
]);
Expand Down
14 changes: 13 additions & 1 deletion lib/src/builder/motion_list_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ abstract class MotionListBase<W extends Widget, E extends Object>
final AnimatedWidgetBuilder? removeItemBuilder;
final bool? buildDefaultDragHandles;
final bool? longPressDraggable;
final bool Function(E a, E b)? isSameItem;

const MotionListBase(
{Key? key,
Expand All @@ -50,7 +51,8 @@ abstract class MotionListBase<W extends Widget, E extends Object>
this.insertItemBuilder,
this.removeItemBuilder,
this.buildDefaultDragHandles,
this.longPressDraggable})
this.longPressDraggable,
this.isSameItem})
: super(key: key);
}

Expand Down Expand Up @@ -129,6 +131,10 @@ abstract class MotionListBaseState<
@protected
bool get longPressDraggable => widget.longPressDraggable ?? false;

@nonVirtual
@protected
bool Function(E a, E b) get isSameItem => widget.isSameItem ?? (a, b) => a == b;

@override
void initState() {
super.initState();
Expand Down Expand Up @@ -192,12 +198,18 @@ abstract class MotionListBaseState<
void calculateDiff(List oldList, List newList) {
// Detect removed and updated items
for (int i = oldList.length - 1; i >= 0; i--) {
if(isSameItem(oldList[i], newList[i])) {
continue;
}
if (!newList.contains(oldList[i])) {
listKey.currentState!.removeItem(i, removeItemDuration: removeDuration);
}
}
// Detect added items
for (int i = 0; i < newList.length; i++) {
if(isSameItem(oldList[i], newList[i])) {
continue;
}
if (!oldList.contains(newList[i])) {
listKey.currentState!.insertItem(i, insertDuration: insertDuration);
}
Expand Down
Loading

0 comments on commit fda873b

Please sign in to comment.