Skip to content

Commit

Permalink
Merge pull request #291 from takenet/release/0.3.1
Browse files Browse the repository at this point in the history
Release/0.3.1
  • Loading branch information
mpamaro authored Jan 7, 2025
2 parents 322c32a + 31f85a7 commit 3200c1c
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 53 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.3.1

- [DSGroupCard] Improved the scrolling of reply messages

## 0.3.0

- Add support to new Flutter version (3.27.1)
Expand Down
2 changes: 0 additions & 2 deletions lib/blip_ds.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
library;

export 'package:scroll_to_index/scroll_to_index.dart' show AutoScrollController;

export 'src/enums/ds_align.enum.dart' show DSAlign;
export 'src/enums/ds_border_radius.enum.dart' show DSBorderRadius;
export 'src/enums/ds_delivery_report_status.enum.dart'
Expand Down
44 changes: 44 additions & 0 deletions lib/src/controllers/chat/ds_highlight.controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';

class DSHighlightController extends GetxController {
final List<Map<String, AnimationController>> _animations = [];

void highlight(final String key) {
Future.delayed(
const Duration(milliseconds: 600),
() => _highlight(key),
);
}

AnimationController? getController(final String key) {
return _animations
.firstWhereOrNull(
(element) => element.keys.first.contains(key.toString()))
?.values
.first;
}

void add(final String key, final AnimationController controller) =>
_animations.add(
{
key.toString(): controller,
},
);

void _highlight(final String key) {
var controller = _animations
.firstWhereOrNull(
(element) => element.keys.first.contains(key.toString()))
?.values
.first;

if (controller != null) {
controller.value = 1;
Future.delayed(
Duration(seconds: 2),
() => controller.value = 0,
);
}
}
}
3 changes: 3 additions & 0 deletions lib/src/widgets/chat/ds_file_message_bubble.widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class DSFileMessageBubble extends StatelessWidget {
final bool isUploading;
final DSReplyContent? replyContent;
final bool simpleStyle;
final void Function(String)? onTapReply;

/// Creates a Design System's [DSMessageBubble] used on files other than image, audio, or video
DSFileMessageBubble({
Expand All @@ -44,6 +45,7 @@ class DSFileMessageBubble extends StatelessWidget {
this.isUploading = false,
this.replyContent,
this.simpleStyle = false,
this.onTapReply,
}) : style = style ?? DSMessageBubbleStyle(),
controller = DSFileMessageBubbleController();

Expand All @@ -60,6 +62,7 @@ class DSFileMessageBubble extends StatelessWidget {
child: Opacity(
opacity: !isUploading ? 1 : .5,
child: DSMessageBubble(
onTapReply: onTapReply,
replyContent: replyContent,
borderRadius: borderRadius,
padding: EdgeInsets.zero,
Expand Down
47 changes: 47 additions & 0 deletions lib/src/widgets/chat/ds_highlight.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'package:flutter/material.dart';

import '../../controllers/chat/ds_highlight.controller.dart';

class DSHighlight extends StatefulWidget {
final Widget child;
final DSHighlightController controller;

const DSHighlight({
super.key,
required this.child,
required this.controller,
});

@override
State<DSHighlight> createState() => _DSTesteState();
}

class _DSTesteState extends State<DSHighlight> with TickerProviderStateMixin {
late AnimationController? animationController;

@override
void initState() {
animationController =
widget.controller.getController(widget.key.toString());

if (animationController == null) {
animationController = AnimationController(vsync: this);
widget.controller.add(widget.key.toString(), animationController!);
}

super.initState();
}

@override
Widget build(BuildContext context) {
return DecoratedBoxTransition(
decoration: DecorationTween(
begin: BoxDecoration(),
end: BoxDecoration(
color: Colors.black.withValues(alpha: 0.1),
),
).animate(animationController!),
child: widget.child,
);
}
}
1 change: 1 addition & 0 deletions lib/src/widgets/utils/ds_card.widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ class DSCard extends StatelessWidget {
shouldAuthenticate: shouldAuthenticate,
isUploading: isUploading,
simpleStyle: simpleStyle,
onTapReply: onTapReply,
);
}
}
Expand Down
103 changes: 54 additions & 49 deletions lib/src/widgets/utils/ds_group_card.widget.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:scrollview_observer/scrollview_observer.dart';

import '../../controllers/chat/ds_highlight.controller.dart';
import '../../enums/ds_align.enum.dart';
import '../../enums/ds_border_radius.enum.dart';
import '../../enums/ds_button_shape.enum.dart';
Expand All @@ -15,6 +18,7 @@ import '../../utils/ds_message_content_type.util.dart';
import '../../utils/ds_utils.util.dart';
import '../animations/ds_reply_swipe.widget.dart';
import '../buttons/ds_button.widget.dart';
import '../chat/ds_highlight.dart';
import '../chat/ds_message_bubble_detail.widget.dart';
import '../chat/ds_quick_reply.widget.dart';
import '../chat/typing/ds_typing_message_bubble.widget.dart';
Expand Down Expand Up @@ -85,7 +89,7 @@ class DSGroupCard extends StatefulWidget {
final DSMessageBubbleAvatarConfig avatarConfig;
final void Function()? onInfinitScroll;
final bool shrinkWrap;
final AutoScrollController? scrollController;
final ScrollController? scrollController;
final Future<String?> Function(String)? onAsyncFetchSession;
final void Function(DSMessageItem)? onReply;

Expand All @@ -96,18 +100,18 @@ class DSGroupCard extends StatefulWidget {
class _DSGroupCardState extends State<DSGroupCard> {
final List<Widget> widgets = [];
final showScrollBottomButton = false.obs;
late final AutoScrollController controller;
final highlightController = DSHighlightController();

String? previousReplyId;

late ListObserverController observerController;
late ScrollController controller;

@override
void initState() {
controller = widget.scrollController ??
AutoScrollController(
suggestedRowHeight: 80,
viewportBoundaryGetter: () =>
Rect.fromLTRB(0, 0, 0, MediaQuery.of(context).padding.bottom),
axis: Axis.vertical,
);
controller = widget.scrollController ?? ScrollController();

observerController = ListObserverController(controller: controller);

controller.addListener(() {
final nextPageTrigger = 0.90 * controller.position.maxScrollExtent;
Expand All @@ -125,6 +129,7 @@ class _DSGroupCardState extends State<DSGroupCard> {
@override
void dispose() {
controller.dispose();
highlightController.dispose();
super.dispose();
}

Expand All @@ -134,30 +139,29 @@ class _DSGroupCardState extends State<DSGroupCard> {

return Stack(
children: [
ListView.builder(
controller: controller,
padding: const EdgeInsets.symmetric(vertical: 16.0),
reverse: true,
shrinkWrap: widget.shrinkWrap,
itemCount: widgets.length,
itemBuilder: (_, int index) {
return AutoScrollTag(
key: widgets[index].key as ValueKey<String>,
controller: controller,
index: index,
highlightColor: Colors.black.withValues(alpha: 0.1),
ListViewObserver(
controller: observerController,
child: ListView.builder(
controller: controller,
padding: const EdgeInsets.symmetric(vertical: 16.0),
reverse: true,
shrinkWrap: widget.shrinkWrap,
itemCount: widgets.length,
itemBuilder: (_, int index) => DSHighlight(
key: widgets[index].key,
controller: highlightController,
child: widgets[index],
);
},
findChildIndexCallback: (Key key) {
final valueKey = key as ValueKey<String>;
final index = widgets.indexWhere((widget) =>
(widget.key as ValueKey<String>).value == valueKey.value);
),
findChildIndexCallback: (Key key) {
final valueKey = key as ValueKey<String>;
final index = widgets.indexWhere((widget) =>
(widget.key as ValueKey<String>).value == valueKey.value);

if (index == -1) return null;
if (index == -1) return null;

return index;
},
return index;
},
),
),
Align(
alignment: Alignment.bottomRight,
Expand Down Expand Up @@ -268,7 +272,7 @@ class _DSGroupCardState extends State<DSGroupCard> {
_getBorderRadius(length, msgCount, group['align']);

final messageId =
'${message.id ?? DSUtils.generateUniqueID()}-${message.isUploading}';
'${message.id ?? DSUtils.generateUniqueID()}-${message.metadata?['#uniqueId']}-${message.isUploading}';

final bubble = DSCard(
key: ValueKey<String>(messageId),
Expand Down Expand Up @@ -501,25 +505,28 @@ class _DSGroupCardState extends State<DSGroupCard> {
return borderRadius;
}

Future<void> _onScrollPrevious() async {
void _onScrollPrevious() {
int index = 0;

if (previousReplyId != null) {
index = widgets.indexWhere(
(element) => element.key.toString().contains(previousReplyId!),
);
if (previousReplyId == null) {
return _scrollToIndex(index);
}

index = widgets.indexWhere(
(element) => element.key.toString().contains(previousReplyId!),
);

if (index == -1) {
index = 0;
}

await _scrollToIndex(index, highlight: index != 0);
_scrollToIndex(index);

highlightController.highlight(previousReplyId!);
previousReplyId = null;
}

Future<void> _onTapReply(
void _onTapReply(
final String inReplyToId,
final String repliedId,
) async {
Expand All @@ -535,18 +542,16 @@ class _DSGroupCardState extends State<DSGroupCard> {
return;
}

await _scrollToIndex(index);
_scrollToIndex(index);

highlightController.highlight(inReplyToId);
}

Future<void> _scrollToIndex(final int index, {bool highlight = true}) async {
await controller.scrollToIndex(
index,
preferPosition: AutoScrollPosition.middle,
duration: const Duration(milliseconds: 500),
void _scrollToIndex(final int index) {
observerController.animateTo(
index: index,
duration: const Duration(seconds: 1),
curve: Curves.ease,
);

if (highlight) {
controller.highlight(index);
}
}
}
4 changes: 2 additions & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: blip_ds
description: Blip Design System for Flutter.
version: 0.3.0
version: 0.3.1
homepage: https://github.com/takenet/blip-ds-flutter#readme
repository: https://github.com/takenet/blip-ds-flutter

Expand Down Expand Up @@ -40,7 +40,7 @@ dependencies:
video_compress: ^3.1.3
flutter_image_compress: ^2.1.0
phone_number: ^2.0.1
scroll_to_index: ^3.0.1
scrollview_observer: ^1.24.0

dev_dependencies:
flutter_test:
Expand Down

0 comments on commit 3200c1c

Please sign in to comment.