superlistapp / super_sliver_list

Drop-in replacement for SliverList and ListView that can handle large amount of items with variable extents and reliably jump / animate to any item.
https://superlistapp.github.io/super_sliver_list/
MIT License
277 stars 15 forks source link

I'm having a problem with `ListController.animateToItem` inside a child `OverlayEntry` suddenly calling `OverlayEntry.remove`. #65

Open gbtb16 opened 1 month ago

gbtb16 commented 1 month ago

Hi there!

I'm using version 0.4.1 of super_sliver_list.

I have a problem when using an OverlayEntry that has as a child a custom StatefulWidget, we can call it XComponent, which returns a set of items in a CustomScrollView, along with a SuperSliverList.builder.

In XComponent's initState, there is a controller that references an external ValueNotifier that changes when the user highlights an item. Once the highlight changes, it calls ListController.animateToItem to move to the item selected by the text or manually. Visually testing through the app, it's not possible to find the problem. It is seen in my unit tests when selecting an item with the Enter key or left mouse click, which in addition to selecting the item, automatically calls ListController.animateToItem and then closes the overlay and jumps to the next FocusNode.

In question, closing the overlay while selecting an item, which in turn calls ListController.animateToItem, is where the problem lies. The exception thrown is:

First error:

The following assertion was thrown while finalizing the widget tree:
ScrollableState#562f2(tickers: tracking 1 ticker, position:
ScrollPositionWithSingleContext#73217(offset: 0.0, range: 0.0..183.0, viewport: 213.0,
ScrollableState, ClampingScrollPhysics -> RangeMaintainingScrollPhysics, null,
ScrollDirection.idle), effective physics: ClampingScrollPhysics -> RangeMaintainingScrollPhysics)
was disposed with an active Ticker.
ScrollableState created a Ticker via its TickerProviderStateMixin, but at the time dispose() was
called on the mixin, that Ticker was still active. All Tickers must be disposed before calling
super.dispose().
Tickers used by AnimationControllers should be disposed by calling dispose() on the
AnimationController itself. Otherwise, the ticker will leak.
The offending ticker was:
  _WidgetTicker(created by ScrollableState#562f2)
  The stack trace when the _WidgetTicker was actually created was:
  #0      new Ticker.<anonymous closure> (package:flutter/src/scheduler/ticker.dart:71:40)
  #1      new Ticker (package:flutter/src/scheduler/ticker.dart:73:6)
  #2      new _WidgetTicker (package:flutter/src/widgets/ticker_provider.dart)
  #3      TickerProviderStateMixin.createTicker
  (package:flutter/src/widgets/ticker_provider.dart:296:34)
  #4      new AnimationController (package:flutter/src/animation/animation_controller.dart:265:21)
  #5      AnimateToItem.animate (package:super_sliver_list/src/animate_to_item.dart:37:24)

Second error:

══╡ EXCEPTION CAUGHT BY ANIMATION LIBRARY ╞═════════════════════════════════════════════════════════
The following assertion was thrown while notifying listeners for AnimationController:
SuperSliverMultiBoxAdaptorElement unmounted
'package:flutter/src/widgets/framework.dart':
Failed assertion: line 6280 pos 12: '_renderObject != null'

Either the assertion indicates an error in the framework itself, or we should provide substantially
more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=2_bug.yml

When the exception was thrown, this was the stack:
#2      RenderObjectElement.renderObject (package:flutter/src/widgets/framework.dart:6280:12)
#3      SliverMultiBoxAdaptorElement.renderObject (package:flutter/src/widgets/sliver.dart:725:57)
#4      SuperSliverMultiBoxAdaptorElement.renderObject (package:super_sliver_list/src/element.dart:28:13)        
#5      SuperSliverMultiBoxAdaptorElement.getOffsetToReveal (package:super_sliver_list/src/element.dart:47:12)   
#6      ExtentManager.getOffsetToReveal (package:super_sliver_list/src/extent_manager.dart:171:21)
#7      AnimateToItem.animate.<anonymous closure> (package:super_sliver_list/src/animate_to_item.dart:47:42)     
#8      AnimationLocalListenersMixin.notifyListeners (package:flutter/src/animation/listener_helpers.dart:161:19)#9      AnimationController._tick (package:flutter/src/animation/animation_controller.dart:865:5)
#10     Ticker._tick (package:flutter/src/scheduler/ticker.dart:258:12)
#11     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1386:15)
#12     SchedulerBinding.handleBeginFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:1233:11)
#13     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:633:13)
#14     SchedulerBinding.handleBeginFrame (package:flutter/src/scheduler/binding.dart:1231:17)
#15     AutomatedTestWidgetsFlutterBinding.pump.<anonymous closure> (package:flutter_test/src/binding.dart:1265:9)
#18     TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:71:41)
#19     AutomatedTestWidgetsFlutterBinding.pump (package:flutter_test/src/binding.dart:1256:27)
#20     WidgetTester.pumpAndSettle.<anonymous closure> (package:flutter_test/src/widget_tester.dart:712:23)  

Third error:

══╡ EXCEPTION CAUGHT BY FOUNDATION LIBRARY ╞════════════════════════════════════════════════════════
The following assertion was thrown while dispatching notifications for ValueNotifier<int?>:
ListController is not attached.
'package:super_sliver_list/src/super_sliver_list.dart':
Failed assertion: line 130 pos 12: '_delegate != null'

When the exception was thrown, this was the stack:
#2      ListController.animateToItem (package:super_sliver_list/src/super_sliver_list.dart:130:12)
#3      _AutocompleteV2OverlayConteudoItensState._scrollToHighlight (package:lib_flutter_drie/components_layout_v2/autocomplete_v2/components/autocomplete_v2_overlay_conteudo_itens.dart:101:21)
#4      _AutocompleteV2OverlayConteudoItensState._listener (package:lib_flutter_drie/components_layout_v2/autocomplete_v2/components/autocomplete_v2_overlay_conteudo_itens.dart:94:7)
#5      ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:432:24)
#6      ValueNotifier.value= (package:flutter/src/foundation/change_notifier.dart:554:5)

All the highlighting logic has been tested and proven before, so this wouldn't be your problem.

Studying the case, I imagine the problem is that when an AnimateToItem is created, animate() is called immediately after, but these items are never actually disposed of, resulting in a malfunction when externally forced to exit the loop on its own.

super_sliver_list.dart at 122 line in animateToItem method. image

animate_to_item.dart at 28 line in animate method. image