fluttercommunity / flutter-draggable-scrollbar

Draggable Scrollbar - A scrollbar that can be dragged for quickly navigation through a vertical list. Additional option is showing label next to scrollthumb with information about current item. Maintainer: @marica27
https://pub.dev/packages/draggable_scrollbar
MIT License
441 stars 77 forks source link

[Proposed fix] Build scheduled during frame exception when using flutter-expandable package #30

Closed instance-id closed 1 year ago

instance-id commented 4 years ago

Hey there, When using the flutter-expandable package along with draggable_scrollbars, within a ListView.separated, collapsing a card in my list caused the error listed below:

════════ Exception caught by rendering library ═════════════════════════════════════════════════════
The following assertion was thrown during performLayout():
Build scheduled during frame.

While the widget tree was being built, laid out, and painted, a new frame was scheduled to rebuild the widget tree.

This might be because setState() was called from a layout or paint callback. If a change is needed to the widget tree, it should be applied as the tree is being built. Scheduling a change for the subsequent frame instead results in an interface that lags behind by one frame. If this was done to make your build dependent on a size measured at layout time, consider using a LayoutBuilder, CustomSingleChildLayout, or CustomMultiChildLayout. If, on the other hand, the one frame delay is the desired effect, for example because this is an animation, consider scheduling the frame in a post-frame callback using SchedulerBinding.addPostFrameCallback or using an AnimationController to trigger the animation.

I have discovered that on line 417 of draggable_scrollbar, if instead of simply setState(), it is changed to the following with an addPostFrameCallback: The issue no longer occurs.

I am still fairly new to flutter, so I can't say for sure if this would have issues in any other cases, but it fixed my issue straight away.

    // Addition of addPostFrameCallback
    WidgetsBinding.instance.addPostFrameCallback((_) {
      setState(() {
        if (notification is ScrollUpdateNotification) {
          _barOffset += getBarDelta(
            notification.scrollDelta,
            barMaxScrollExtent,
            viewMaxScrollExtent,
          );

          if (_barOffset < barMinScrollExtent) {
            _barOffset = barMinScrollExtent;
          }
          if (_barOffset > barMaxScrollExtent) {
            _barOffset = barMaxScrollExtent;
          }

          _viewOffset += notification.scrollDelta;
          if (_viewOffset < widget.controller.position.minScrollExtent) {
            _viewOffset = widget.controller.position.minScrollExtent;
          }
          if (_viewOffset > viewMaxScrollExtent) {
            _viewOffset = viewMaxScrollExtent;
          }
        }

        if (notification is ScrollUpdateNotification ||
            notification is OverscrollNotification) {
          if (_thumbAnimationController.status != AnimationStatus.forward) {
            _thumbAnimationController.forward();
          }

          _fadeoutTimer?.cancel();
          _fadeoutTimer = Timer(widget.scrollbarTimeToFade, () {
            _thumbAnimationController.reverse();
            _labelAnimationController.reverse();
            _fadeoutTimer = null;
          });
        }
      });
    });

Thanks, -id