Skip to content

Commit

Permalink
feat: ✨ add functionality to scroll to bottom button (#246)
Browse files Browse the repository at this point in the history
  • Loading branch information
vatsaltanna authored Aug 23, 2024
1 parent d57c5cc commit 202142a
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 112 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## [2.2.0]

* **feat**: [246](https://github.com/SimformSolutionsPvtLtd/flutter_chatview/pull/246) add
functionality to scroll to bottom button

## [2.1.1]

* **Fix**: [238](https://github.com/SimformSolutionsPvtLtd/flutter_chatview/issues/238) Clear
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,19 @@ ChatView(
),
```

35. Use `ScrollToBottomButtonConfig` to change the configuration of scroll to bottom button.


```dart
ChatView(
...
scrollToBottomButtonConfig: ScrollToBottomButtonConfig(
),
...
),
```

## How to use

Check out [blog](https://medium.com/simform-engineering/chatview-a-cutting-edge-chat-ui-solution-7367b1f9d772) for better understanding and basic implementation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class ConfigurationsInheritedWidget extends InheritedWidget {
this.typeIndicatorConfig,
this.replyPopupConfig,
this.emojiPickerSheetConfig,
this.scrollToBottomButtonConfig,
}) : super(key: key, child: child);

/// Provides configuration for background of chat.
Expand Down Expand Up @@ -50,6 +51,9 @@ class ConfigurationsInheritedWidget extends InheritedWidget {
/// Configuration for emoji picker sheet
final Config? emojiPickerSheetConfig;

/// Provides a configuration for scroll to bottom button config
final ScrollToBottomButtonConfig? scrollToBottomButtonConfig;

static ConfigurationsInheritedWidget? of(BuildContext context) => context
.dependOnInheritedWidgetOfExactType<ConfigurationsInheritedWidget>();

Expand Down
3 changes: 3 additions & 0 deletions lib/src/models/config_models/feature_active_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class FeatureActiveConfig {
this.lastSeenAgoBuilderVisibility = true,
this.receiptsBuilderVisibility = true,
this.enableOtherUserName = true,
this.enableScrollToBottomButton = false,
});

/// Used for enable/disable swipe to reply.
Expand Down Expand Up @@ -54,4 +55,6 @@ class FeatureActiveConfig {
/// Used for enable/disable other users name.
final bool enableOtherUserName;

/// Used for enable/disable Scroll To Bottom Button.
final bool enableScrollToBottomButton;
}
45 changes: 45 additions & 0 deletions lib/src/models/config_models/scroll_to_bottom_button_config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import 'package:chatview/chatview.dart';
import 'package:flutter/material.dart';

/// Configuration for the "Scroll to Bottom" button.
class ScrollToBottomButtonConfig {
ScrollToBottomButtonConfig({
this.backgroundColor,
this.border,
this.borderRadius,
this.icon,
this.scrollAnimationDuration,
this.alignment,
this.padding,
this.onClick,
this.buttonDisplayOffset,
});

/// The background color of the button.
final Color? backgroundColor;

/// The border of the button.
final Border? border;

/// The border radius of the button.
final BorderRadius? borderRadius;

/// The icon displayed on the button.
final Icon? icon;

/// The duration of the scroll animation when the button is clicked.
final Duration? scrollAnimationDuration;

/// The alignment of the button on top of text field.
final ScrollButtonAlignment? alignment;

/// The padding around the button.
final EdgeInsets? padding;

/// The callback function to be executed when the button is clicked.
final VoidCallback? onClick;

/// The scroll offset after which the button is displayed.
/// The button appears when the scroll position is greater than or equal to this value.
final double? buttonDisplayOffset;
}
1 change: 1 addition & 0 deletions lib/src/models/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ export 'data_models/suggestion_item_data.dart';
export 'config_models/reply_suggestions_config.dart';
export 'config_models/suggestion_list_config.dart';
export 'config_models/suggestion_item_config.dart';
export 'config_models/scroll_to_bottom_button_config.dart';
10 changes: 10 additions & 0 deletions lib/src/values/enumeration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,13 @@ extension GroupedListOrderExtension on GroupedListOrder {

bool get isDesc => this == GroupedListOrder.desc;
}

enum ScrollButtonAlignment {
left(Alignment.bottomLeft),
center(Alignment.bottomCenter),
right(Alignment.bottomRight);

const ScrollButtonAlignment(this.alignment);

final Alignment alignment;
}
6 changes: 6 additions & 0 deletions lib/src/widgets/chat_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class ChatView extends StatefulWidget {
this.emojiPickerSheetConfig,
this.replyMessageBuilder,
this.replySuggestionsConfig,
this.scrollToBottomButtonConfig,
}) : chatBackgroundConfig =
chatBackgroundConfig ?? const ChatBackgroundConfiguration(),
chatViewStateConfig =
Expand Down Expand Up @@ -146,6 +147,9 @@ class ChatView extends StatefulWidget {
/// Provides a callback for the view when replying to message
final CustomViewForReplyMessage? replyMessageBuilder;

/// Provides a configuration for scroll to bottom button config
final ScrollToBottomButtonConfig? scrollToBottomButtonConfig;

static void closeReplyMessageView(BuildContext context) {
final state = context.findAncestorStateOfType<_ChatViewState>();
if (state == null) return;
Expand Down Expand Up @@ -229,6 +233,8 @@ class _ChatViewState extends State<ChatView>
repliedMessageConfig: widget.repliedMessageConfig,
swipeToReplyConfig: widget.swipeToReplyConfig,
emojiPickerSheetConfig: widget.emojiPickerSheetConfig,
scrollToBottomButtonConfig:
widget.scrollToBottomButtonConfig,
child: Stack(
children: [
if (chatViewState.isLoading)
Expand Down
95 changes: 95 additions & 0 deletions lib/src/widgets/scroll_to_bottom_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import 'package:chatview/src/extensions/extensions.dart';
import 'package:flutter/material.dart';

class ScrollToBottomButton extends StatefulWidget {
const ScrollToBottomButton({super.key});

@override
ScrollToBottomButtonState createState() => ScrollToBottomButtonState();
}

class ScrollToBottomButtonState extends State<ScrollToBottomButton> {
bool isButtonVisible = false;
ScrollController? scrollController;

@override
void initState() {
super.initState();

WidgetsBinding.instance.addPostFrameCallback((_) {
scrollController = chatViewIW?.chatController.scrollController;
scrollController?.addListener(_updateScrollButtonVisibility);
});
}

void _updateScrollButtonVisibility() {
if (!mounted) return;

final double currentOffset = scrollController?.offset ?? 0;
final double buttonDisplayOffset =
chatListConfig.scrollToBottomButtonConfig?.buttonDisplayOffset ?? 300;
final bool isOffsetCrossedLimit = currentOffset > buttonDisplayOffset;
if (isOffsetCrossedLimit && !isButtonVisible) {
setState(() {
isButtonVisible = true;
});
} else {
setState(() {
isButtonVisible = false;
});
}
}

@override
Widget build(BuildContext context) {
final scrollToBottomButtonConfig =
chatListConfig.scrollToBottomButtonConfig;
return TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: isButtonVisible ? 1.0 : 0.0),
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
builder: (context, scale, child) {
return Transform.scale(
scale: scale,
child: InkWell(
onTap: () {
scrollToBottomButtonConfig?.onClick?.call();
final scrollController =
chatViewIW?.chatController.scrollController;
scrollController?.animateTo(
0,
duration: scrollToBottomButtonConfig?.scrollAnimationDuration ??
const Duration(milliseconds: 200),
curve: Curves.linear,
);
},
child: Container(
decoration: BoxDecoration(
borderRadius: scrollToBottomButtonConfig?.borderRadius ??
BorderRadius.circular(50),
border: scrollToBottomButtonConfig?.border ??
Border.all(color: Colors.grey),
color:
scrollToBottomButtonConfig?.backgroundColor ?? Colors.white,
),
padding: const EdgeInsets.all(4),
child: scrollToBottomButtonConfig?.icon ??
const Icon(
Icons.keyboard_arrow_down_rounded,
color: Colors.grey,
weight: 10,
size: 30,
),
),
),
);
},
);
}

@override
void dispose() {
scrollController?.removeListener(_updateScrollButtonVisibility);
super.dispose();
}
}
Loading

0 comments on commit 202142a

Please sign in to comment.