Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gap in message list #1309

Open
gnprice opened this issue Jan 27, 2025 · 2 comments · May be fixed by #1312
Open

Gap in message list #1309

gnprice opened this issue Jan 27, 2025 · 2 comments · May be fixed by #1312
Assignees
Labels
a-msglist The message-list screen, except what's label:a-content a-sticky_header Our `sticky_header` library

Comments

@gnprice
Copy link
Member

gnprice commented Jan 27, 2025

There's a symptom I've seen happen just a couple of times, and just now while working on the sticky-header code (for #82) I realized what's causing it and how to fix it.

Repro recipe:

  • Scroll up in history, a screenful or two past some message that you sent and can still edit.
  • From a different client, edit that message to be much taller than it was.
  • Scroll back down quickly, say by using the scroll-to-end button.

The result might look like this:

Actual Expected
Image Image

Note the big gap above the "3" message; the recipient header at the very bottom of the list; and the recipient header out of order relative to the message (it has the wrong text for any later message).

Other times the gap can be smaller, like the height of a short message.

I saw a similar gap as part of #725. It's possible the cause of this issue was also the root cause of #725.

Diagnosis

The cause of the issue is that the sticky-header implementation isn't properly handling one aspect of the sliver layout protocol. Specifically, when _RenderSliverStickyHeaderListInner.performLayout produces a SliverGeometry with scrollOffsetCorrection non-null, _RenderSliverStickyHeaderList.performLayout is swallowing that information instead of passing it on to its parent.

I'll send a fix as part of the sticky-headers PR I'm developing for #82.

@gnprice gnprice added a-msglist The message-list screen, except what's label:a-content a-sticky_header Our `sticky_header` library labels Jan 27, 2025
@gnprice gnprice added this to the M6: Post-launch milestone Jan 27, 2025
@gnprice gnprice self-assigned this Jan 27, 2025
@gnprice
Copy link
Member Author

gnprice commented Jan 28, 2025

When reproducing this issue, it sometimes also produces an error to the debug console. Here's an example — which seems to have been provoked by an expired event queue, and the widget tree getting replaced as a result:

I/flutter (23516): Transient error polling event queue for PerAccountStore#43ac4: NetworkException: Failed host lookup: 'chat.zulip.org' (ClientException with SocketException: Failed host lookup: 'chat.zulip.org' (OS Error: No address associated with hostname, errno = 7), uri=https://chat.zulip.org/api/v1/events?queue_id=b859ad25-f2ce-4064-8df6-ff4a393d32bc&last_event_id=2)
I/flutter (23516): Backing off, then will retry…

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY
╞═══════════════════════════════════════════════════════════
The following assertion was thrown while finalizing the widget tree:
'package:flutter/src/rendering/object.dart': Failed assertion: line 2412 pos 14:
'_debugRelayoutBoundaryAlreadyMarkedNeedsLayout()': 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.yml

When the exception was thrown, this was the stack:
#2      RenderObject.markNeedsLayout
(package:flutter/src/rendering/object.dart:2412:14)
#3      RenderBox.markNeedsLayout (package:flutter/src/rendering/box.dart:2850:11)
#4      RenderObject.markParentNeedsLayout
(package:flutter/src/rendering/object.dart:2458:14)
#5      RenderObject.markNeedsLayout
(package:flutter/src/rendering/object.dart:2426:7)
#6      RenderBox.markNeedsLayout (package:flutter/src/rendering/box.dart:2850:11)
#7      RenderObject.markParentNeedsLayout
(package:flutter/src/rendering/object.dart:2458:14)
#8      RenderObject.markNeedsLayout
(package:flutter/src/rendering/object.dart:2426:7)
…
#29     RenderObject.markNeedsLayout
(package:flutter/src/rendering/object.dart:2426:7)
#30     RenderBox.markNeedsLayout (package:flutter/src/rendering/box.dart:2850:11)
#31     RenderObject.markParentNeedsLayout
(package:flutter/src/rendering/object.dart:2458:14)
#32     RenderBox.markNeedsLayout (package:flutter/src/rendering/box.dart:2847:7)
#33     RenderObject.markParentNeedsLayout
(package:flutter/src/rendering/object.dart:2458:14)
#34     RenderBox.markNeedsLayout (package:flutter/src/rendering/box.dart:2847:7)
#35     RenderObject.markParentNeedsLayout
(package:flutter/src/rendering/object.dart:2458:14)
#36     RenderBox.markNeedsLayout (package:flutter/src/rendering/box.dart:2847:7)
#37     RenderObject.markParentNeedsLayout
(package:flutter/src/rendering/object.dart:2458:14)
#38     RenderBox.markNeedsLayout (package:flutter/src/rendering/box.dart:2847:7)
#39     RenderObject.markParentNeedsLayout
(package:flutter/src/rendering/object.dart:2458:14)
#40     RenderBox.markNeedsLayout (package:flutter/src/rendering/box.dart:2847:7)
#41     RenderObject.markParentNeedsLayout
(package:flutter/src/rendering/object.dart:2458:14)
#42     RenderBox.markNeedsLayout (package:flutter/src/rendering/box.dart:2847:7)
#43     RenderObject.markParentNeedsLayout
(package:flutter/src/rendering/object.dart:2458:14)
#44     RenderBox.markNeedsLayout (package:flutter/src/rendering/box.dart:2847:7)
#45     RenderImage.image= (package:flutter/src/rendering/image.dart:99:7)
#46     RawImage.didUnmountRenderObject
(package:flutter/src/widgets/basic.dart:6297:18)
#47     RenderObjectElement.unmount
(package:flutter/src/widgets/framework.dart:6782:15)
#48     _InactiveElements._unmount
(package:flutter/src/widgets/framework.dart:2085:13)
#49     _InactiveElements._unmount.<anonymous closure>
(package:flutter/src/widgets/framework.dart:2083:7)
#50     SingleChildRenderObjectElement.visitChildren
(package:flutter/src/widgets/framework.dart:7013:14)
#51     _InactiveElements._unmount
(package:flutter/src/widgets/framework.dart:2081:13)
#52     _InactiveElements._unmount.<anonymous closure>
(package:flutter/src/widgets/framework.dart:2083:7)
#53     ComponentElement.visitChildren
(package:flutter/src/widgets/framework.dart:5781:14)
…
#435    ComponentElement.visitChildren
(package:flutter/src/widgets/framework.dart:5781:14)
#436    _InactiveElements._unmount
(package:flutter/src/widgets/framework.dart:2081:13)
#437    ListIterable.forEach (dart:_internal/iterable.dart:49:13)
#438    _InactiveElements._unmountAll
(package:flutter/src/widgets/framework.dart:2094:25)
#439    BuildOwner.lockState (package:flutter/src/widgets/framework.dart:2975:15)
#440    BuildOwner.finalizeTree
(package:flutter/src/widgets/framework.dart:3298:7)
#441    WidgetsBinding.drawFrame
(package:flutter/src/widgets/binding.dart:1236:19)
#442    RendererBinding._handlePersistentFrameCallback
(package:flutter/src/rendering/binding.dart:482:5)
#443    SchedulerBinding._invokeFrameCallback
(package:flutter/src/scheduler/binding.dart:1442:15)
#444    SchedulerBinding.handleDrawFrame
(package:flutter/src/scheduler/binding.dart:1355:9)
#445    SchedulerBinding._handleDrawFrame
(package:flutter/src/scheduler/binding.dart:1208:5)
#446    _invoke (dart:ui/hooks.dart:316:13)
#447    PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:428:5)
#448    _drawFrame (dart:ui/hooks.dart:288:31)
(elided 2 frames from class _AssertionError)
══════════════════════════════════════════════════════════════════════════════════
══════════════════

I/flutter (23516): … Backoff wait complete, retrying poll.

@gnprice
Copy link
Member Author

gnprice commented Jan 28, 2025

On a couple of occasions today and yesterday, I also saw repros of this issue produce red error screens (in debug builds, naturally) from other failed assertions:

child._parent == this _owner != null
Image Image

In the latter case I caught it in the debug console as well — and the first exception there was _debugRelayoutBoundaryAlreadyMarkedNeedsLayout failing, just like quoted above. So the _owner != null assertion failure was some downstream consequence of the trees being left in an inconsistent state.

gnprice added a commit to gnprice/zulip-flutter that referenced this issue Jan 29, 2025
Fixes zulip#1309.
Fixes zulip#725.

This is necessary when scrolling back to the origin over items that
grew taller while off screen (and beyond the 250px of near-on-screen
cached items).  For example that can happen because a message was
edited, or because new messages came in that were taller than those
previously present.
@gnprice gnprice linked a pull request Jan 29, 2025 that will close this issue
gnprice added a commit to gnprice/zulip-flutter that referenced this issue Jan 31, 2025
Fixes zulip#1309.
Fixes zulip#725.

This is necessary when scrolling back to the origin over items that
grew taller while off screen (and beyond the 250px of near-on-screen
cached items).  For example that can happen because a message was
edited, or because new messages came in that were taller than those
previously present.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a-msglist The message-list screen, except what's label:a-content a-sticky_header Our `sticky_header` library
Projects
Status: No status
Development

Successfully merging a pull request may close this issue.

1 participant