Dimibe / sticky_grouped_list

A ScrollablePositionedList in which items can be grouped into sections with sticky headers.
https://pub.dev/packages/sticky_grouped_list
MIT License
184 stars 71 forks source link

I can't seem to understand what the error is. #57

Open iamevaristus opened 1 year ago

iamevaristus commented 1 year ago

I was using the GroupedListView but then decided to switch over to the Sticky one. Then, I started getting errors. When the message is loaded, I get this error:

The following StateError was thrown while dispatching notifications for ValueNotifier<Iterable<ItemPosition>>:
Bad state: No element

When the exception was thrown, this was the stack
#0      Iterable.reduce (dart:core/iterable.dart:353:7)
#1      StickyGroupedListViewState._positionListener
package:sticky_grouped_list/src/sticky_grouped_list.dart:316
#2      ChangeNotifier.notifyListeners
package:flutter/…/foundation/change_notifier.dart:351
#3      ValueNotifier.value=
package:flutter/…/foundation/change_notifier.dart:456
#4      _ScrollablePositionedListState._updatePositions
package:scrollable_positioned_list/src/scrollable_positioned_list.dart:578
#5      ChangeNotifier.notifyListeners
package:flutter/…/foundation/change_notifier.dart:351
#6      ValueNotifier.value=
package:flutter/…/foundation/change_notifier.dart:456
#7      _PositionedListState._schedulePositionNotificationUpdate.<anonymous closure>
package:scrollable_positioned_list/src/positioned_list.dart:364
#8      SchedulerBinding._invokeFrameCallback
package:flutter/…/scheduler/binding.dart:1175
#9      SchedulerBinding.handleDrawFrame
package:flutter/…/scheduler/binding.dart:1113
#10     SchedulerBinding._handleDrawFrame
package:flutter/…/scheduler/binding.dart:1015
#11     _invoke (dart:ui/hooks.dart:148:13)
#12     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:318:5)
#13     _drawFrame (dart:ui/hooks.dart:115:31)
The ValueNotifier<Iterable<ItemPosition>> sending notification was: ValueNotifier<Iterable<ItemPosition>>#b33d6((ItemPosition(index: 0, itemLeadingEdge: 0.0, itemTrailingEdge: 0.07679180887372014), ItemPosition(index: 1, itemLeadingEdge: 0.07679180887372014, itemTrailingEdge: 0.07679180887372014), ItemPosition(index: 2, itemLeadingEdge: 0.07679180887372014, itemTrailingEdge: 0.15358361774744028), ..., ItemPosition(index: 12, itemLeadingEdge: 0.46075085324232085, itemTrailingEdge: 0.5853242320819113), ItemPosition(index: 13, itemLeadingEdge: 0.5853242320819113, itemTrailingEdge: 0.658703071672355)))

I already but a check on whether the messageList is empty or not, and I am sure of what am saying. Whenever the message is fully loaded, I get the "Bad State, No element" error.

The Sticky Widget:

class ChatListWidget extends StatelessWidget {
  final List<UserChatModel> messages;
  final UserInformationModel userModel;
  final GroupedItemScrollController? messageController;
  final ItemPositionsListener? itemPositionsListener;
  final void Function(String message, bool isUser, MessageType msgType, String otherName, String messageID) replyMessage;
  final String roomID;
  final String otherFirstName;
  const ChatListWidget({
    super.key, required this.messages, required this.userModel, required this.replyMessage, required this.roomID,
    required this.otherFirstName, this.messageController, this.itemPositionsListener
  });
// index >= 0 && index < messages.length &&
  @override
  Widget build(BuildContext context) {
    return StickyGroupedListView<UserChatModel, int>(
      elements: messages,
      // itemScrollController: messageController,
      order: StickyGroupedListOrder.values.first,
      initialScrollIndex: 0,
      groupBy: (msg) => msg.updatedAt.day,
      itemPositionsListener: itemPositionsListener,
      elementIdentifier: (element) => element.replyID.isEmpty ? element.msgID : element.replyID,
      reverse: true,
      groupSeparatorBuilder: (value) => DateLabel(model: value),
      itemBuilder: (context, message) {
        // final message = messages[index];
        if (!message.isRead && message.otherID == userModel.serchID) {
          ChatFunctions.setChatMessageSeen(context: context, roomID: roomID, messageID: message.msgID);
        }
        return SwipeTo(
          onLeftSwipe: () {
            if(message.serchID == userModel.serchID) {
              replyMessage(message.message, message.serchID == userModel.serchID, message.msgType, otherFirstName, message.msgID);
            }
          },
          onRightSwipe: () {
            if(message.serchID != userModel.serchID) {
              replyMessage(message.message, message.serchID == userModel.serchID, message.msgType, otherFirstName, message.msgID);
            }
          },
          child: MessageCard(
            message: message, userID: userModel.serchID,
            goToReplied: () => messageController?.scrollToElement(identifier: message.replyID, duration: const Duration(seconds: 1), curve: Curves.easeOut),
          )
        );
      },
    );
  }
}

Where it is passed:

if(loadingMessages)
  Expanded(
    child: userMessages.isEmpty ? Center(child: SText(
      text: "Start conversation with ${widget.otherProfile.firstName}", color: Theme.of(context).primaryColor, size: 18
    )) : ChatListWidget(
      messages: userMessages, userModel: userInformationModel,
      roomID: widget.roomID.isEmpty ? roomID! : widget.roomID,
      otherFirstName: widget.otherProfile.firstName, messageController: messageController,
      replyMessage: (message, isUser, msgType, otherName, messageID) => replyMessage(
        message: message, isUser: isUser, msgType: msgType, otherName: otherName, messageID: messageID
      ),
    )
  )
  else
  Expanded(
    child: localUnsavedMessages.isEmpty ? Center(child: SText(
      text: "Start conversation with ${widget.otherProfile.firstName}", color: Theme.of(context).primaryColor, size: 18
    )) : ChatListWidget(
      messages: localUnsavedMessages, userModel: userInformationModel,
      roomID: widget.roomID.isEmpty ? roomID! : widget.roomID,
      otherFirstName: widget.otherProfile.firstName,
      messageController: messageScrollController,
      itemPositionsListener: itemPositionsListener,
      replyMessage: (message, isUser, msgType, otherName, messageID) => replyMessage(
        message: message, isUser: isUser, msgType: msgType, otherName: otherName, messageID: messageID
      ),
    )
  ),

I need a fast fix

vanosidor commented 1 year ago

Have the same error stack in Crashlytics:

Fatal Exception: io.flutter.plugins.firebase.crashlytics.FlutterError: Bad state: No element. Error thrown while dispatching notifications for ValueNotifier<Iterable<ItemPosition>>.
       at Iterable.reduce(dart:core)
       at StickyGroupedListViewState._positionListener(sticky_grouped_list.dart:316)
       at ChangeNotifier.notifyListeners(change_notifier.dart:403)
       at ValueNotifier.value=(change_notifier.dart:530)
       at _ScrollablePositionedListState._updatePositions(scrollable_positioned_list.dart:653)
       at ChangeNotifier.notifyListeners(change_notifier.dart:403)
       at ValueNotifier.value=(change_notifier.dart:530)
       at _PositionedListState._schedulePositionNotificationUpdate.<fn>(positioned_list.dart:365)
       at SchedulerBinding._invokeFrameCallback(binding.dart:1297)
       at SchedulerBinding.handleDrawFrame(binding.dart:1236)
       at SchedulerBinding._handleDrawFrame(binding.dart:1085)
Sumit258000 commented 1 year ago

Did you guys found any fix or know whats wrong?

kwiliodev commented 1 year ago

Same issue, has anyone found a fix? This happens for me when I use a filter and the result from the database is empty. This seems to only happen after the widget is loaded so if the list is empty on first load, it works fine. Hoping there is a solution, otherwise I sadly have to remove from the app. Thanks.

kwiliodev commented 1 year ago

I added PR from another PR #53 that I saw but it had merge issues. This seems to fix the issue locally. If either can be merged soon, that would be great, thanks!

tomaspodmanicky commented 10 months ago

will there be fix soon?

my workaround is this -> it works without triggering the error and if you have RefreshIndicator above, it will work aswell:

elements.isNotEmpty ? StickyGroupedListView<T, DateTime>(
              .......
) : ListView(
  physics: AlwaysScrollableScrollPhysics(),
)