使用SizeTransition实现Flutter列表增删动画后出现移除项闪烁问题
列表项增删动画Widget的闪烁问题求助
我实现了一个用于列表项增删时展示动画效果的Widget,尝试过AnimatedList但发现它和flutter_hooks不兼容,所以自己写了以下实现:
class AnimatedTasksListByRating extends HookWidget { const AnimatedTasksListByRating( {super.key, required this.taskList, this.splashColor, this.checkboxActiveColor}); final List<Task> taskList; final Color? splashColor; final Color? checkboxActiveColor; final Duration animationDuration = const Duration(milliseconds: 400); @override Widget build(BuildContext context) { //TODO: fix edge-case: quickly tapping the filter chips messes with the animation //TODO: optimize - even checking one task rebuilds all of the lists //separate to 2 lists - so we can calculate the diff and animate accordingly final displayListState = useState<List<Task>>(taskList); //used to mark which items need to animate final indexToRemove = <int>[]; final indexToAdd = <int>[]; //animation controllers for adding and removing items final animController = useAnimationController(duration: animationDuration, initialValue: 1); final addAnim = Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation(parent: animController, curve: Curves.easeInOutCubic)); final removeAnim = Tween(begin: 1.0, end: 0.0).animate( CurvedAnimation(parent: animController, curve: Curves.easeInOutCubic)); final columnChildren = <Widget>[]; //check if animating from empty state if (taskList.isNotEmpty && displayListState.value.isEmpty) { displayListState.value.add(Task.placeholder); } //find all tasks that have been added, and call insertItem on them. for (int i = 0; i < taskList.length; i++) { if (!displayListState.value.contains(taskList[i])) { displayListState.value.insert(i, taskList[i]); indexToAdd.add(i); } } //find all tasks that have been removed, and call removeItem on them. for (int i = 0; i < displayListState.value.length; i++) { if (!taskList.contains(displayListState.value[i])) { indexToRemove.add(i); } } //animating to empty state if (taskList.isEmpty && displayListState.value.isNotEmpty) { displayListState.value.add(Task.placeholder); } if (displayListState.value.isEmpty) { return ListTile( title: Text( 'No Tasks Here', style: Theme.of(context).textTheme.titleMedium, ), ); } for (int i = 0; i < displayListState.value.length; i++) { Widget toAdd; if (displayListState.value[i] == Task.placeholder) { //if we are moving to empty state - use the adding animation. //if we are moving from the empty state - use the removing animation final animToUse = indexToRemove.contains(i) ? removeAnim : addAnim; toAdd = SizeTransition( sizeFactor: animToUse, child: ListTile( title: Text( 'No Tasks Here', style: Theme.of(context).textTheme.titleMedium, ), ), ); } else if (indexToRemove.contains(i)) { toAdd = SizeTransition( sizeFactor: removeAnim, child: TaskListItem( splashColor: splashColor, checkboxActiveColor: checkboxActiveColor, task: displayListState.value[i], ), ); } else if (indexToAdd.contains(i)) { toAdd = SizeTransition( sizeFactor: addAnim, child: TaskListItem( splashColor: splashColor, checkboxActiveColor: checkboxActiveColor, task: displayListState.value[i], ), ); } else { toAdd = TaskListItem( splashColor: splashColor, checkboxActiveColor: checkboxActiveColor, task: displayListState.value[i], ); } columnChildren.add(toAdd); } if (indexToAdd.isNotEmpty || indexToRemove.isNotEmpty) { final currentState = 1 - animController.value; animController.forward(from: currentState).whenCompleteOrCancel(() { displayListState.value = taskList; }); } return Column(children: columnChildren); } }
这个Widget的逻辑是对比数据列表taskList和当前显示列表displayListState.value,标记需要增删的项并执行动画。整体运行正常,但存在一个问题:有时已移除的项会闪烁回显几帧后才消失。
我在动画完成后的whenCompleteOrCancel回调里把displayListState.value设为taskList,猜测可能是回调延迟导致的这个问题,但不确定具体原因,想请教解决办法。
内容的提问来源于stack exchange,提问作者itayrabin
相关产品推荐
相关产品推荐

