From 160baa5d956a2d9b3c54b914609c8714d562a317 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Wed, 5 May 2021 15:53:08 +0530 Subject: [PATCH] lint changes mlv --- .../lib/src/message_list_view.dart | 410 +++++++++--------- 1 file changed, 211 insertions(+), 199 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view.dart index 5b3b0359..2800a041 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -12,34 +12,60 @@ import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/system_message.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:visibility_detector/visibility_detector.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; +import 'package:stream_chat_flutter/src/swipeable.dart'; -import '../stream_chat_flutter.dart'; -import 'connection_status_builder.dart'; -import 'date_divider.dart'; -import 'extension.dart'; -import 'swipeable.dart'; - +/// Widget builder for message typedef MessageBuilder = Widget Function( BuildContext, MessageDetails, List, ); + +/// Widget builder for parent message typedef ParentMessageBuilder = Widget Function( BuildContext, Message?, ); + +/// Widget builder for system message typedef SystemMessageBuilder = Widget Function( BuildContext, Message, ); + +/// Widget builder for thread typedef ThreadBuilder = Widget Function(BuildContext context, Message? parent); + +/// Callback for thread taps typedef ThreadTapCallback = void Function(Message, Widget?); +/// Callback on message swiped typedef OnMessageSwiped = void Function(Message); + +/// Callback on message tapped typedef OnMessageTap = void Function(Message); + +/// Callback on reply tapped typedef ReplyTapCallback = void Function(Message); +/// Class for message details class MessageDetails { + /// Constructor for creating [MessageDetails] + MessageDetails( + BuildContext context, + this.message, + List messages, + this.index, + ) { + isMyMessage = message.user?.id == StreamChat.of(context).user?.id; + isLastUser = index + 1 < messages.length && + message.user?.id == messages[index + 1].user?.id; + isNextUser = + index - 1 >= 0 && message.user!.id == messages[index - 1].user?.id; + } + /// True if the message belongs to the current user bool? isMyMessage; @@ -54,19 +80,6 @@ class MessageDetails { /// The index of the message int index; - - MessageDetails( - BuildContext context, - this.message, - List messages, - this.index, - ) { - isMyMessage = message.user?.id == StreamChat.of(context).user?.id; - isLastUser = index + 1 < messages.length && - message.user?.id == messages[index + 1].user?.id; - isNextUser = - index - 1 >= 0 && message.user!.id == messages[index - 1].user?.id; - } } /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_listview.png) @@ -104,10 +117,12 @@ class MessageDetails { /// ``` /// /// -/// Make sure to have a [StreamChannel] ancestor in order to provide the information about the channels. +/// Make sure to have a [StreamChannel] ancestor in order to +/// provide the information about the channels. /// The widget uses a [ListView.custom] to render the list of channels. /// -/// The widget components render the ui based on the first ancestor of type [StreamChatTheme]. +/// The widget components render the ui based on the first +/// ancestor of type [StreamChatTheme]. /// Modify it to change the widget appearance. class MessageListView extends StatefulWidget { /// Instantiate a new MessageListView @@ -158,10 +173,12 @@ class MessageListView extends StatefulWidget { final ThreadBuilder? threadBuilder; /// Function called when tapping on a thread - /// By default it calls [Navigator.push] using the widget built using [threadBuilder] + /// By default it calls [Navigator.push] using the widget + /// built using [threadBuilder] final ThreadTapCallback? onThreadTap; - /// If true will show a scroll to bottom message when there are new messages and the scroll offset is not zero + /// If true will show a scroll to bottom message when there are new + /// messages and the scroll offset is not zero final bool showScrollToBottom; /// Parent message in case of a thread @@ -201,8 +218,10 @@ class MessageListView extends StatefulWidget { /// Color used while highlighting initial message final Color? messageHighlightColor; + /// Callback when show message is tapped final ShowMessageCallback? onShowMessage; + /// Flag for showing tile on header final bool showConnectionStateTile; /// Function called when messages are fetched @@ -214,8 +233,10 @@ class MessageListView extends StatefulWidget { /// Function used to build an empty widget final WidgetBuilder? emptyBuilder; - /// Callback triggered when an error occurs while performing the given request. - /// This parameter can be used to display an error message to users in the event + /// Callback triggered when an error occurs while performing the + /// given request. + /// This parameter can be used to display an error message to + /// users in the event /// of a connection failure. final ErrorBuilder? errorWidgetBuilder; @@ -223,10 +244,12 @@ class MessageListView extends StatefulWidget { final bool Function(Message)? messageFilter; /// Attachment builders for the default message widget - /// Please change this in the [MessageWidget] if you are using a custom implementation + /// Please change this in the [MessageWidget] if you are using a + /// custom implementation final Map? customAttachmentBuilders; - /// Called when any message is tapped except a system message (use [onSystemMessageTap] instead) + /// Called when any message is tapped except a system message + /// (use [onSystemMessageTap] instead) final OnMessageTap? onMessageTap; /// Called when system message is tapped @@ -238,6 +261,7 @@ class MessageListView extends StatefulWidget { /// Customize the MessageWidget textBuilder final void Function(BuildContext context, Message message)? textBuilder; + /// Callback for when link is tapped final void Function(String link)? onLinkTap; @override @@ -257,9 +281,8 @@ class _MessageListViewState extends State { if (streamChannel!.initialMessageId != null) { final messages = streamChannel!.channel.state!.messages; final totalMessages = messages.length; - final messageIndex = messages.indexWhere((e) { - return e.id == streamChannel!.initialMessageId; - }); + final messageIndex = + messages.indexWhere((e) => e.id == streamChannel!.initialMessageId); final index = totalMessages - messageIndex; if (index != 0) return index - 1; return index; @@ -272,9 +295,7 @@ class _MessageListViewState extends State { return 0; } - bool _isInitialMessage(String id) { - return streamChannel!.initialMessageId == id; - } + bool _isInitialMessage(String id) => streamChannel!.initialMessageId == id; bool get _upToDate => streamChannel!.channel.state!.isUpToDate; @@ -295,50 +316,46 @@ class _MessageListViewState extends State { final MessageListController _messageListController = MessageListController(); @override - Widget build(BuildContext context) { - return MessageListCore( - messageFilter: widget.messageFilter, - loadingBuilder: widget.loadingBuilder ?? - (context) { - return Center( - child: const CircularProgressIndicator(), - ); - }, - emptyBuilder: widget.emptyBuilder ?? - (context) { - return Center( - child: Text( - 'No chats here yet...', - style: StreamChatTheme.of(context).textTheme.footnote.copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .black - .withOpacity(.5)), - ), - ); - }, - messageListBuilder: widget.messageListBuilder ?? - (context, list) { - return _buildListView(list); - }, - messageListController: _messageListController, - parentMessage: widget.parentMessage, - showScrollToBottom: widget.showScrollToBottom, - errorWidgetBuilder: widget.errorWidgetBuilder ?? - (BuildContext context, Object error) { - return Center( - child: Text( - 'Something went wrong', - style: StreamChatTheme.of(context).textTheme.footnote.copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .black - .withOpacity(.5)), - ), - ); - }, - ); - } + Widget build(BuildContext context) => MessageListCore( + messageFilter: widget.messageFilter, + loadingBuilder: widget.loadingBuilder ?? + (context) => const Center( + child: CircularProgressIndicator(), + ), + emptyBuilder: widget.emptyBuilder ?? + (context) => Center( + child: Text( + 'No chats here yet...', + style: StreamChatTheme.of(context) + .textTheme + .footnote + .copyWith( + color: StreamChatTheme.of(context) + .colorTheme + .black + .withOpacity(.5)), + ), + ), + messageListBuilder: widget.messageListBuilder ?? + (context, list) => _buildListView(list), + messageListController: _messageListController, + parentMessage: widget.parentMessage, + showScrollToBottom: widget.showScrollToBottom, + errorWidgetBuilder: widget.errorWidgetBuilder ?? + (BuildContext context, Object error) => Center( + child: Text( + 'Something went wrong', + style: StreamChatTheme.of(context) + .textTheme + .footnote + .copyWith( + color: StreamChatTheme.of(context) + .colorTheme + .black + .withOpacity(.5)), + ), + ), + ); Widget _buildListView(List data) { messages = data; @@ -384,6 +401,7 @@ class _MessageListViewState extends State { } return InfoTile( + // ignore: avoid_bool_literals_in_conditional_expressions showMessage: widget.showConnectionStateTile ? showStatus : false, tileAnchor: Alignment.topCenter, childAnchor: Alignment.topCenter, @@ -426,8 +444,8 @@ class _MessageListViewState extends State { itemCount: messages.length + 2 + (_isThreadConversation ? 1 : 0), separatorBuilder: (context, i) { - if (i == messages.length) return Offstage(); - if (i == 0) return SizedBox(height: 30); + if (i == messages.length) return const Offstage(); + if (i == 0) return const SizedBox(height: 30); if (i == messages.length + 1) { final replyCount = widget.parentMessage!.replyCount; return Container( @@ -438,6 +456,7 @@ class _MessageListViewState extends State { child: Padding( padding: const EdgeInsets.all(8), child: Text( + // ignore: lines_longer_than_80_chars '$replyCount ${replyCount == 1 ? 'Reply' : 'Replies'}', textAlign: TextAlign.center, style: StreamChatTheme.of(context) @@ -481,9 +500,9 @@ class _MessageListViewState extends State { !isNextUserSame || isThread || isDeleted) { - return SizedBox(height: 8); + return const SizedBox(height: 8); } - return SizedBox(height: 2); + return const SizedBox(height: 2); }, itemBuilder: (context, i) { if (i == messages.length + 2) { @@ -559,13 +578,13 @@ class _MessageListViewState extends State { builder: (context, values, _) { final items = _itemPositionListener.itemPositions.value; if (items.isEmpty || messages.isEmpty) { - return SizedBox(); + return const SizedBox(); } var index = _getTopElement(values).index; if (index > messages.length) { - return SizedBox(); + return const SizedBox(); } if (index == messages.length) { @@ -587,95 +606,90 @@ class _MessageListViewState extends State { } Future _paginateData( - StreamChannelState? channel, QueryDirection direction) { - return _messageListController.paginateData!(direction: direction); - } + StreamChannelState? channel, QueryDirection direction) => + _messageListController.paginateData!(direction: direction); - ItemPosition _getTopElement(Iterable values) { - return values - .where((ItemPosition position) => position.itemLeadingEdge < 0.9) - .reduce((ItemPosition max, ItemPosition position) => - position.itemLeadingEdge > max.itemLeadingEdge ? position : max); - } + ItemPosition _getTopElement(Iterable values) => values + .where((ItemPosition position) => position.itemLeadingEdge < 0.9) + .reduce((ItemPosition max, ItemPosition position) => + position.itemLeadingEdge > max.itemLeadingEdge ? position : max); - Widget _buildScrollToBottom() { - return StreamBuilder>( - stream: Rx.combineLatest2( - streamChannel!.channel.state!.isUpToDateStream, - streamChannel!.channel.state!.unreadCountStream, - (bool isUpToDate, int unreadCount) => Tuple2(isUpToDate, unreadCount), - ), - builder: (_, snapshot) { - if (snapshot.hasError) { - return Offstage(); - } else if (!snapshot.hasData) { - return Offstage(); - } - final isUpToDate = snapshot.data!.item1; - final showScrollToBottom = !isUpToDate || _showScrollToBottom; - if (!showScrollToBottom) { - return Offstage(); - } - final unreadCount = snapshot.data!.item2; - final showUnreadCount = unreadCount > 0 && - streamChannel!.channel.state!.members.any((e) => - e.userId == streamChannel!.channel.client.state.user!.id); - return Positioned( - bottom: 8, - right: 8, - width: 40, - height: 40, - child: Stack( - clipBehavior: Clip.none, - children: [ - FloatingActionButton( - backgroundColor: StreamChatTheme.of(context).colorTheme.white, - onPressed: () { - if (unreadCount > 0) { - streamChannel!.channel.markRead(); - } - if (!_upToDate) { - _bottomPaginationActive = false; - _topPaginationActive = false; - streamChannel!.reloadChannel(); - } else { - setState(() => _showScrollToBottom = false); - _scrollController!.scrollTo( - index: 0, - duration: Duration(seconds: 1), - curve: Curves.easeInOut, - ); - } - }, - child: StreamSvgIcon.down( - color: StreamChatTheme.of(context).colorTheme.black, + Widget _buildScrollToBottom() => StreamBuilder>( + stream: Rx.combineLatest2( + streamChannel!.channel.state!.isUpToDateStream, + streamChannel!.channel.state!.unreadCountStream, + (bool isUpToDate, int unreadCount) => Tuple2(isUpToDate, unreadCount), + ), + builder: (_, snapshot) { + if (snapshot.hasError) { + return const Offstage(); + } else if (!snapshot.hasData) { + return const Offstage(); + } + final isUpToDate = snapshot.data!.item1; + final showScrollToBottom = !isUpToDate || _showScrollToBottom; + if (!showScrollToBottom) { + return const Offstage(); + } + final unreadCount = snapshot.data!.item2; + final showUnreadCount = unreadCount > 0 && + streamChannel!.channel.state!.members.any((e) => + e.userId == streamChannel!.channel.client.state.user!.id); + return Positioned( + bottom: 8, + right: 8, + width: 40, + height: 40, + child: Stack( + clipBehavior: Clip.none, + children: [ + FloatingActionButton( + backgroundColor: StreamChatTheme.of(context).colorTheme.white, + onPressed: () { + if (unreadCount > 0) { + streamChannel!.channel.markRead(); + } + if (!_upToDate) { + _bottomPaginationActive = false; + _topPaginationActive = false; + streamChannel!.reloadChannel(); + } else { + setState(() => _showScrollToBottom = false); + _scrollController!.scrollTo( + index: 0, + duration: const Duration(seconds: 1), + curve: Curves.easeInOut, + ); + } + }, + child: StreamSvgIcon.down( + color: StreamChatTheme.of(context).colorTheme.black, + ), ), - ), - if (showUnreadCount) - Positioned( - width: 20, - height: 20, - left: 10, - top: -10, - child: CircleAvatar( - child: Padding( - padding: const EdgeInsets.all(3), - child: Text( - '$unreadCount', - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.bold, + if (showUnreadCount) + Positioned( + width: 20, + height: 20, + left: 10, + top: -10, + child: CircleAvatar( + child: Padding( + padding: const EdgeInsets.all(3), + child: Text( + '$unreadCount', + style: const TextStyle( + fontSize: 11, + fontWeight: FontWeight.bold, + ), ), ), ), ), - ), - ], - ), - ); - }, - ); - } + ], + ), + ); + }, + ); Widget _buildLoadingIndicator( StreamChannelState? streamChannel, @@ -685,7 +699,7 @@ class _MessageListViewState extends State { ? streamChannel!.queryTopMessages : streamChannel!.queryBottomMessages; return StreamBuilder( - key: Key('LOADING-INDICATOR'), + key: const Key('LOADING-INDICATOR'), stream: stream, initialData: false, builder: (context, snapshot) { @@ -695,24 +709,24 @@ class _MessageListViewState extends State { .colorTheme .accentRed .withOpacity(.2), - child: Center( + child: const Center( child: Text('Error loading messages'), ), ); } if (!snapshot.data!) { if (!_isThreadConversation && direction == QueryDirection.top) { - return Container( + return const SizedBox( height: 52, width: double.infinity, ); } - return Offstage(); + return const Offstage(); } - return Center( + return const Center( child: Padding( - padding: const EdgeInsets.all(8), - child: const CircularProgressIndicator(), + padding: EdgeInsets.all(8), + child: CircularProgressIndicator(), ), ); }, @@ -728,7 +742,7 @@ class _MessageListViewState extends State { Widget messageWidget; if (widget.messageBuilder != null) { messageWidget = Builder( - key: ValueKey('TOP-MESSAGE'), + key: const ValueKey('TOP-MESSAGE'), builder: (_) => widget.messageBuilder!( context, MessageDetails( @@ -810,7 +824,7 @@ class _MessageListViewState extends State { padding: const EdgeInsets.all(8), showSendingIndicator: false, onThreadTap: _onThreadTap as void Function(Message)?, - borderRadiusGeometry: BorderRadius.only( + borderRadiusGeometry: const BorderRadius.only( topLeft: Radius.circular(16), bottomLeft: Radius.circular(2), topRight: Radius.circular(16), @@ -886,8 +900,8 @@ class _MessageListViewState extends State { final channel = streamChannel!.channel; final readList = channel.state?.read?.where((read) { if (read.user.id == userId) return false; - return (read.lastRead.isAfter(message.createdAt) || - read.lastRead.isAtSameMomentAs(message.createdAt)); + return read.lastRead.isAfter(message.createdAt) || + read.lastRead.isAtSameMomentAs(message.createdAt); }).toList() ?? []; @@ -945,6 +959,7 @@ class _MessageListViewState extends State { showSendingIndicator: showSendingIndicator, showUserAvatar: showUserAvatar, onQuotedMessageTap: (quotedMessageId) async { + // ignore: prefer_function_declarations_over_variables final scrollToIndex = () { final index = messages.indexWhere((m) => m.id == quotedMessageId); _scrollController?.scrollTo( @@ -984,14 +999,14 @@ class _MessageListViewState extends State { ), attachmentPadding: EdgeInsets.all(hasFileAttachment ? 4 : 2), borderRadiusGeometry: BorderRadius.only( - topLeft: Radius.circular(16), + topLeft: const Radius.circular(16), bottomLeft: Radius.circular( (timeDiff >= 1 || !isNextUserSame) && !(hasReplies || isThreadMessage) ? 0 : 16, ), - topRight: Radius.circular(16), - bottomRight: Radius.circular(16), + topRight: const Radius.circular(16), + bottomRight: const Radius.circular(16), ), textPadding: EdgeInsets.symmetric( vertical: 8, @@ -1031,7 +1046,7 @@ class _MessageListViewState extends State { !message.isEphemeral && widget.onMessageSwiped != null) { child = Container( - decoration: BoxDecoration(), + decoration: const BoxDecoration(), clipBehavior: Clip.hardEdge, child: Swipeable( onSwipeEnd: () { @@ -1059,12 +1074,10 @@ class _MessageListViewState extends State { ), duration: const Duration(seconds: 3), onEnd: () => initialMessageHighlightComplete = true, - builder: (_, color, child) { - return Container( - color: color, - child: child, - ); - }, + builder: (_, color, child) => Container( + color: color, + child: child, + ), child: Padding( padding: const EdgeInsets.only(top: 4), child: child, @@ -1124,19 +1137,18 @@ class _MessageListViewState extends State { _onThreadTap = (Message message) { Navigator.push( context, - MaterialPageRoute(builder: (_) { - return StreamBuilder( - stream: streamChannel!.channel.state!.messagesStream.map( - (messages) => - messages!.firstWhere((m) => m.id == message.id)), - initialData: message, - builder: (_, snapshot) { - return StreamChannel( - channel: streamChannel!.channel, - child: widget.threadBuilder!(context, snapshot.data), - ); - }); - }), + MaterialPageRoute( + builder: (_) => StreamBuilder( + stream: streamChannel!.channel.state!.messagesStream.map( + (messages) => + messages!.firstWhere((m) => m.id == message.id)), + initialData: message, + builder: (_, snapshot) => StreamChannel( + channel: streamChannel!.channel, + child: widget.threadBuilder!(context, snapshot.data), + ), + ), + ), ); }; }