Skip to content

Commit c7c6d15

Browse files
check
1 parent 8c2c893 commit c7c6d15

File tree

1 file changed

+79
-32
lines changed

1 file changed

+79
-32
lines changed

lib/widgets/message_list.dart

+79-32
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import 'dart:math';
1+
import 'dart:math' as math;
22

33
import 'package:collection/collection.dart';
44
import 'package:flutter/material.dart';
@@ -516,36 +516,47 @@ class _MessageListState extends State<MessageList> with PerAccountStoreAwareStat
516516
}
517517

518518
final viewportDimension = scrollController.position.viewportDimension;
519-
final maxScrollExtent = scrollController.position.maxScrollExtent;
520519
final currentScroll = scrollController.position.pixels;
521520

522-
// If we're within 300px of the bottommost viewport, auto-scroll
523-
if (maxScrollExtent - currentScroll - viewportDimension < 300) {
521+
// If we're one viewportDimension from the bottomList, scroll to it
522+
if (currentScroll + viewportDimension > 0) {
524523

525-
final distance = scrollController.position.pixels;
526-
final durationMsAtSpeedLimit = (1000 * distance / 8000).ceil();
527-
final durationMs = max(300, durationMsAtSpeedLimit);
524+
// Calculate initial scroll parameters
525+
final distanceToCenter = scrollController.position.pixels;
526+
final durationMsAtSpeedLimit = (1000 * distanceToCenter / 8000).ceil();
527+
final durationMs = math.max(300, durationMsAtSpeedLimit);
528528

529-
await scrollController.animateTo(
530-
scrollController.position.maxScrollExtent,
531-
duration: Duration(milliseconds: durationMs),
532-
curve: Curves.ease);
529+
// If we're not at the bottomSliver,scroll to it
530+
if(distanceToCenter<36){
531+
await scrollController.animateTo(
532+
36, //Scroll 36 px inside bottomSliver.The sizedBox is 36px high. so theres no chance of overscrolling
533+
duration: Duration(milliseconds: durationMs),
534+
curve: Curves.easeIn);
535+
await Future<void>.delayed(const Duration(milliseconds: 50));
536+
}
533537

538+
// Wait for the layout to settle so scrollController.position.pixels is updated properly
534539

535540

536-
if (scrollController.position.pixels + 40 < scrollController.position.maxScrollExtent ) {
541+
final distanceToBottom = scrollController.position.maxScrollExtent - scrollController.position.pixels;
542+
final durationMsToBottom = math.min(1500, (1000 * distanceToBottom / 8000).ceil());
543+
// If we go too fast, we'll overscroll.as
544+
545+
// After scroling to the bottom sliver, scroll to the bottom of the bottomSliver if we're not already there
546+
if (distanceToBottom > 36) {
537547
await scrollController.animateTo(
538548
scrollController.position.maxScrollExtent,
539-
duration: Duration(milliseconds: durationMs),
540-
curve: Curves.ease);
541-
}
549+
duration: Duration(milliseconds: durationMsToBottom),
550+
curve: Curves.ease);
551+
}
552+
542553
}
543554
});
544555
}
545556
}
546557

547558
void _handleScrollMetrics(ScrollMetrics scrollMetrics) {
548-
if (scrollMetrics.extentAfter == 0) {
559+
if (scrollMetrics.extentAfter < 40) {
549560
_scrollToBottomVisibleValue.value = false;
550561
} else {
551562
_scrollToBottomVisibleValue.value = true;
@@ -675,10 +686,13 @@ class _MessageListState extends State<MessageList> with PerAccountStoreAwareStat
675686
sliver = SliverSafeArea(sliver: sliver);
676687
}
677688

689+
690+
678691
return CustomScrollView(
679692
// TODO: Offer `ScrollViewKeyboardDismissBehavior.interactive` (or
680693
// similar) if that is ever offered:
681694
// https://github.com/flutter/flutter/issues/57609#issuecomment-1355340849
695+
682696
keyboardDismissBehavior: switch (Theme.of(context).platform) {
683697
// This seems to offer the only built-in way to close the keyboard
684698
// on iOS. It's not ideal; see TODO above.
@@ -734,6 +748,30 @@ class _MessageListState extends State<MessageList> with PerAccountStoreAwareStat
734748
}
735749
}
736750

751+
752+
class NoOverScrollPhysics extends ScrollPhysics {
753+
const NoOverScrollPhysics({super.parent});
754+
755+
@override
756+
NoOverScrollPhysics applyTo(ScrollPhysics? ancestor) {
757+
return NoOverScrollPhysics(parent: buildParent(ancestor));
758+
}
759+
760+
@override
761+
double applyBoundaryConditions(ScrollMetrics position, double value) {
762+
// Prevent overscroll at the top
763+
if (value < position.minScrollExtent) {
764+
return position.minScrollExtent;
765+
}
766+
// Prevent overscroll at the bottom
767+
if (value > position.maxScrollExtent) {
768+
return position.maxScrollExtent;
769+
}
770+
return 0.0; // Allow normal scrolling within bounds
771+
}
772+
}
773+
774+
737775
class ScrollToBottomButton extends StatelessWidget {
738776
const ScrollToBottomButton({super.key, required this.scrollController, required this.visibleValue});
739777

@@ -742,26 +780,34 @@ class ScrollToBottomButton extends StatelessWidget {
742780

743781
Future<void> _navigateToBottom() async {
744782
// Calculate initial scroll parameters
745-
final distance = scrollController.position.pixels;
746-
final durationMsAtSpeedLimit = (1000 * distance / 8000).ceil();
747-
final durationMs = max(300, durationMsAtSpeedLimit);
783+
final distanceToCenter = scrollController.position.pixels;
784+
final durationMsAtSpeedLimit = (1000 * distanceToCenter / 8000).ceil();
785+
final durationMs = math.max(300, durationMsAtSpeedLimit);
748786

749-
// Do a single scroll attempt with a completion check
750-
await scrollController.animateTo(
751-
scrollController.position.maxScrollExtent,
787+
// If we're not at the bottomSliver,scroll to it
788+
if(distanceToCenter<36){
789+
await scrollController.animateTo(
790+
36, //Scroll 36 px inside bottomSliver.The sizedBox is 36px high. so theres no chance of overscrolling
752791
duration: Duration(milliseconds: durationMs),
753-
curve: Curves.ease);
754-
var count =1;
755-
// Check if we actually reached bottom, if not try again
756-
// This handles cases where content was loaded during scroll
757-
while (scrollController.position.pixels + 40 < scrollController.position.maxScrollExtent) {
792+
curve: Curves.easeIn);
793+
}
794+
795+
796+
// Wait for the layout to settle so scrollController.position.pixels is updated properly
797+
await Future<void>.delayed(const Duration(milliseconds: 50));
798+
799+
800+
final distanceToBottom = scrollController.position.maxScrollExtent - scrollController.position.pixels;
801+
final durationMsToBottom = math.min(1000, (1000 * distanceToBottom / 8000).ceil());
802+
// If we go too fast, we'll overscroll.
803+
804+
// After scroling to the bottom sliver, scroll to the bottom of the bottomSliver if we're not already there
805+
if (distanceToBottom > 36) {
758806
await scrollController.animateTo(
759807
scrollController.position.maxScrollExtent,
760-
duration: const Duration(milliseconds: 300),
761-
curve: Curves.ease);
762-
count++;
808+
duration: Duration(milliseconds: durationMsToBottom),
809+
curve: Curves.easeOut);
763810
}
764-
print("count: $count");
765811
}
766812

767813
@override
@@ -1221,7 +1267,8 @@ class DmRecipientHeader extends StatelessWidget {
12211267
.where((id) => id != store.selfUserId)
12221268
.map((id) => store.users[id]?.fullName ?? zulipLocalizations.unknownUserName)
12231269
.sorted()
1224-
.join(", "));
1270+
.join(", ")
1271+
);
12251272
} else {
12261273
// TODO pick string; web has glitchy "You and $yourname"
12271274
title = zulipLocalizations.messageListGroupYouWithYourself;

0 commit comments

Comments
 (0)