marcglasberg / indexed_list_view

Flutter package: Similar to a ListView, but lets you programmatically jump to any item, by index.
BSD 2-Clause "Simplified" License
251 stars 21 forks source link

ScrollController attached to multiple scroll views #37

Closed AAverin closed 1 year ago

AAverin commented 2 years ago

Somehow, changing the controller, list gets rebuild but is not functional, as underlying scroll controller ends up being attached to multiple places.

When the exception was thrown, this was the stack: 
#2      ScrollController.position (package:flutter/src/widgets/scroll_controller.dart:108:12)
#3      ScrollController.offset (package:flutter/src/widgets/scroll_controller.dart:115:24)
#4      my code reading controller.offset
#5      ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:308:24)
#6      ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:308:24)
#7      ScrollPosition.notifyListeners (package:flutter/src/widgets/scroll_position.dart:969:11)
#8      ScrollPosition.setPixels (package:flutter/src/widgets/scroll_position.dart:278:9)
#9      ScrollPositionWithSingleContext.setPixels (package:flutter/src/widgets/scroll_position_with_single_context.dart:82:18)

I will try to dig deeper into the code to find an issue and suggest a fix, but maybe it is a known issue already?

AAverin commented 2 years ago

There is also this exception. Controller indeed got disposed already as I am rebuilding the whole screen with the list, so there is a new controller.

A IndexedScrollController was used after being disposed.

Once you have called dispose() on a IndexedScrollController, it can no longer be used.
The relevant error-causing widget was: 
  Container Container:file:///Volumes/MyData/Projects/Personal/PhotoHound/flutter_photohound/photo_hound/lib/components/common/TimeSlider.dart:260:24
When the exception was thrown, this was the stack: 
#0      ChangeNotifier._debugAssertNotDisposed.<anonymous closure> (package:flutter/src/foundation/change_notifier.dart:114:9)
#1      ChangeNotifier._debugAssertNotDisposed (package:flutter/src/foundation/change_notifier.dart:120:6)
#2      ChangeNotifier.removeListener (package:flutter/src/foundation/change_notifier.dart:233:12)
#3      _IndexedListViewState.didUpdateWidget (package:indexed_list_view/indexed_list_view.dart:170:28)
#4      StatefulElement.update (package:flutter/src/widgets/framework.dart:4943:57)
#5      Element.updateChild (package:flutter/src/widgets/framework.dart:3501:15)
#6      SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6291:14)
#7      Element.updateChild (package:flutter/src/widgets/framework.dart:3501:15)
#8      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4780:16)
#9      Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#10     StatelessElement.update (package:flutter/src/widgets/framework.dart:4834:5)
#11     Element.updateChild (package:flutter/src/widgets/framework.dart:3501:15)
#12     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4780:16)
#13     Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#14     ProxyElement.update (package:flutter/src/widgets/framework.dart:5108:5)
#15     Element.updateChild (package:flutter/src/widgets/framework.dart:3501:15)
#16     RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:5787:32)
#17     MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6445:17)
#18     Element.updateChild (package:flutter/src/widgets/framework.dart:3501:15)
#19     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6291:14)
#20     Element.updateChild (package:flutter/src/widgets/framework.dart:3501:15)
#21     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4780:16)
#22     Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#23     StatelessElement.update (package:flutter/src/widgets/framework.dart:4834:5)
#24     Element.updateChild (package:flutter/src/widgets/framework.dart:3501:15)
#25     RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:5787:32)
#26     MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6445:17)
#27     Element.updateChild (package:flutter/src/widgets/framework.dart:3501:15)
#28     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4780:16)
#29     Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#30     StatelessElement.update (package:flutter/src/widgets/framework.dart:4834:5)
#31     HookElement.update (package:flutter_hooks/src/framework.dart:378:11)
#32     Element.updateChild (package:flutter/src/widgets/framework.dart:3501:15)
#33     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4780:16)
#34     Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#35     ProxyElement.update (package:flutter/src/widgets/framework.dart:5108:5)
#36     Element.updateChild (package:flutter/src/widgets/framework.dart:3501:15)
#37     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4780:16)
#38     Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#39     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2659:19)
#40     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
#41     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:363:5)
#42     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144:15)
#43     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1081:9)
#44     SchedulerBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:862:7)
AAverin commented 2 years ago

Funny thing I noticed is that underlying Scrollable.didChangeWidget() doesn't get invoked when controller is changed

AAverin commented 2 years ago

One more extra exception I get afterwards:

======== Exception caught by widgets library =======================================================
The following assertion was thrown building HookBuilder(useValueListenable: false, useValueListenable: CameraPosition(bearing: -0.0, target: LatLng(49.603974947644545, 12.627328350029586), tilt: 0.0, zoom: 4.962529878031322), useValueListenable: null, useValueListenable: null):
'package:flutter/src/widgets/framework.dart': Failed assertion: line 5219 pos 14: '_dependents.isEmpty': is not true.

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.md

The relevant error-causing widget was: 
  HookBuilder HookBuilder:file:///Volumes/MyData/Projects/Personal/.../AnyMapPage.dart:496:19
When the exception was thrown, this was the stack: 
#2      InheritedElement.debugDeactivated.<anonymous closure> (package:flutter/src/widgets/framework.dart:5219:14)
#3      InheritedElement.debugDeactivated (package:flutter/src/widgets/framework.dart:5221:6)
#4      _InactiveElements._deactivateRecursively.<anonymous closure> (package:flutter/src/widgets/framework.dart:1948:15)
#5      _InactiveElements._deactivateRecursively (package:flutter/src/widgets/framework.dart:1950:6)
#6      MultiChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:6386:16)
#7      _InactiveElements._deactivateRecursively (package:flutter/src/widgets/framework.dart:1946:13)
#8      SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:6271:14)
#9      _InactiveElements._deactivateRecursively (package:flutter/src/widgets/framework.dart:1946:13)
AAverin commented 2 years ago

The cause lies in attach for ScrollController being called twice. Both calls come from didChangeDependencies, and the second call happens on the first scroll event for some reason. Happens only on rebuilds of the list when underlying controller changes.

AAverin commented 2 years ago

Here is a comparison of a correctly attached position after rebuild and a new position, that gets attached on first scroll event of the rebuild list. image

AAverin commented 2 years ago

So the trick seems to be not to change controller for the list and reuse the one that is already there

AAverin commented 2 years ago

Having to reuse controller between multiple instances and rebuilds leads to more troubles as listener updates end up being called for to-be-soon-destroyed-instance, potentially corrupting state

marcglasberg commented 1 year ago

Hi @AAverin, thanks for filling this issue, and sorry for the delay in responding. Could you please provide me with a single standalone file, with main method and all, that demonstrates and reproduces the above issue, with minimal and simple code?

Thank you.

marcglasberg commented 1 year ago

I am closing this. I'll reopen in case you respond. Thanks.