Skip to content

Commit bdac26f

Browse files
gnpricechrisbobbe
authored andcommitted
msglist [nfc]: Let ScrollView count forward, and sliver handle reverse
This means that the overall scroll view has AxisDirection.down as the scroll direction, rather than AxisDirection.up, and the SliverStickyHeader is passed GrowthDirection.reverse instead of GrowthDirection.forward in order to get the same effect. This makes the scrolling a little easier to think about when focused on MessageList's code, because for example (as seen in this diff) `scrollMetrics.extentBefore` now refers to the messages that are older than the ones on screen, rather than those that are newer. This also brings us closer to the setup we'll want for #80 and #82, opening the message list at an anchor other than the end: the empty placeholder sliver at the bottom will become the list of messages after the starting anchor, while the sliver at the top will remain the list of messages above the anchor.
1 parent 03e5f07 commit bdac26f

File tree

2 files changed

+14
-14
lines changed

2 files changed

+14
-14
lines changed

lib/widgets/message_list.dart

+11-11
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,13 @@ class _MessageListState extends State<MessageList> with PerAccountStoreAwareStat
212212
}
213213

214214
void _adjustButtonVisibility(ScrollMetrics scrollMetrics) {
215-
if (scrollMetrics.extentBefore == 0) {
215+
if (scrollMetrics.extentAfter == 0) {
216216
_scrollToBottomVisibleValue.value = false;
217217
} else {
218218
_scrollToBottomVisibleValue.value = true;
219219
}
220220

221-
if (scrollMetrics.extentAfter < kFetchMessagesBufferPixels) {
221+
if (scrollMetrics.extentBefore < kFetchMessagesBufferPixels) {
222222
// TODO: This ends up firing a second time shortly after we fetch a batch.
223223
// The result is that each time we decide to fetch a batch, we end up
224224
// fetching two batches in quick succession. This is basically harmless
@@ -278,6 +278,7 @@ class _MessageListState extends State<MessageList> with PerAccountStoreAwareStat
278278

279279
Widget _buildListView(context) {
280280
final length = model!.items.length;
281+
const centerSliverKey = ValueKey('center sliver');
281282
return CustomScrollView(
282283
// TODO: Offer `ScrollViewKeyboardDismissBehavior.interactive` (or
283284
// similar) if that is ever offered:
@@ -292,18 +293,12 @@ class _MessageListState extends State<MessageList> with PerAccountStoreAwareStat
292293

293294
controller: scrollController,
294295
semanticChildCount: length + 2,
295-
296-
// Setting reverse: true means the scroll starts at the bottom.
297-
// Flipping the indexes (in the SliverChildBuilderDelegate callback)
298-
// means the start/bottom has the latest messages.
299-
// This works great when we want to start from the latest.
300-
// TODO handle scroll starting at first unread, or link anchor
301-
// TODO on new message when scrolled up, anchor scroll to what's in view
302-
reverse: true,
296+
anchor: 1.0,
297+
center: centerSliverKey,
303298

304299
slivers: [
305300
SliverStickyHeaderList(
306-
headerPlacement: HeaderPlacement.scrollingEnd,
301+
headerPlacement: HeaderPlacement.scrollingStart,
307302
delegate: SliverChildBuilderDelegate(
308303
// To preserve state across rebuilds for individual [MessageItem]
309304
// widgets as the size of [MessageListView.items] changes we need
@@ -337,6 +332,11 @@ class _MessageListState extends State<MessageList> with PerAccountStoreAwareStat
337332
final data = model!.items[length - 1 - (i - 2)];
338333
return _buildItem(data, i);
339334
})),
335+
336+
// This is a trivial placeholder that occupies no space. Its purpose is
337+
// to have the key that's passed to [ScrollView.center], and so to cause
338+
// the above [SliverStickyHeaderList] to run from bottom to top.
339+
const SliverToBoxAdapter(key: centerSliverKey),
340340
]);
341341
}
342342

test/widgets/message_list_test.dart

+3-3
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ void main() {
148148
// Initial state should be not visible, as the message list renders with latest message in view
149149
check(isButtonVisible(tester)).equals(false);
150150

151-
scrollController.jumpTo(600);
151+
scrollController.jumpTo(-600);
152152
await tester.pump();
153153
check(isButtonVisible(tester)).equals(true);
154154

@@ -165,7 +165,7 @@ void main() {
165165
// Initial state should be not visible, as the message list renders with latest message in view
166166
check(isButtonVisible(tester)).equals(false);
167167

168-
scrollController.jumpTo(600);
168+
scrollController.jumpTo(-600);
169169
await tester.pump();
170170
check(isButtonVisible(tester)).equals(true);
171171

@@ -187,7 +187,7 @@ void main() {
187187
// Initial state should be not visible, as the message list renders with latest message in view
188188
check(isButtonVisible(tester)).equals(false);
189189

190-
scrollController.jumpTo(600);
190+
scrollController.jumpTo(-600);
191191
await tester.pump();
192192
check(isButtonVisible(tester)).equals(true);
193193

0 commit comments

Comments
 (0)