Skip to content

Commit fdc9d0c

Browse files
committed
msglist: MessageList now has a scroll to bottom button
1 parent 71ec0cc commit fdc9d0c

File tree

1 file changed

+49
-1
lines changed

1 file changed

+49
-1
lines changed

lib/widgets/message_list.dart

+49-1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,31 @@ class MessageListAppBarTitle extends StatelessWidget {
9797
}
9898
}
9999

100+
class ScrollToBottomButton extends StatelessWidget {
101+
const ScrollToBottomButton({super.key, required this.scrollController, required this.visibleValue});
102+
final ValueNotifier<bool> visibleValue;
103+
final ScrollController scrollController;
104+
105+
Future<void> _navigateToBottom(BuildContext context) async {
106+
scrollController.animateTo(0, duration: const Duration(milliseconds: 300), curve: Curves.easeIn);
107+
}
108+
109+
@override
110+
Widget build(BuildContext context) {
111+
return ValueListenableBuilder<bool>(
112+
builder: (BuildContext context, bool value, Widget? child) {
113+
return (value && child != null) ? child : const SizedBox.shrink();
114+
},
115+
valueListenable: visibleValue,
116+
// TODO: fix hardcoded values for size and style here
117+
child: IconButton(
118+
tooltip: "Scroll to bottom",
119+
icon: const Icon(Icons.expand_circle_down_rounded),
120+
iconSize: 40,
121+
style: IconButton.styleFrom(foregroundColor: const HSLColor.fromAHSL(0.5,240,0.96,0.68).toColor()),
122+
onPressed: () => _navigateToBottom(context)));
123+
}
124+
}
100125

101126
class MessageList extends StatefulWidget {
102127
const MessageList({super.key, required this.narrow});
@@ -109,6 +134,9 @@ class MessageList extends StatefulWidget {
109134

110135
class _MessageListState extends State<MessageList> {
111136
MessageListView? model;
137+
final ScrollController scrollController = ScrollController();
138+
139+
final ValueNotifier<bool> _scrollToBottomVisibleValue = ValueNotifier<bool>(false);
112140

113141
@override
114142
void didChangeDependencies() {
@@ -161,7 +189,26 @@ class _MessageListState extends State<MessageList> {
161189
child: Center(
162190
child: ConstrainedBox(
163191
constraints: const BoxConstraints(maxWidth: 760),
164-
child: _buildListView(context))))));
192+
child: NotificationListener<ScrollEndNotification>(
193+
onNotification: (scrollEnd) {
194+
final metrics = scrollEnd.metrics;
195+
if (metrics.atEdge && metrics.pixels == 0) {
196+
_scrollToBottomVisibleValue.value = false;
197+
} else {
198+
_scrollToBottomVisibleValue.value = true;
199+
}
200+
return true;
201+
},
202+
child: Stack(
203+
children: <Widget>[
204+
_buildListView(context),
205+
Container(
206+
alignment: Alignment.bottomRight,
207+
child: ScrollToBottomButton(scrollController: scrollController, visibleValue: _scrollToBottomVisibleValue),
208+
),
209+
]
210+
),
211+
))))));
165212
}
166213

167214
Widget _buildListView(context) {
@@ -179,6 +226,7 @@ class _MessageListState extends State<MessageList> {
179226
_ => ScrollViewKeyboardDismissBehavior.manual,
180227
},
181228

229+
controller: scrollController,
182230
itemCount: length,
183231
// Setting reverse: true means the scroll starts at the bottom.
184232
// Flipping the indexes (in itemBuilder) means the start/bottom

0 commit comments

Comments
 (0)