Skip to content

Commit

Permalink
Fix blink issue (#56)
Browse files Browse the repository at this point in the history
* Fiz blink issue for AnimatedList

* Fiz blink issue for AnimatedList

* Fix blink issue for AnimatedList

* Fix blink issue for AnimatedGrid

* Fix blink issue for AnimatedGrid

* Fix blink issue for AnimatedGrid

* Reformat files

* Reformat files
  • Loading branch information
cp-sneha-s authored Apr 14, 2024
1 parent 3e82694 commit c82ca73
Show file tree
Hide file tree
Showing 10 changed files with 410 additions and 76 deletions.
2 changes: 1 addition & 1 deletion example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
2 changes: 1 addition & 1 deletion example/lib/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class _HomePageState extends State<HomePage> {
index: list[index].index);
},
sliverGridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
SliverReorderableGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4),
enterTransition: animations,
exitTransition: animations,
Expand Down
2 changes: 2 additions & 0 deletions lib/animated_reorderable_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export 'src/animated_gridview.dart';
export 'src/animated_listview.dart';
export 'src/animated_reorderable_gridview.dart';
export 'src/animated_reorderable_listview.dart';
export 'src/component/sliver_grid_with_fixed_cross_axis_count.dart';
export 'src/component/sliver_grid_with_main_axis_extent.dart';
6 changes: 3 additions & 3 deletions lib/src/animation/provider/animation_effect.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,17 @@ class EffectEntry {
Duration get begin => delay;

/// The end time for this entry.
Duration get end => duration;
Duration get end => begin + duration;

/// Builds a sub-animation based on the properties of this entry.
CurveTween buildAnimation({
required Duration totalDuration,
Curve? curve,
}) {
int ttlT = duration.inMicroseconds;
int beginT = begin.inMicroseconds, endT = end.inMicroseconds;
return CurveTween(
curve: Interval(beginT / ttlT, endT / totalDuration.inMicroseconds,
curve: Interval(beginT / totalDuration.inMicroseconds,
endT / totalDuration.inMicroseconds,
curve: curve ?? this.curve),
);
}
Expand Down
127 changes: 88 additions & 39 deletions lib/src/builder/motion_animated_builder.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:math';

import 'package:animated_reorderable_list/animated_reorderable_list.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -114,6 +115,7 @@ class MotionBuilderState extends State<MotionBuilder>
for (int i = 0; i < widget.initialCount; i++) {
childrenMap[i] = MotionData();
}

super.initState();
}

Expand Down Expand Up @@ -444,10 +446,7 @@ class MotionBuilderState extends State<MotionBuilder>
void insertItem(int index, {required Duration insertDuration}) {
assert(index >= 0);
final int itemIndex = _indexToItemIndex(index);

if (itemIndex < 0 || itemIndex > _itemsCount) {
return;
}
assert(itemIndex >= 0 && itemIndex <= _itemsCount);

for (final _ActiveItem item in _incomingItems) {
if (item.itemIndex >= itemIndex) item.itemIndex += 1;
Expand All @@ -460,39 +459,44 @@ class MotionBuilderState extends State<MotionBuilder>
duration: insertDuration,
vsync: this,
);
final AnimationController sizeController = AnimationController(
duration: kAnimationDuration,
vsync: this,
);

final _ActiveItem incomingItem = _ActiveItem.animation(
controller,
itemIndex,
sizeController,
);

_incomingItems
..add(incomingItem)
..sort();

final motionData = MotionData(
endOffset: Offset.zero, startOffset: Offset.zero, visible: false);
final lastKey = childrenMap.keys.last;
childrenMap.update(lastKey, (value) => value.copyWith(visible: false));
final motionData =
MotionData(endOffset: Offset.zero, startOffset: Offset.zero);

final updatedChildrenMap = <int, MotionData>{};

if (childrenMap.containsKey(itemIndex)) {
for (final entry in childrenMap.entries) {
if (entry.key == itemIndex) {
updatedChildrenMap[itemIndex] = motionData;
updatedChildrenMap[entry.key + 1] =
entry.value.copyWith(startOffset: _itemOffsetAt(entry.key));
updatedChildrenMap[itemIndex] = motionData.copyWith(visible: false);
updatedChildrenMap[entry.key + 1] = entry.value.copyWith(
startOffset: _itemOffsetAt(entry.key),
endOffset: getChildOffset(entry.key));
} else if (entry.key > itemIndex) {
updatedChildrenMap[entry.key + 1] =
entry.value.copyWith(startOffset: _itemOffsetAt(entry.key));
updatedChildrenMap[entry.key + 1] = entry.value.copyWith(
startOffset: _itemOffsetAt(entry.key),
endOffset: getChildOffset(entry.key));
} else {
updatedChildrenMap[entry.key] = entry.value;
}
}
childrenMap.clear();
childrenMap.addAll(updatedChildrenMap);

Future.delayed(kAnimationDuration).then((value) {
sizeController.forward().then((value) {
controller.forward().then<void>((_) {
_removeActiveItemAt(_incomingItems, incomingItem.itemIndex)!
.controller!
Expand All @@ -508,7 +512,7 @@ class MotionBuilderState extends State<MotionBuilder>
});
}
setState(() {
_itemsCount = childrenMap.length;
_itemsCount += 1;
});
}

Expand All @@ -518,44 +522,55 @@ class MotionBuilderState extends State<MotionBuilder>
if (itemIndex < 0 || itemIndex >= _itemsCount) {
return;
}
assert(itemIndex >= 0 && itemIndex < _itemsCount);

assert(_activeItemAt(_outgoingItems, itemIndex) == null);

if (childrenMap.containsKey(itemIndex)) {
final _ActiveItem? incomingItem =
_removeActiveItemAt(_incomingItems, itemIndex);

final AnimationController sizeController = incomingItem?.sizeAnimation ??
AnimationController(
vsync: this, duration: kAnimationDuration, value: 1.0);
final AnimationController controller = incomingItem?.controller ??
AnimationController(
duration: removeItemDuration, value: 1.0, vsync: this);
duration: removeItemDuration, value: 1.0, vsync: this)
..addStatusListener((status) => ());
final _ActiveItem outgoingItem =
_ActiveItem.animation(controller, itemIndex);
_ActiveItem.animation(controller, itemIndex, sizeController);
_outgoingItems
..add(outgoingItem)
..sort();

controller.reverse().then<void>((void value) {
_removeActiveItemAt(_outgoingItems, outgoingItem.itemIndex)!
.controller!
.dispose();

// Decrement the incoming and outgoing item indices to account
// for the removal.
for (final _ActiveItem item in _incomingItems) {
if (item.itemIndex > outgoingItem.itemIndex) item.itemIndex -= 1;
}
for (final _ActiveItem item in _outgoingItems) {
if (item.itemIndex > outgoingItem.itemIndex) item.itemIndex -= 1;
if (controller.status == AnimationStatus.dismissed) {
if (childrenMap.containsKey(itemIndex)) {
childrenMap.update(
itemIndex, (value) => value.copyWith(visible: false));
}
sizeController.reverse(from: 1.0).then((value) {
final removedItem =
_removeActiveItemAt(_outgoingItems, outgoingItem.itemIndex)!;
removedItem.controller!.dispose();
removedItem.sizeAnimation!.dispose();

// Decrement the incoming and outgoing item indices to account
// for the removal.
for (final _ActiveItem item in _incomingItems) {
if (item.itemIndex > outgoingItem.itemIndex) item.itemIndex -= 1;
}
for (final _ActiveItem item in _outgoingItems) {
if (item.itemIndex > outgoingItem.itemIndex) item.itemIndex -= 1;
}
_onItemRemoved(itemIndex, removeItemDuration);
});
}

_onItemRemoved(itemIndex, removeItemDuration);
});
}
}

void _onItemRemoved(int itemIndex, Duration removeDuration) {
childrenMap.update(
itemIndex + 1, (value) => value.copyWith(visible: false));
final updatedChildrenMap = <int, MotionData>{};
if (childrenMap.containsKey(itemIndex)) {
for (final entry in childrenMap.entries) {
Expand All @@ -564,8 +579,9 @@ class MotionBuilderState extends State<MotionBuilder>
} else if (entry.key == itemIndex) {
continue;
} else {
updatedChildrenMap[entry.key - 1] = childrenMap[entry.key]!
.copyWith(startOffset: _itemOffsetAt(entry.key));
updatedChildrenMap[entry.key - 1] = childrenMap[entry.key]!.copyWith(
startOffset: _itemOffsetAt(entry.key),
endOffset: _itemOffsetAt(entry.key - 1));
}
}
}
Expand All @@ -575,6 +591,27 @@ class MotionBuilderState extends State<MotionBuilder>
setState(() => _itemsCount -= 1);
}

Offset getChildOffset(int index) {
final currentOffset = _itemOffsetAt(index);
if (!isGrid) {
return currentOffset;
}

if (widget.delegateBuilder
is SliverReorderableGridDelegateWithFixedCrossAxisCount) {
final delegateBuilder = widget.delegateBuilder
as SliverReorderableGridDelegateWithFixedCrossAxisCount;
return delegateBuilder.getOffset(index, currentOffset);
} else if (widget.delegateBuilder
is SliverReorderableGridWithMaxCrossAxisExtent) {
final delegateBuilder =
widget.delegateBuilder as SliverReorderableGridWithMaxCrossAxisExtent;
final offset = delegateBuilder.getOffset(index, currentOffset);
return offset;
}
return Offset.zero;
}

Offset _itemOffsetAt(int index) {
final itemRenderBox =
_items[index]?.context.findRenderObject() as RenderBox?;
Expand Down Expand Up @@ -627,6 +664,7 @@ class MotionBuilderState extends State<MotionBuilder>
index: index,
key: itemGlobalKey,
motionData: motionData,
isGrid: isGrid,
updateMotionData: (MotionData motionData) {
final itemOffset = _itemOffsetAt(index);
childrenMap[index] = motionData.copyWith(
Expand Down Expand Up @@ -732,13 +770,21 @@ class MotionBuilderState extends State<MotionBuilder>
Widget _removeItemBuilder(_ActiveItem outgoingItem, Widget child) {
final Animation<double> animation =
outgoingItem.controller ?? kAlwaysCompleteAnimation;
return widget.removeAnimationBuilder(context, child, animation);
final Animation<double> sizeAnimation =
outgoingItem.sizeAnimation ?? kAlwaysCompleteAnimation;
return SizeTransition(
sizeFactor: sizeAnimation,
child: widget.removeAnimationBuilder(context, child, animation));
}

Widget _insertItemBuilder(_ActiveItem? incomingItem, Widget child) {
final Animation<double> animation =
incomingItem?.controller ?? kAlwaysCompleteAnimation;
return widget.insertAnimationBuilder(context, child, animation);
final Animation<double> sizeAnimation =
incomingItem?.sizeAnimation ?? kAlwaysCompleteAnimation;
return SizeTransition(
sizeFactor: sizeAnimation,
child: widget.insertAnimationBuilder(context, child, animation));
}
}

Expand Down Expand Up @@ -776,11 +822,14 @@ class _MotionBuilderItemGlobalKey extends GlobalObjectKey {
}

class _ActiveItem implements Comparable<_ActiveItem> {
_ActiveItem.animation(this.controller, this.itemIndex);
_ActiveItem.animation(this.controller, this.itemIndex, this.sizeAnimation);

_ActiveItem.index(this.itemIndex) : controller = null;
_ActiveItem.index(this.itemIndex)
: controller = null,
sizeAnimation = null;

final AnimationController? controller;
final AnimationController? sizeAnimation;
int itemIndex;

@override
Expand Down
29 changes: 14 additions & 15 deletions lib/src/builder/motion_list_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -156,23 +156,22 @@ abstract class MotionListBaseState<
void addEffect(AnimationEffect effect, List<EffectEntry> enteries,
{required bool enter}) {
Duration zero = Duration.zero;

if (effect.duration != null) {
if (enter) {
_enterDuration = effect.duration! > _enterDuration
? effect.duration!
: _enterDuration;
assert(_enterDuration >= zero, "Duration can not be negative");
} else {
_exitDuration =
effect.duration! > _exitDuration ? effect.duration! : _exitDuration;
assert(_exitDuration >= zero, "Duration can not be negative");
}
final timeForAnimation =
(effect.delay ?? zero) + (effect.duration ?? kAnimationDuration);
if (enter) {
_enterDuration =
timeForAnimation > _enterDuration ? timeForAnimation : _enterDuration;
assert(_enterDuration >= zero, "Duration can not be negative");
} else {
_exitDuration =
timeForAnimation > _exitDuration ? timeForAnimation : _exitDuration;
assert(_exitDuration >= zero, "Duration can not be negative");
}

EffectEntry entry = EffectEntry(
animationEffect: effect,
delay: effect.delay ?? zero,
duration: effect.duration ?? removeDuration,
duration: effect.duration ?? kAnimationDuration,
curve: effect.curve ?? Curves.linear);

enteries.add(entry);
Expand Down Expand Up @@ -203,7 +202,7 @@ abstract class MotionListBaseState<
Widget animatedChild = child;
for (EffectEntry entry in _enterAnimations) {
animatedChild = entry.animationEffect
.build(context, animatedChild, animation, entry, enterDuration);
.build(context, animatedChild, animation, entry, insertDuration);
}
return animatedChild;
}
Expand All @@ -219,7 +218,7 @@ abstract class MotionListBaseState<
Widget animatedChild = child;
for (EffectEntry entry in _exitAnimations) {
animatedChild = entry.animationEffect
.build(context, animatedChild, animation, entry, exitDuration);
.build(context, animatedChild, animation, entry, removeDuration);
}
return animatedChild;
}
Expand Down
Loading

0 comments on commit c82ca73

Please sign in to comment.