From bbb71ec47fb6d1ba349654e87c04a9bc4ee7ba78 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 27 Oct 2020 13:28:17 +0530 Subject: [PATCH 1/8] feat: Added sticky header to display position data --- lib/src/message_list_view.dart | 278 ++++++++++++++++++--------------- pubspec.yaml | 1 + 2 files changed, 149 insertions(+), 130 deletions(-) diff --git a/lib/src/message_list_view.dart b/lib/src/message_list_view.dart index 7c529f95..42b434fd 100644 --- a/lib/src/message_list_view.dart +++ b/lib/src/message_list_view.dart @@ -6,6 +6,7 @@ import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter/src/message_widget.dart'; import 'package:stream_chat_flutter/src/system_message.dart'; import 'package:visibility_detector/visibility_detector.dart'; +import 'package:widgets_visibility_provider/widgets_visibility_provider.dart'; import '../stream_chat_flutter.dart'; import 'date_divider.dart'; @@ -149,150 +150,167 @@ class _MessageListViewState extends State { List _newMessageList = []; Function _onThreadTap; bool _showScrollToBottom = false; + var positionData; @override Widget build(BuildContext context) { final streamChannel = StreamChannel.of(context); /// TODO: find a better solution when (https://github.com/flutter/flutter/issues/21023) is fixed - return Stack( - children: [ - NotificationListener( - onNotification: (_) { - if (_scrollController.offset < 150 && _newMessageList.isNotEmpty) { - setState(() { - _messages.insertAll(0, _newMessageList); - _newMessageList.clear(); - }); - } - return true; - }, - child: ListView.custom( - key: Key('messageListView'), - physics: widget.scrollPhysics, - keyboardDismissBehavior: widget.keyboardDismissBehavior, - controller: _scrollController, - reverse: true, - childrenDelegate: SliverChildBuilderDelegate( - (context, i) { - if (i == _messages.length + 1) { - if (widget.parentMessage != null) { - if (widget.parentMessageBuilder != null) { - return widget.parentMessageBuilder( - context, - widget.parentMessage, - ); - } else { - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - buildParentMessage(widget.parentMessage), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 32), - child: Container( - padding: const EdgeInsets.all(8), - child: Text( - 'Start of thread', - textAlign: TextAlign.center, - ), - color: - Theme.of(context).accentColor.withAlpha(50), - ), - ), - ], - ); - } - } else { - return SizedBox(); - } - } - - if (i == _messages.length) { - return _buildLoadingIndicator(streamChannel); - } - final message = _messages[i]; - final nextMessage = i > 0 ? _messages[i - 1] : null; - - Widget messageWidget; - - if (i == 0) { - messageWidget = _buildBottomMessage( - context, - message, - _messages, - streamChannel, - ); - } else if (i == _messages.length - 1) { - messageWidget = _buildTopMessage( - context, - message, - _messages, - streamChannel, - ); - } else { - if (widget.messageBuilder != null) { - messageWidget = Builder( - key: ValueKey('MESSAGE-${message.id}'), - builder: (_) => widget.messageBuilder( + return WidgetsVisibilityProvider( + condition: (c) => null, + child: Stack( + children: [ + NotificationListener( + onNotification: (_) { + if (_scrollController.offset < 150 && _newMessageList.isNotEmpty) { + setState(() { + _messages.insertAll(0, _newMessageList); + _newMessageList.clear(); + }); + } + return true; + }, + child: ListView.custom( + key: Key('messageListView'), + physics: widget.scrollPhysics, + keyboardDismissBehavior: widget.keyboardDismissBehavior, + controller: _scrollController, + reverse: true, + childrenDelegate: SliverChildBuilderDelegate( + (context, i) { + if (i == _messages.length + 1) { + if (widget.parentMessage != null) { + if (widget.parentMessageBuilder != null) { + return widget.parentMessageBuilder( context, - MessageDetails( - context, - message, - _messages, - i, - ), - _messages), + widget.parentMessage, + ); + } else { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + buildParentMessage(widget.parentMessage), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 32), + child: Container( + padding: const EdgeInsets.all(8), + child: Text( + 'Start of thread', + textAlign: TextAlign.center, + ), + color: + Theme.of(context).accentColor.withAlpha(50), + ), + ), + ], + ); + } + } else { + return SizedBox(); + } + } + + if (i == _messages.length) { + return _buildLoadingIndicator(streamChannel); + } + final message = _messages[i]; + final nextMessage = i > 0 ? _messages[i - 1] : null; + + Widget messageWidget; + + if (i == 0) { + messageWidget = _buildBottomMessage( + context, + message, + _messages, + streamChannel, + ); + } else if (i == _messages.length - 1) { + messageWidget = _buildTopMessage( + context, + message, + _messages, + streamChannel, ); } else { - messageWidget = buildMessage(message, _messages, i); + if (widget.messageBuilder != null) { + messageWidget = Builder( + key: ValueKey('MESSAGE-${message.id}'), + builder: (_) => widget.messageBuilder( + context, + MessageDetails( + context, + message, + _messages, + i, + ), + _messages), + ); + } else { + messageWidget = buildMessage(message, _messages, i); + } } - } - if (nextMessage != null && - !Jiffy(message.createdAt.toLocal()) - .isSame(nextMessage.createdAt.toLocal(), Units.DAY)) { - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - messageWidget, - Padding( - padding: const EdgeInsets.symmetric(vertical: 12.0), - child: widget.dateDividerBuilder != null - ? widget.dateDividerBuilder( - nextMessage.createdAt.toLocal()) - : DateDivider( - dateTime: nextMessage.createdAt.toLocal(), - ), - ), - ], - ); - } + if (nextMessage != null && + !Jiffy(message.createdAt.toLocal()) + .isSame(nextMessage.createdAt.toLocal(), Units.DAY)) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + messageWidget, + Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: widget.dateDividerBuilder != null + ? widget.dateDividerBuilder( + nextMessage.createdAt.toLocal()) + : DateDivider( + dateTime: nextMessage.createdAt.toLocal(), + ), + ), + ], + ); + } - return messageWidget; - }, - childCount: _messages.length + 2, - findChildIndexCallback: (key) { - final ValueKey valueKey = key; - final index = _messages - .indexWhere((m) => 'MESSAGE-${m.id}' == valueKey.value); - return index != -1 ? index : null; - }, + return VisibleNotifierWidget(child: messageWidget); + }, + childCount: _messages.length + 2, + findChildIndexCallback: (key) { + final ValueKey valueKey = key; + final index = _messages + .indexWhere((m) => 'MESSAGE-${m.id}' == valueKey.value); + return index != -1 ? index : null; + }, + ), ), ), - ), - if (widget.showScrollToBottom) - StreamBuilder( - stream: streamChannel.channel.on( - EventType.messageNew, - ), - builder: (context, _) { - if (!_showScrollToBottom || - streamChannel.channel.state.unreadCount == 0) { - return SizedBox(); - } - return _buildScrollToBottom(streamChannel); - }), - ], + if (widget.showScrollToBottom) + StreamBuilder( + stream: streamChannel.channel.on( + EventType.messageNew, + ), + builder: (context, _) { + if (!_showScrollToBottom || + streamChannel.channel.state.unreadCount == 0) { + return SizedBox(); + } + return _buildScrollToBottom(streamChannel); + }), + Positioned( + top: 20.0, + child: WidgetsVisibilityListener( + listener: (context, event) { + setState(() { + positionData = event.positionDataList.first.startPosition; + }); + }, + child: Chip( + label: Text(positionData.toString()), + ), + ), + ), + ], + ), ); } diff --git a/pubspec.yaml b/pubspec.yaml index 4ae31b50..26144e64 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,6 +30,7 @@ dependencies: flutter_slidable: ^0.5.4 carousel_slider: ^2.2.1 clipboard: ^0.1.2+8 + widgets_visibility_provider: ^2.0.2 flutter: fonts: From d93dfb5af4014cac52f6433bf5edd2b36bfdbba9 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 27 Oct 2020 13:37:00 +0530 Subject: [PATCH 2/8] feat: Added sticky header to display position data --- lib/src/message_list_view.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/src/message_list_view.dart b/lib/src/message_list_view.dart index 42b434fd..0eaab75b 100644 --- a/lib/src/message_list_view.dart +++ b/lib/src/message_list_view.dart @@ -160,6 +160,7 @@ class _MessageListViewState extends State { return WidgetsVisibilityProvider( condition: (c) => null, child: Stack( + alignment: Alignment.center, children: [ NotificationListener( onNotification: (_) { @@ -301,13 +302,13 @@ class _MessageListViewState extends State { child: WidgetsVisibilityListener( listener: (context, event) { setState(() { - positionData = event.positionDataList.first.startPosition; + positionData = event.positionDataList.isNotEmpty ? event.positionDataList[0] : null; }); }, - child: Chip( - label: Text(positionData.toString()), + child: DateDivider( + dateTime: _messages[positionData].createdAt.toLocal(), + ), ), - ), ), ], ), From 6de4ee9963e2d447e7c8f9647271b60f6da5f642 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 27 Oct 2020 13:50:16 +0530 Subject: [PATCH 3/8] feat: Added sticky header to display position data --- lib/src/message_list_view.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/message_list_view.dart b/lib/src/message_list_view.dart index 0eaab75b..83fbd75f 100644 --- a/lib/src/message_list_view.dart +++ b/lib/src/message_list_view.dart @@ -273,7 +273,7 @@ class _MessageListViewState extends State { ); } - return VisibleNotifierWidget(child: messageWidget); + return VisibleNotifierWidget(child: messageWidget, data: i,); }, childCount: _messages.length + 2, findChildIndexCallback: (key) { @@ -297,12 +297,13 @@ class _MessageListViewState extends State { } return _buildScrollToBottom(streamChannel); }), + if(positionData != null) Positioned( top: 20.0, child: WidgetsVisibilityListener( listener: (context, event) { setState(() { - positionData = event.positionDataList.isNotEmpty ? event.positionDataList[0] : null; + positionData = event.positionDataList.isNotEmpty ? event.positionDataList[0].data : null; }); }, child: DateDivider( From 1cb007ec2da153eebbe15fe562696900c8c69665 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 27 Oct 2020 14:14:00 +0530 Subject: [PATCH 4/8] feat: Added sticky header to display position data --- lib/src/message_list_view.dart | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/lib/src/message_list_view.dart b/lib/src/message_list_view.dart index 83fbd75f..e3502539 100644 --- a/lib/src/message_list_view.dart +++ b/lib/src/message_list_view.dart @@ -150,7 +150,6 @@ class _MessageListViewState extends State { List _newMessageList = []; Function _onThreadTap; bool _showScrollToBottom = false; - var positionData; @override Widget build(BuildContext context) { @@ -164,7 +163,8 @@ class _MessageListViewState extends State { children: [ NotificationListener( onNotification: (_) { - if (_scrollController.offset < 150 && _newMessageList.isNotEmpty) { + if (_scrollController.offset < 150 && + _newMessageList.isNotEmpty) { setState(() { _messages.insertAll(0, _newMessageList); _newMessageList.clear(); @@ -193,7 +193,8 @@ class _MessageListViewState extends State { children: [ buildParentMessage(widget.parentMessage), Padding( - padding: const EdgeInsets.symmetric(horizontal: 32), + padding: + const EdgeInsets.symmetric(horizontal: 32), child: Container( padding: const EdgeInsets.all(8), child: Text( @@ -273,7 +274,10 @@ class _MessageListViewState extends State { ); } - return VisibleNotifierWidget(child: messageWidget, data: i,); + return VisibleNotifierWidget( + child: messageWidget, + data: i, + ); }, childCount: _messages.length + 2, findChildIndexCallback: (key) { @@ -297,19 +301,19 @@ class _MessageListViewState extends State { } return _buildScrollToBottom(streamChannel); }), - if(positionData != null) Positioned( top: 20.0, - child: WidgetsVisibilityListener( - listener: (context, event) { - setState(() { - positionData = event.positionDataList.isNotEmpty ? event.positionDataList[0].data : null; - }); - }, - child: DateDivider( - dateTime: _messages[positionData].createdAt.toLocal(), - ), - ), + child: WidgetsVisibilityConsumer( + builder: (context, event) { + if(event.positionDataList == null || event.positionDataList.isEmpty) { + return Container(); + } + + return DateDivider( + dateTime: _messages[event.positionDataList[0].data].createdAt.toLocal(), + ); + }, + ), ), ], ), From 42d758264af3a53ebf60b2b4b89b65e4c4836399 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Tue, 27 Oct 2020 14:27:56 +0530 Subject: [PATCH 5/8] fix: Corrected date header --- lib/src/message_list_view.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/message_list_view.dart b/lib/src/message_list_view.dart index e3502539..e833f2e1 100644 --- a/lib/src/message_list_view.dart +++ b/lib/src/message_list_view.dart @@ -304,13 +304,14 @@ class _MessageListViewState extends State { Positioned( top: 20.0, child: WidgetsVisibilityConsumer( + listener: (context, event,) {}, builder: (context, event) { if(event.positionDataList == null || event.positionDataList.isEmpty) { return Container(); } return DateDivider( - dateTime: _messages[event.positionDataList[0].data].createdAt.toLocal(), + dateTime: _messages[event.positionDataList.last.data].createdAt.toLocal(), ); }, ), From b22cf7bdf36f88aeb2b07cf483c973cdc1f4f1a1 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Wed, 28 Oct 2020 13:24:41 +0530 Subject: [PATCH 6/8] fix: Fixed Visiblity --- lib/src/message_list_view.dart | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/src/message_list_view.dart b/lib/src/message_list_view.dart index e833f2e1..da260b0e 100644 --- a/lib/src/message_list_view.dart +++ b/lib/src/message_list_view.dart @@ -257,20 +257,23 @@ class _MessageListViewState extends State { if (nextMessage != null && !Jiffy(message.createdAt.toLocal()) .isSame(nextMessage.createdAt.toLocal(), Units.DAY)) { - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - messageWidget, - Padding( - padding: const EdgeInsets.symmetric(vertical: 12.0), - child: widget.dateDividerBuilder != null - ? widget.dateDividerBuilder( - nextMessage.createdAt.toLocal()) - : DateDivider( - dateTime: nextMessage.createdAt.toLocal(), - ), - ), - ], + return VisibleNotifierWidget( + data: i, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + messageWidget, + Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: widget.dateDividerBuilder != null + ? widget.dateDividerBuilder( + nextMessage.createdAt.toLocal()) + : DateDivider( + dateTime: nextMessage.createdAt.toLocal(), + ), + ), + ], + ), ); } From bb46710f2416b5409160ee56daf470b35b3140e6 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 28 Oct 2020 11:42:02 +0100 Subject: [PATCH 7/8] modify condition --- example/ios/Flutter/.last_build_id | 2 +- example/pubspec.yaml | 2 +- lib/src/message_list_view.dart | 46 +++++++++++++++++++++--------- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/example/ios/Flutter/.last_build_id b/example/ios/Flutter/.last_build_id index 171fde31..661f8167 100644 --- a/example/ios/Flutter/.last_build_id +++ b/example/ios/Flutter/.last_build_id @@ -1 +1 @@ -0289cdadbd29bab804f275e3c5907d07 \ No newline at end of file +89eb023669fee58472de747080d78b5a \ No newline at end of file diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 9a6554e6..cb0eca8e 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,6 +1,6 @@ name: example description: A new Flutter project. -version: 1.0.31+33 +version: 1.0.32+34 environment: sdk: ">=2.2.2 <3.0.0" diff --git a/lib/src/message_list_view.dart b/lib/src/message_list_view.dart index da260b0e..bb0f98c0 100644 --- a/lib/src/message_list_view.dart +++ b/lib/src/message_list_view.dart @@ -157,7 +157,9 @@ class _MessageListViewState extends State { /// TODO: find a better solution when (https://github.com/flutter/flutter/issues/21023) is fixed return WidgetsVisibilityProvider( - condition: (c) => null, + condition: (positionData) => + positionData.endPosition >= 20 && + positionData.startPosition <= positionData.viewportSize, child: Stack( alignment: Alignment.center, children: [ @@ -257,14 +259,24 @@ class _MessageListViewState extends State { if (nextMessage != null && !Jiffy(message.createdAt.toLocal()) .isSame(nextMessage.createdAt.toLocal(), Units.DAY)) { - return VisibleNotifierWidget( - data: i, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - messageWidget, - Padding( - padding: const EdgeInsets.symmetric(vertical: 12.0), + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + messageWidget, + Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: VisibleNotifierWidget( + condition: ( + ScrollNotification previousNotification, + PositionData previousPositionData, + ScrollNotification currentNotification, + PositionData currentPositionData, + ) { + print( + 'currentPositionData.endPosition: ${currentPositionData.endPosition}'); + return true; + }, + data: i, child: widget.dateDividerBuilder != null ? widget.dateDividerBuilder( nextMessage.createdAt.toLocal()) @@ -272,8 +284,8 @@ class _MessageListViewState extends State { dateTime: nextMessage.createdAt.toLocal(), ), ), - ], - ), + ), + ], ); } @@ -307,14 +319,20 @@ class _MessageListViewState extends State { Positioned( top: 20.0, child: WidgetsVisibilityConsumer( - listener: (context, event,) {}, + listener: ( + context, + event, + ) {}, builder: (context, event) { - if(event.positionDataList == null || event.positionDataList.isEmpty) { + if (event.positionDataList == null || + event.positionDataList.isEmpty) { return Container(); } return DateDivider( - dateTime: _messages[event.positionDataList.last.data].createdAt.toLocal(), + dateTime: _messages[event.positionDataList.last.data] + .createdAt + .toLocal(), ); }, ), From d5f23549f456b426ad4fe7b5fe5f2f665b2997d8 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 28 Oct 2020 11:42:09 +0100 Subject: [PATCH 8/8] modify condition --- lib/src/message_actions_modal.dart | 7 +++++-- lib/src/message_input.dart | 22 +++++++++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index 7ee4311c..ce680d34 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -218,13 +218,16 @@ class MessageActionsModal extends StatelessWidget { StreamIcons.edit, size: 22, color: - StreamChatTheme.of(context).primaryIconTheme.color, + StreamChatTheme.of(context).primaryIconTheme.color, ), onPressed: () {}, ), Text( 'Edit message', - style: Theme.of(context).textTheme.headline6.copyWith(fontWeight: FontWeight.bold), + style: Theme.of(context) + .textTheme + .headline6 + .copyWith(fontWeight: FontWeight.bold), ), IconButton( icon: Icon( diff --git a/lib/src/message_input.dart b/lib/src/message_input.dart index 30910924..d03a61bc 100644 --- a/lib/src/message_input.dart +++ b/lib/src/message_input.dart @@ -192,7 +192,7 @@ class MessageInputState extends State { padding: const EdgeInsets.all(8.0), child: _buildTextField(context), ), - if(widget.parentMessage != null) + if (widget.parentMessage != null) Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: _buildDmCheckbox(), @@ -222,7 +222,11 @@ class MessageInputState extends State { Widget _buildDmCheckbox() { return Row( children: [ - Checkbox(value: _sendAsDm, onChanged: (val) => setState(() {_sendAsDm = val;})), + Checkbox( + value: _sendAsDm, + onChanged: (val) => setState(() { + _sendAsDm = val; + })), Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Text('Send also as direct message'), @@ -316,9 +320,11 @@ class MessageInputState extends State { var matchedCommandsList = StreamChannel.of(context) .channel .config - .commands.where((element) => element.name == s.substring(1)).toList(); + .commands + .where((element) => element.name == s.substring(1)) + .toList(); - if(matchedCommandsList.length == 1) { + if (matchedCommandsList.length == 1) { _chosenCommand = matchedCommandsList[0]; textEditingController.clear(); _messageIsPresent = false; @@ -796,8 +802,8 @@ class MessageInputState extends State { return Center( child: InkWell( child: Padding( - padding: EdgeInsets.only( - left: 8.0, right: padding, top: 8.0, bottom: 8.0), + padding: + EdgeInsets.only(left: 8.0, right: padding, top: 8.0, bottom: 8.0), child: Icon(StreamIcons.attach), ), onTap: () { @@ -1053,7 +1059,9 @@ class MessageInputState extends State { child: Transform.rotate( angle: widget.editMessage == null ? -pi / 2 : 0, child: Icon( - widget.editMessage == null ? StreamIcons.send_message : StreamIcons.check_send, + widget.editMessage == null + ? StreamIcons.send_message + : StreamIcons.check_send, color: StreamChatTheme.of(context).accentColor, ), ),