- Implemented ChatScrollController to manage scroll behavior and visibility of jump-to-bottom button. - Added functionality to automatically scroll to the bottom when the keyboard opens. - Created JumpToBottomButton widget that appears when the user scrolls up, allowing quick navigation back to the bottom of the chat.chore/offband-rebrand
parent
75b8b8af70
commit
fa514533eb
File diff suppressed because one or more lines are too long
@ -0,0 +1,68 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ChatScrollController extends ScrollController {
|
||||
final ValueNotifier<bool> showJumpToBottom = ValueNotifier(false);
|
||||
VoidCallback? onScrollNearTop;
|
||||
|
||||
static const _bottomThreshold = 100.0;
|
||||
static const _topThreshold = 50.0;
|
||||
|
||||
ChatScrollController() {
|
||||
addListener(_handleScroll);
|
||||
}
|
||||
|
||||
void _handleScroll() {
|
||||
if (!hasClients) return;
|
||||
final pos = position;
|
||||
|
||||
// With reverse: true, position 0 is bottom, maxScrollExtent is top
|
||||
// Show jump button when scrolled away from bottom (position > threshold)
|
||||
final isAtBottom = pos.pixels <= _bottomThreshold;
|
||||
if (showJumpToBottom.value == isAtBottom) {
|
||||
showJumpToBottom.value = !isAtBottom;
|
||||
}
|
||||
|
||||
// Pagination trigger when scrolled near top (maxScrollExtent)
|
||||
if (pos.pixels >= pos.maxScrollExtent - _topThreshold) {
|
||||
onScrollNearTop?.call();
|
||||
}
|
||||
}
|
||||
|
||||
void jumpToBottom() {
|
||||
if (hasClients && position.maxScrollExtent > 0) {
|
||||
animateTo(
|
||||
0, // With reverse: true, position 0 is bottom
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void handleKeyboardOpen() {
|
||||
// Simple: just scroll to bottom when keyboard opens
|
||||
if (hasClients) {
|
||||
animateTo(
|
||||
0, // With reverse: true, position 0 is bottom
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void scrollToBottomIfAtBottom() {
|
||||
// Only scroll if jump button is NOT showing (i.e., already at bottom)
|
||||
if (!showJumpToBottom.value && hasClients && position.maxScrollExtent > 0) {
|
||||
animateTo(
|
||||
0, // With reverse: true, position 0 is bottom
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
showJumpToBottom.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../helpers/chat_scroll_controller.dart';
|
||||
|
||||
class JumpToBottomButton extends StatelessWidget {
|
||||
final ChatScrollController scrollController;
|
||||
|
||||
const JumpToBottomButton({
|
||||
super.key,
|
||||
required this.scrollController,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: scrollController.showJumpToBottom,
|
||||
builder: (context, show, _) {
|
||||
if (!show) return const SizedBox.shrink();
|
||||
return Positioned(
|
||||
right: 16,
|
||||
bottom: 16,
|
||||
child: FloatingActionButton.small(
|
||||
onPressed: scrollController.jumpToBottom,
|
||||
child: const Icon(Icons.keyboard_arrow_down),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue