From ded26db306532289498d9048473e0e6e74385721 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Fri, 27 Nov 2020 12:54:57 +0530 Subject: [PATCH 01/12] feat : MessageSearchListView Signed-off-by: xsahil03x --- example/lib/main.dart | 167 ++++++++++++-- lib/src/message_search_bloc.dart | 109 +++++++++ lib/src/message_search_item.dart | 98 +++++++++ lib/src/message_search_list_view.dart | 305 ++++++++++++++++++++++++++ lib/src/stream_chat_theme.dart | 1 + lib/stream_chat_flutter.dart | 3 + 6 files changed, 663 insertions(+), 20 deletions(-) create mode 100644 lib/src/message_search_bloc.dart create mode 100644 lib/src/message_search_item.dart create mode 100644 lib/src/message_search_list_view.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 50ff18b0..bab4b1e0 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -12,6 +12,8 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'notifications_service.dart'; import 'routes/app_routes.dart'; import 'routes/routes.dart'; +import 'search_text_field.dart'; +import 'dart:async'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -66,7 +68,45 @@ class MyApp extends StatelessWidget { } } -class ChannelListPage extends StatelessWidget { +class ChannelListPage extends StatefulWidget { + @override + _ChannelListPageState createState() => _ChannelListPageState(); +} + +class _ChannelListPageState extends State { + TextEditingController _controller; + + String _channelQuery = ''; + + bool _isSearchActive = false; + + Timer _debounce; + + void _channelQueryListener() { + if (_debounce?.isActive ?? false) _debounce.cancel(); + _debounce = Timer(const Duration(milliseconds: 350), () { + if (mounted) { + setState(() { + _channelQuery = _controller.text; + _isSearchActive = _channelQuery.isNotEmpty; + }); + } + }); + } + + @override + void initState() { + super.initState(); + _controller = TextEditingController()..addListener(_channelQueryListener); + } + + @override + void dispose() { + _controller?.removeListener(_channelQueryListener); + _controller?.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final user = StreamChat.of(context).user; @@ -79,26 +119,50 @@ class ChannelListPage extends StatelessWidget { drawer: _buildDrawer(context, user), drawerEdgeDragWidth: 50, body: ChannelsBloc( - child: ChannelListView( - onStartChatPressed: () { - Navigator.pushNamed(context, Routes.NEW_CHAT); - }, - swipeToAction: true, - filter: { - 'members': { - '\$in': [user.id], - }, - 'draft': { - r'$ne': true, - }, - }, - options: { - 'presence': true, - }, - pagination: PaginationParams( - limit: 20, + child: MessageSearchBloc( + child: Column( + children: [ + SearchTextField( + controller: _controller, + ), + Expanded( + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 350), + child: _isSearchActive + ? MessageSearchListView( + messageQuery: _channelQuery, + filters: { + 'members': { + r'$in': [user.id] + } + }, + paginationParams: PaginationParams(limit: 20), + ) + : ChannelListView( + onStartChatPressed: () { + Navigator.pushNamed(context, Routes.NEW_CHAT); + }, + swipeToAction: true, + filter: { + 'members': { + r'$in': [user.id], + }, + 'draft': { + r'$ne': true, + }, + }, + options: { + 'presence': true, + }, + pagination: PaginationParams( + limit: 20, + ), + channelWidget: ChannelPage(), + ), + ), + ), + ], ), - channelWidget: ChannelPage(), ), ), ); @@ -207,6 +271,68 @@ class ChannelListPage extends StatelessWidget { } } +class ChannelQuerySearchResultPage extends StatelessWidget { + final Stream> searchResultStream; + + const ChannelQuerySearchResultPage({ + Key key, + @required this.searchResultStream, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return StreamBuilder>( + initialData: const [], + stream: searchResultStream, + builder: (context, snapshot) { + final result = snapshot.data; + return Column( + children: [ + if (result.isNotEmpty) + Container( + width: double.maxFinite, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Colors.black.withOpacity(0.02), + Colors.white.withOpacity(0.05), + ], + stops: [0, 1], + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 8, + ), + child: Text( + '${result.length} results', + style: TextStyle( + color: Colors.black.withOpacity(0.5), + ), + ), + ), + ), + Expanded( + child: ListView.builder( + itemCount: result.length, + itemBuilder: (context, index) { + return ListTile( + leading: UserAvatar(), + title: Text(result[index].toJson().toString()), + ); + }, + ), + ), + ], + ); + }, + ); + } +} + class ChannelPage extends StatelessWidget { const ChannelPage({ Key key, @@ -215,6 +341,7 @@ class ChannelPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( + backgroundColor: Color.fromRGBO(252, 252, 252, 1), appBar: ChannelHeader( showTypingIndicator: false, ), diff --git a/lib/src/message_search_bloc.dart b/lib/src/message_search_bloc.dart new file mode 100644 index 00000000..bff2d68e --- /dev/null +++ b/lib/src/message_search_bloc.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; +import 'package:rxdart/rxdart.dart'; +import 'package:stream_chat/stream_chat.dart'; + +import 'stream_chat.dart'; + +/// Widget dedicated to the management of a message list with pagination +class MessageSearchBloc extends StatefulWidget { + /// The widget child + final Widget child; + + /// Instantiate a new MessageSearchBloc + const MessageSearchBloc({ + Key key, + @required this.child, + }) : super(key: key); + + @override + MessageSearchBlocState createState() => MessageSearchBlocState(); + + /// Use this method to get the current [MessageSearchBlocState] instance + static MessageSearchBlocState of(BuildContext context) { + MessageSearchBlocState state; + + state = context.findAncestorStateOfType(); + + if (state == null) { + throw Exception('You must have a MessageSearchBloc widget as ancestor'); + } + + return state; + } +} + +/// The current state of the [MessageSearchBloc] +class MessageSearchBlocState extends State + with AutomaticKeepAliveClientMixin { + /// The current messages list + List get messages => _messagesController.value; + + /// The current messages list as a stream + Stream> get messagesStream => _messagesController.stream; + + final BehaviorSubject> _messagesController = BehaviorSubject(); + + final BehaviorSubject _queryMessagesLoadingController = + BehaviorSubject.seeded(false); + + /// The stream notifying the state of queryUsers call + Stream get queryMessagesLoading => + _queryMessagesLoadingController.stream; + + /// Calls [Client.search] updating [queryMessagesLoading] stream + Future search({ + Map filter, + List sort, + String query, + PaginationParams pagination, + }) async { + final client = StreamChat.of(context).client; + + if (client.state?.user == null || + _queryMessagesLoadingController.value == true) { + return; + } + _queryMessagesLoadingController.add(true); + try { + final clear = pagination == null || + pagination.offset == null || + pagination.offset == 0; + + final oldMessages = List.from(messages ?? []); + + final messageResponse = await client.search( + filter, + sort, + query, + pagination, + ); + + if (clear) { + _messagesController.add(messageResponse.results); + } else { + final temp = oldMessages + messageResponse.results; + _messagesController.add(temp); + } + + _queryMessagesLoadingController.add(false); + } catch (err, stackTrace) { + _queryMessagesLoadingController.addError(err, stackTrace); + } + } + + @override + Widget build(BuildContext context) { + super.build(context); + return widget.child; + } + + @override + void dispose() { + _messagesController.close(); + _queryMessagesLoadingController.close(); + super.dispose(); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/src/message_search_item.dart b/lib/src/message_search_item.dart new file mode 100644 index 00000000..d97db708 --- /dev/null +++ b/lib/src/message_search_item.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.dart'; +import 'package:jiffy/jiffy.dart'; +import 'package:stream_chat/stream_chat.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; + +class MessageSearchItem extends StatelessWidget { + final Message message; + + const MessageSearchItem({ + Key key, + @required this.message, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final data = Message.fromJson(message.extraData['message']); + final user = data.user; + debugPrint(message.toJson().toString()); + return ListTile( + leading: UserAvatar( + user: user, + constraints: BoxConstraints.tightFor( + height: 40, + width: 40, + ), + ), + title: Text( + user.name, + style: StreamChatTheme.of(context).channelPreviewTheme.title, + ), + subtitle: Row( + children: [ + Expanded(child: _buildSubtitle(context, data)), + _buildDate(context, data), + ], + ), + ); + } + + Widget _buildDate(BuildContext context, Message message) { + final lastUpdatedAt = message.updatedAt; + String stringDate; + final now = DateTime.now(); + + if (now.year != lastUpdatedAt.year || + now.month != lastUpdatedAt.month || + now.day != lastUpdatedAt.day) { + stringDate = Jiffy(lastUpdatedAt.toLocal()).format('dd/MM/yyyy'); + } else { + stringDate = Jiffy(lastUpdatedAt.toLocal()).format('HH:mm'); + } + + return Text( + stringDate, + style: StreamChatTheme.of(context).channelPreviewTheme.lastMessageAt, + ); + } + + Widget _buildSubtitle(BuildContext context, Message message) { + if (message == null) { + return SizedBox(); + } + + var text = message.text; + if (message.isDeleted) { + text = 'This message was deleted.'; + } else if (message.attachments != null) { + final parts = [ + ...message.attachments.map((e) { + if (e.type == 'image') { + return '📷'; + } else if (e.type == 'video') { + return '🎬'; + } else if (e.type == 'giphy') { + return '[GIF]'; + } + return null; + }).where((e) => e != null), + message.text ?? '', + ]; + + text = parts.join(' '); + } + + return Text( + text, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith( + color: + StreamChatTheme.of(context).channelPreviewTheme.subtitle.color, + fontStyle: (message.isSystem || message.isDeleted) + ? FontStyle.italic + : FontStyle.normal, + ), + ); + } +} diff --git a/lib/src/message_search_list_view.dart b/lib/src/message_search_list_view.dart new file mode 100644 index 00000000..f317a677 --- /dev/null +++ b/lib/src/message_search_list_view.dart @@ -0,0 +1,305 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:stream_chat/stream_chat.dart'; +import 'package:stream_chat_flutter/src/message_search_item.dart'; + +import 'lazy_load_scroll_view.dart'; +import 'message_search_bloc.dart'; + +class MessageSearchListView extends StatefulWidget { + final String messageQuery; + final Map filters; + final List sortOptions; + final PaginationParams paginationParams; + final bool pullToRefresh; + + /// The builder used when the channel list is empty. + final WidgetBuilder emptyBuilder; + + /// The builder that will be used in case of error + final Widget Function(Error error) errorBuilder; + + /// Builder used to create a custom item separator + final IndexedWidgetBuilder separatorBuilder; + + const MessageSearchListView({ + Key key, + @required this.messageQuery, + @required this.filters, + this.sortOptions, + this.paginationParams, + this.emptyBuilder, + this.errorBuilder, + this.separatorBuilder, + this.pullToRefresh = true, + }) : super(key: key); + + @override + _MessageSearchListViewState createState() => _MessageSearchListViewState(); +} + +class _MessageSearchListViewState extends State { + @override + void initState() { + super.initState(); + final messageSearchBloc = MessageSearchBloc.of(context); + messageSearchBloc.search( + filter: widget.filters, + sort: widget.sortOptions, + query: widget.messageQuery, + pagination: widget.paginationParams, + ); + } + + @override + Widget build(BuildContext context) { + final messageSearchBloc = MessageSearchBloc.of(context); + + if (!widget.pullToRefresh) { + return _buildListView(messageSearchBloc); + } + + return RefreshIndicator( + onRefresh: () async { + return messageSearchBloc.search( + filter: widget.filters, + sort: widget.sortOptions, + query: widget.messageQuery, + pagination: widget.paginationParams, + ); + }, + child: _buildListView(messageSearchBloc), + ); + } + + Widget _separatorBuilder(BuildContext context, int index) { + return Container( + height: 1, + color: Theme.of(context).brightness == Brightness.dark + ? Colors.white.withOpacity(0.1) + : Colors.black.withOpacity(0.1), + ); + } + + Widget _listItemBuilder(BuildContext context, Message message) { + return MessageSearchItem(message: message); + } + + Widget _buildQueryProgressIndicator( + context, MessageSearchBlocState messageSearchBloc) { + return StreamBuilder( + stream: messageSearchBloc.queryMessagesLoading, + initialData: false, + builder: (context, snapshot) { + if (snapshot.hasError) { + return Container( + color: Color(0xffd0021B).withAlpha(26), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Center( + child: Text('Error loading messages'), + ), + ), + ); + } + return Container( + height: 100, + padding: EdgeInsets.all(32), + child: Center( + child: snapshot.data ? CircularProgressIndicator() : Container(), + ), + ); + }); + } + + Widget _buildListView(MessageSearchBlocState messageSearchBloc) { + return StreamBuilder>( + stream: messageSearchBloc.messagesStream, + builder: (context, snapshot) { + if (snapshot.hasError) { + if (snapshot.error is Error) { + print((snapshot.error as Error).stackTrace); + } + + if (widget.errorBuilder != null) { + return widget.errorBuilder(snapshot.error); + } + + var message = snapshot.error.toString(); + if (snapshot.error is DioError) { + final dioError = snapshot.error as DioError; + if (dioError.type == DioErrorType.RESPONSE) { + message = dioError.message; + } else { + message = 'Check your connection and retry'; + } + } + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text.rich( + TextSpan( + children: [ + WidgetSpan( + child: Padding( + padding: const EdgeInsets.only( + right: 2.0, + ), + child: Icon(Icons.error_outline), + ), + ), + TextSpan(text: 'Error loading messages'), + ], + ), + style: Theme.of(context).textTheme.headline6, + ), + Padding( + padding: const EdgeInsets.only( + top: 16.0, + ), + child: Text(message), + ), + FlatButton( + onPressed: () { + messageSearchBloc.search( + filter: widget.filters, + sort: widget.sortOptions, + query: widget.messageQuery, + pagination: widget.paginationParams, + ); + }, + child: Text('Retry'), + ), + ], + ), + ); + } + + if (!snapshot.hasData) { + return LayoutBuilder( + builder: (context, viewportConstraints) { + return SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: viewportConstraints.maxHeight, + ), + child: Center( + child: CircularProgressIndicator(), + ), + ), + ); + }, + ); + } + + final items = snapshot.data; + + if (items.isEmpty && widget.emptyBuilder != null) { + return widget.emptyBuilder(context); + } + + if (items.isEmpty && widget.emptyBuilder == null) { + return LayoutBuilder( + builder: (context, viewportConstraints) { + return SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: viewportConstraints.maxHeight, + ), + child: Center( + child: Text('There are no messages currently'), + ), + ), + ); + }, + ); + } + + Widget child; + child = LazyLoadScrollView( + onEndOfPage: () => messageSearchBloc.search( + filter: widget.filters, + sort: widget.sortOptions, + pagination: widget.paginationParams.copyWith( + offset: messageSearchBloc.messages?.length ?? 0, + ), + query: widget.messageQuery, + ), + child: ListView.separated( + physics: AlwaysScrollableScrollPhysics(), + itemCount: items.isNotEmpty ? items.length + 1 : items.length, + separatorBuilder: (_, index) { + if (widget.separatorBuilder != null) { + return widget.separatorBuilder(context, index); + } + return _separatorBuilder(context, index); + }, + itemBuilder: (context, index) { + if (index < items.length) { + return _listItemBuilder(context, items[index]); + } + return _buildQueryProgressIndicator(context, messageSearchBloc); + }, + ), + ); + + if (true) { + child = Column( + children: [ + Container( + width: double.maxFinite, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Colors.black.withOpacity(0.02), + Colors.white.withOpacity(0.05), + ], + stops: [0, 1], + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 8, + ), + child: Text( + '${items.length} results', + style: TextStyle( + color: Colors.black.withOpacity(0.5), + ), + ), + ), + ), + Expanded(child: child), + ], + ); + } + return child; + }, + ); + } + + @override + void didUpdateWidget(MessageSearchListView oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.filters?.toString() != oldWidget.filters?.toString() || + jsonEncode(widget.sortOptions) != jsonEncode(oldWidget.sortOptions) || + widget.paginationParams?.toJson()?.toString() != + oldWidget.paginationParams?.toJson()?.toString() || + widget.messageQuery?.toString() != oldWidget.messageQuery?.toString()) { + final messageSearchBloc = MessageSearchBloc.of(context); + messageSearchBloc.search( + filter: widget.filters, + sort: widget.sortOptions, + query: widget.messageQuery, + pagination: widget.paginationParams, + ); + } + } +} diff --git a/lib/src/stream_chat_theme.dart b/lib/src/stream_chat_theme.dart index d5b2c1c2..9c24525c 100644 --- a/lib/src/stream_chat_theme.dart +++ b/lib/src/stream_chat_theme.dart @@ -245,6 +245,7 @@ class StreamChatThemeData { title: TextStyle( fontSize: 14, color: isDark ? Colors.white : Colors.black, + fontWeight: FontWeight.bold ), subtitle: TextStyle( fontSize: 12.5, diff --git a/lib/stream_chat_flutter.dart b/lib/stream_chat_flutter.dart index f8ad25ea..35239cbd 100644 --- a/lib/stream_chat_flutter.dart +++ b/lib/stream_chat_flutter.dart @@ -38,3 +38,6 @@ export 'src/users_bloc.dart'; export 'src/users_bloc.dart'; export 'src/utils.dart'; export 'src/video_attachment.dart'; +export 'src/message_search_bloc.dart'; +export 'src/message_search_item.dart'; +export 'src/message_search_list_view.dart'; From c6552be54062b0fa0aa724b2056bd926404a754b Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Fri, 27 Nov 2020 13:42:10 +0530 Subject: [PATCH 02/12] add docComments Signed-off-by: xsahil03x --- lib/src/message_search_item.dart | 23 +++++++- lib/src/message_search_list_view.dart | 76 +++++++++++++++++++++------ 2 files changed, 82 insertions(+), 17 deletions(-) diff --git a/lib/src/message_search_item.dart b/lib/src/message_search_item.dart index d97db708..a89f2ee0 100644 --- a/lib/src/message_search_item.dart +++ b/lib/src/message_search_item.dart @@ -3,22 +3,40 @@ import 'package:jiffy/jiffy.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +/// It shows the current [Message] preview. +/// +/// Usually you don't use this widget as it's the default item used by [MessageSearchListView]. +/// +/// The widget renders the ui based on the first ancestor of type [StreamChatTheme]. +/// Modify it to change the widget appearance. class MessageSearchItem extends StatelessWidget { - final Message message; - + /// Instantiate a new MessageSearchItem const MessageSearchItem({ Key key, @required this.message, + this.onTap, + this.showOnlineStatus = true, }) : super(key: key); + /// [Message] displayed + final Message message; + + /// Function called when tapping this widget + final VoidCallback onTap; + + /// If true the [MessageSearchItem] will show the current online Status + final bool showOnlineStatus; + @override Widget build(BuildContext context) { final data = Message.fromJson(message.extraData['message']); final user = data.user; debugPrint(message.toJson().toString()); return ListTile( + onTap: onTap, leading: UserAvatar( user: user, + showOnlineStatus: showOnlineStatus, constraints: BoxConstraints.tightFor( height: 40, width: 40, @@ -31,6 +49,7 @@ class MessageSearchItem extends StatelessWidget { subtitle: Row( children: [ Expanded(child: _buildSubtitle(context, data)), + SizedBox(width: 16), _buildDate(context, data), ], ), diff --git a/lib/src/message_search_list_view.dart b/lib/src/message_search_list_view.dart index f317a677..9ad013db 100644 --- a/lib/src/message_search_list_view.dart +++ b/lib/src/message_search_list_view.dart @@ -7,22 +7,36 @@ import 'package:stream_chat_flutter/src/message_search_item.dart'; import 'lazy_load_scroll_view.dart'; import 'message_search_bloc.dart'; +/// +/// It shows the list of searched messages. +/// +/// ```dart +/// class MessageSearchPage extends StatelessWidget { +/// @override +/// Widget build(BuildContext context) { +/// return Scaffold( +/// body: MessageSearchListView( +/// messageQuery: _channelQuery, +/// filters: { +/// 'members': { +/// r'$in': [user.id] +/// } +/// }, +/// paginationParams: PaginationParams(limit: 20), +/// ), +/// ); +/// } +/// } +/// ``` +/// +/// +/// Make sure to have a [MessageSearchBloc] ancestor in order to provide the information about the messages. +/// The widget uses a [ListView.separated] to render the list of messages. +/// +/// The widget components render the ui based on the first ancestor of type [StreamChatTheme]. +/// Modify it to change the widget appearance. class MessageSearchListView extends StatefulWidget { - final String messageQuery; - final Map filters; - final List sortOptions; - final PaginationParams paginationParams; - final bool pullToRefresh; - - /// The builder used when the channel list is empty. - final WidgetBuilder emptyBuilder; - - /// The builder that will be used in case of error - final Widget Function(Error error) errorBuilder; - - /// Builder used to create a custom item separator - final IndexedWidgetBuilder separatorBuilder; - + /// Instantiate a new MessageSearchListView const MessageSearchListView({ Key key, @required this.messageQuery, @@ -35,6 +49,38 @@ class MessageSearchListView extends StatefulWidget { this.pullToRefresh = true, }) : super(key: key); + /// Message String to search on + final String messageQuery; + + /// The query filters to use. + /// You can query on any of the custom fields you've defined on the [Channel]. + /// You can also filter other built-in channel fields. + final Map filters; + + /// The sorting used for the channels matching the filters. + /// Sorting is based on field and direction, multiple sorting options can be provided. + /// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count. + /// Direction can be ascending or descending. + final List sortOptions; + + /// Pagination parameters + /// limit: the number of users to return (max is 30) + /// offset: the offset (max is 1000) + /// message_limit: how many messages should be included to each channel + final PaginationParams paginationParams; + + /// Set it to false to disable the pull-to-refresh widget + final bool pullToRefresh; + + /// The builder used when the channel list is empty. + final WidgetBuilder emptyBuilder; + + /// The builder that will be used in case of error + final Widget Function(Error error) errorBuilder; + + /// Builder used to create a custom item separator + final IndexedWidgetBuilder separatorBuilder; + @override _MessageSearchListViewState createState() => _MessageSearchListViewState(); } From ad37e1b46542b3485ad578bb7dde60996137f925 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Fri, 27 Nov 2020 13:44:04 +0530 Subject: [PATCH 03/12] [UserListView] fix docComments Signed-off-by: xsahil03x --- lib/src/user_list_view.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/src/user_list_view.dart b/lib/src/user_list_view.dart index 569eb38a..cc763971 100644 --- a/lib/src/user_list_view.dart +++ b/lib/src/user_list_view.dart @@ -6,7 +6,6 @@ import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart'; import 'package:stream_chat_flutter/src/users_bloc.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'stream_chat.dart'; import 'user_item.dart'; /// Callback called when tapping on a user @@ -41,8 +40,8 @@ typedef UserItemBuilder = Widget Function(BuildContext, User, bool); /// ``` /// /// -/// Make sure to have a [StreamChat] ancestor in order to provide the information about the channels. -/// The widget uses a [ListView.custom] to render the list of channels. +/// Make sure to have a [UsersBloc] ancestor in order to provide the information about the users. +/// The widget uses a [ListView.separated], [GridView.builder] to render the list, grid of channels. /// /// The widget components render the ui based on the first ancestor of type [StreamChatTheme]. /// Modify it to change the widget appearance. @@ -321,7 +320,6 @@ class _UserListViewState extends State final child = _isListView ? ListView.separated( physics: AlwaysScrollableScrollPhysics(), - // controller: _scrollController, itemCount: items.isNotEmpty ? items.length + 1 : items.length, separatorBuilder: (_, index) { if (widget.separatorBuilder != null) { From 5867dc3317a99c9d30a674ad3411df1be02cec26 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Fri, 27 Nov 2020 13:45:28 +0530 Subject: [PATCH 04/12] [UserListView] remove redundant property Signed-off-by: xsahil03x --- lib/src/user_list_view.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/src/user_list_view.dart b/lib/src/user_list_view.dart index cc763971..b57e2d55 100644 --- a/lib/src/user_list_view.dart +++ b/lib/src/user_list_view.dart @@ -62,7 +62,6 @@ class UserListView extends StatefulWidget { this.separatorBuilder, this.onImageTap, this.selectedUsers, - this.swipeToAction = false, this.pullToRefresh = true, this.groupAlphabetically = false, this.crossAxisCount = 1, @@ -75,9 +74,6 @@ class UserListView extends StatefulWidget { /// The builder that will be used in case of error final Widget Function(Error error) errorBuilder; - /// If true a default swipe to action behaviour will be added to this widget - final bool swipeToAction; - /// The builder used when the channel list is empty. final WidgetBuilder emptyBuilder; From 4df3bf161034e92a0108585513d7b696fef8b272 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Fri, 27 Nov 2020 13:58:33 +0530 Subject: [PATCH 05/12] minor changes Signed-off-by: xsahil03x --- lib/src/message_search_list_view.dart | 35 ++++++++++++--------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/lib/src/message_search_list_view.dart b/lib/src/message_search_list_view.dart index 9ad013db..285e452b 100644 --- a/lib/src/message_search_list_view.dart +++ b/lib/src/message_search_list_view.dart @@ -7,6 +7,9 @@ import 'package:stream_chat_flutter/src/message_search_item.dart'; import 'lazy_load_scroll_view.dart'; import 'message_search_bloc.dart'; +/// Builder used to create a custom [ListUserItem] from a [User] +typedef MessageSearchItemBuilder = Widget Function(BuildContext, Message); + /// /// It shows the list of searched messages. /// @@ -46,7 +49,8 @@ class MessageSearchListView extends StatefulWidget { this.emptyBuilder, this.errorBuilder, this.separatorBuilder, - this.pullToRefresh = true, + this.itemBuilder, + this.showResultCount = true, }) : super(key: key); /// Message String to search on @@ -69,8 +73,8 @@ class MessageSearchListView extends StatefulWidget { /// message_limit: how many messages should be included to each channel final PaginationParams paginationParams; - /// Set it to false to disable the pull-to-refresh widget - final bool pullToRefresh; + /// Builder used to create a custom item preview + final MessageSearchItemBuilder itemBuilder; /// The builder used when the channel list is empty. final WidgetBuilder emptyBuilder; @@ -81,6 +85,9 @@ class MessageSearchListView extends StatefulWidget { /// Builder used to create a custom item separator final IndexedWidgetBuilder separatorBuilder; + /// Set it to false to hide total results text + final bool showResultCount; + @override _MessageSearchListViewState createState() => _MessageSearchListViewState(); } @@ -101,22 +108,7 @@ class _MessageSearchListViewState extends State { @override Widget build(BuildContext context) { final messageSearchBloc = MessageSearchBloc.of(context); - - if (!widget.pullToRefresh) { - return _buildListView(messageSearchBloc); - } - - return RefreshIndicator( - onRefresh: () async { - return messageSearchBloc.search( - filter: widget.filters, - sort: widget.sortOptions, - query: widget.messageQuery, - pagination: widget.paginationParams, - ); - }, - child: _buildListView(messageSearchBloc), - ); + return _buildListView(messageSearchBloc); } Widget _separatorBuilder(BuildContext context, int index) { @@ -129,6 +121,9 @@ class _MessageSearchListViewState extends State { } Widget _listItemBuilder(BuildContext context, Message message) { + if (widget.itemBuilder != null) { + return widget.itemBuilder(context, message); + } return MessageSearchItem(message: message); } @@ -293,7 +288,7 @@ class _MessageSearchListViewState extends State { ), ); - if (true) { + if (widget.showResultCount) { child = Column( children: [ Container( From 15669e8dd8272eb29587ebe9475ecfed605e2bce Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Fri, 27 Nov 2020 14:02:21 +0530 Subject: [PATCH 06/12] [MessageSearchListView] Add itemTap property Signed-off-by: xsahil03x --- lib/src/message_search_list_view.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/src/message_search_list_view.dart b/lib/src/message_search_list_view.dart index 285e452b..d5a282a4 100644 --- a/lib/src/message_search_list_view.dart +++ b/lib/src/message_search_list_view.dart @@ -7,6 +7,9 @@ import 'package:stream_chat_flutter/src/message_search_item.dart'; import 'lazy_load_scroll_view.dart'; import 'message_search_bloc.dart'; +/// Callback called when tapping on a user +typedef MessageSearchItemTapCallback = void Function(Message); + /// Builder used to create a custom [ListUserItem] from a [User] typedef MessageSearchItemBuilder = Widget Function(BuildContext, Message); @@ -50,6 +53,7 @@ class MessageSearchListView extends StatefulWidget { this.errorBuilder, this.separatorBuilder, this.itemBuilder, + this.onItemTap, this.showResultCount = true, }) : super(key: key); @@ -76,6 +80,9 @@ class MessageSearchListView extends StatefulWidget { /// Builder used to create a custom item preview final MessageSearchItemBuilder itemBuilder; + /// Function called when tapping on a [MessageSearchItem] + final MessageSearchItemTapCallback onItemTap; + /// The builder used when the channel list is empty. final WidgetBuilder emptyBuilder; @@ -124,7 +131,10 @@ class _MessageSearchListViewState extends State { if (widget.itemBuilder != null) { return widget.itemBuilder(context, message); } - return MessageSearchItem(message: message); + return MessageSearchItem( + message: message, + onTap: () => widget.onItemTap(message), + ); } Widget _buildQueryProgressIndicator( From 1ef7511f624dbc597297201b4cac2de17283f07c Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Fri, 27 Nov 2020 14:02:59 +0530 Subject: [PATCH 07/12] minor changes Signed-off-by: xsahil03x --- example/lib/main.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/example/lib/main.dart b/example/lib/main.dart index bab4b1e0..5804ecb2 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -137,6 +137,7 @@ class _ChannelListPageState extends State { } }, paginationParams: PaginationParams(limit: 20), + onItemTap: (message) {}, ) : ChannelListView( onStartChatPressed: () { From d5e8ee20713f4d0fba7f78ba969489af443da28b Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Fri, 27 Nov 2020 14:08:41 +0530 Subject: [PATCH 08/12] close keyboard on listView scroll Signed-off-by: xsahil03x --- example/lib/main.dart | 62 +++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 5804ecb2..3ba3e93c 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -128,38 +128,42 @@ class _ChannelListPageState extends State { Expanded( child: AnimatedSwitcher( duration: const Duration(milliseconds: 350), - child: _isSearchActive - ? MessageSearchListView( - messageQuery: _channelQuery, - filters: { - 'members': { - r'$in': [user.id] - } - }, - paginationParams: PaginationParams(limit: 20), - onItemTap: (message) {}, - ) - : ChannelListView( - onStartChatPressed: () { - Navigator.pushNamed(context, Routes.NEW_CHAT); - }, - swipeToAction: true, - filter: { - 'members': { - r'$in': [user.id], + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onPanDown: (_) => FocusScope.of(context).unfocus(), + child: _isSearchActive + ? MessageSearchListView( + messageQuery: _channelQuery, + filters: { + 'members': { + r'$in': [user.id] + } }, - 'draft': { - r'$ne': true, + paginationParams: PaginationParams(limit: 20), + onItemTap: (message) {}, + ) + : ChannelListView( + onStartChatPressed: () { + Navigator.pushNamed(context, Routes.NEW_CHAT); }, - }, - options: { - 'presence': true, - }, - pagination: PaginationParams( - limit: 20, + swipeToAction: true, + filter: { + 'members': { + r'$in': [user.id], + }, + 'draft': { + r'$ne': true, + }, + }, + options: { + 'presence': true, + }, + pagination: PaginationParams( + limit: 20, + ), + channelWidget: ChannelPage(), ), - channelWidget: ChannelPage(), - ), + ), ), ), ], From e20ed0f012d7cc5974af961d5624526e0164b5da Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Fri, 27 Nov 2020 14:09:45 +0530 Subject: [PATCH 09/12] dartfmt Signed-off-by: xsahil03x --- lib/src/stream_chat_theme.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/src/stream_chat_theme.dart b/lib/src/stream_chat_theme.dart index 9c24525c..54b096d1 100644 --- a/lib/src/stream_chat_theme.dart +++ b/lib/src/stream_chat_theme.dart @@ -243,10 +243,9 @@ class StreamChatThemeData { ), ), title: TextStyle( - fontSize: 14, - color: isDark ? Colors.white : Colors.black, - fontWeight: FontWeight.bold - ), + fontSize: 14, + color: isDark ? Colors.white : Colors.black, + fontWeight: FontWeight.bold), subtitle: TextStyle( fontSize: 12.5, color: (isDark ? Colors.white : Colors.black).withOpacity(0.5), From 78be96eab06944704c6c5256731a808ecc21bcaa Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Fri, 27 Nov 2020 12:49:17 +0100 Subject: [PATCH 10/12] use correct response model --- .gitignore | 3 +- example/lib/main.dart | 6 ++ example/lib/search_text_field.dart | 2 +- example/pubspec.yaml | 2 +- lib/src/channel_bottom_sheet.dart | 9 ++- lib/src/channel_name.dart | 87 ++++++++++++++------------- lib/src/message_search_bloc.dart | 16 ++--- lib/src/message_search_item.dart | 42 ++++++++++--- lib/src/message_search_list_view.dart | 18 +++--- pubspec.yaml | 2 +- 10 files changed, 114 insertions(+), 73 deletions(-) diff --git a/.gitignore b/.gitignore index 3a158cd4..1d3bae26 100644 --- a/.gitignore +++ b/.gitignore @@ -61,4 +61,5 @@ doc/api/ fvm google-services.json -example/ios/dist \ No newline at end of file +example/ios/dist +.vscode/ \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index 3ba3e93c..26590237 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -139,6 +139,12 @@ class _ChannelListPageState extends State { r'$in': [user.id] } }, + sortOptions: [ + SortOption( + 'created_at', + direction: SortOption.ASC, + ), + ], paginationParams: PaginationParams(limit: 20), onItemTap: (message) {}, ) diff --git a/example/lib/search_text_field.dart b/example/lib/search_text_field.dart index f46a48b0..e32bb88b 100644 --- a/example/lib/search_text_field.dart +++ b/example/lib/search_text_field.dart @@ -78,7 +78,7 @@ class SearchTextField extends StatelessWidget { Future.microtask( () => [ controller.clear(), - onChanged(''), + if (onChanged != null) onChanged(''), ], ); } diff --git a/example/pubspec.yaml b/example/pubspec.yaml index e0be8fc9..b66e085b 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,6 +1,6 @@ name: example description: A new Flutter project. -version: 1.0.78+80 +version: 1.0.80+82 environment: sdk: ">=2.2.2 <3.0.0" diff --git a/lib/src/channel_bottom_sheet.dart b/lib/src/channel_bottom_sheet.dart index 4272b0f3..48ce8887 100644 --- a/lib/src/channel_bottom_sheet.dart +++ b/lib/src/channel_bottom_sheet.dart @@ -30,10 +30,13 @@ class ChannelBottomSheet extends StatelessWidget { vertical: 2.0, ), child: Center( - child: ChannelName( + child: StreamChannel( + showLoading: false, channel: channel, - textStyle: - StreamChatTheme.of(context).channelPreviewTheme.title, + child: ChannelName( + textStyle: + StreamChatTheme.of(context).channelPreviewTheme.title, + ), ), ), ), diff --git a/lib/src/channel_name.dart b/lib/src/channel_name.dart index 337321ed..bf3eff22 100644 --- a/lib/src/channel_name.dart +++ b/lib/src/channel_name.dart @@ -12,60 +12,65 @@ class ChannelName extends StatelessWidget { /// Instantiate a new ChannelName const ChannelName({ Key key, - this.channel, this.textStyle, }) : super(key: key); - /// The channel to show the name of - final Channel channel; - /// The style of the text displayed final TextStyle textStyle; @override Widget build(BuildContext context) { final client = StreamChat.of(context); - final channel = this.channel ?? StreamChannel.of(context).channel; + final channel = StreamChannel.of(context).channel; + + return StreamBuilder>( + stream: channel.extraDataStream, + initialData: channel.extraData, + builder: (context, snapshot) { + return _buildName(snapshot.data, channel.state.members, client); + }, + ); + } + + Widget _buildName( + Map extraData, + List members, + StreamChatState client, + ) { return LayoutBuilder( builder: (context, constraints) { - return StreamBuilder>( - stream: channel.extraDataStream, - initialData: channel.extraData, - builder: (context, snapshot) { - String title; - if (snapshot.data['name'] == null) { - final otherMembers = channel.state.members - .where((member) => member.userId != client.user.id); - if (otherMembers.isNotEmpty) { - final maxWidth = constraints.maxWidth; - final maxChars = maxWidth / textStyle.fontSize; - int currentChars = 0; - final currentMembers = []; - otherMembers.forEach((element) { - final newLength = currentChars + element.user.name.length; - if (newLength < maxChars) { - currentChars = newLength; - currentMembers.add(element); - } - }); - - final exceedingMembers = - otherMembers.length - currentMembers.length; - title = - '${currentMembers.map((e) => e.user.name).join(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}'; - } else { - title = channel.id; + String title; + if (extraData['name'] == null) { + final otherMembers = + members.where((member) => member.userId != client.user.id); + if (otherMembers.isNotEmpty) { + final maxWidth = constraints.maxWidth; + final maxChars = maxWidth / textStyle.fontSize; + var currentChars = 0; + final currentMembers = []; + otherMembers.forEach((element) { + final newLength = currentChars + element.user.name.length; + if (newLength < maxChars) { + currentChars = newLength; + currentMembers.add(element); } - } else { - title = snapshot.data['name']; - } + }); - return Text( - title, - style: textStyle, - overflow: TextOverflow.ellipsis, - ); - }, + final exceedingMembers = + otherMembers.length - currentMembers.length; + title = + '${currentMembers.map((e) => e.user.name).join(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}'; + } else { + title = 'No title'; + } + } else { + title = extraData['name']; + } + + return Text( + title, + style: textStyle, + overflow: TextOverflow.ellipsis, ); }, ); diff --git a/lib/src/message_search_bloc.dart b/lib/src/message_search_bloc.dart index bff2d68e..0128bb22 100644 --- a/lib/src/message_search_bloc.dart +++ b/lib/src/message_search_bloc.dart @@ -36,12 +36,14 @@ class MessageSearchBloc extends StatefulWidget { class MessageSearchBlocState extends State with AutomaticKeepAliveClientMixin { /// The current messages list - List get messages => _messagesController.value; + List get messageResponses => _messageResponses.value; /// The current messages list as a stream - Stream> get messagesStream => _messagesController.stream; + Stream> get messagesStream => + _messageResponses.stream; - final BehaviorSubject> _messagesController = BehaviorSubject(); + final BehaviorSubject> _messageResponses = + BehaviorSubject(); final BehaviorSubject _queryMessagesLoadingController = BehaviorSubject.seeded(false); @@ -69,7 +71,7 @@ class MessageSearchBlocState extends State pagination.offset == null || pagination.offset == 0; - final oldMessages = List.from(messages ?? []); + final oldMessages = List.from(messageResponses ?? []); final messageResponse = await client.search( filter, @@ -79,10 +81,10 @@ class MessageSearchBlocState extends State ); if (clear) { - _messagesController.add(messageResponse.results); + _messageResponses.add(messageResponse.results); } else { final temp = oldMessages + messageResponse.results; - _messagesController.add(temp); + _messageResponses.add(temp); } _queryMessagesLoadingController.add(false); @@ -99,7 +101,7 @@ class MessageSearchBlocState extends State @override void dispose() { - _messagesController.close(); + _messageResponses.close(); _queryMessagesLoadingController.close(); super.dispose(); } diff --git a/lib/src/message_search_item.dart b/lib/src/message_search_item.dart index a89f2ee0..8857fcb8 100644 --- a/lib/src/message_search_item.dart +++ b/lib/src/message_search_item.dart @@ -13,13 +13,13 @@ class MessageSearchItem extends StatelessWidget { /// Instantiate a new MessageSearchItem const MessageSearchItem({ Key key, - @required this.message, + @required this.getMessageResponse, this.onTap, this.showOnlineStatus = true, }) : super(key: key); /// [Message] displayed - final Message message; + final GetMessageResponse getMessageResponse; /// Function called when tapping this widget final VoidCallback onTap; @@ -29,9 +29,12 @@ class MessageSearchItem extends StatelessWidget { @override Widget build(BuildContext context) { - final data = Message.fromJson(message.extraData['message']); - final user = data.user; - debugPrint(message.toJson().toString()); + final message = getMessageResponse.message; + final channel = getMessageResponse.channel; + final channelName = channel.extraData['name']; + print('channel.extraData: ${channel.extraData}'); + print('channelName: ${channelName}'); + final user = message.user; return ListTile( onTap: onTap, leading: UserAvatar( @@ -42,15 +45,34 @@ class MessageSearchItem extends StatelessWidget { width: 40, ), ), - title: Text( - user.name, - style: StreamChatTheme.of(context).channelPreviewTheme.title, + title: Row( + children: [ + Text( + user.name, + style: StreamChatTheme.of(context).channelPreviewTheme.title, + ), + if (channelName != null) + Text( + ' in ', + style: StreamChatTheme.of(context) + .channelPreviewTheme + .title + .copyWith( + fontWeight: FontWeight.normal, + ), + ), + if (channelName != null) + Text( + channelName, + style: StreamChatTheme.of(context).channelPreviewTheme.title, + ), + ], ), subtitle: Row( children: [ - Expanded(child: _buildSubtitle(context, data)), + Expanded(child: _buildSubtitle(context, message)), SizedBox(width: 16), - _buildDate(context, data), + _buildDate(context, message), ], ), ); diff --git a/lib/src/message_search_list_view.dart b/lib/src/message_search_list_view.dart index d5a282a4..8b346570 100644 --- a/lib/src/message_search_list_view.dart +++ b/lib/src/message_search_list_view.dart @@ -8,10 +8,11 @@ import 'lazy_load_scroll_view.dart'; import 'message_search_bloc.dart'; /// Callback called when tapping on a user -typedef MessageSearchItemTapCallback = void Function(Message); +typedef MessageSearchItemTapCallback = void Function(GetMessageResponse); /// Builder used to create a custom [ListUserItem] from a [User] -typedef MessageSearchItemBuilder = Widget Function(BuildContext, Message); +typedef MessageSearchItemBuilder = Widget Function( + BuildContext, GetMessageResponse); /// /// It shows the list of searched messages. @@ -127,13 +128,14 @@ class _MessageSearchListViewState extends State { ); } - Widget _listItemBuilder(BuildContext context, Message message) { + Widget _listItemBuilder( + BuildContext context, GetMessageResponse getMessageResponse) { if (widget.itemBuilder != null) { - return widget.itemBuilder(context, message); + return widget.itemBuilder(context, getMessageResponse); } return MessageSearchItem( - message: message, - onTap: () => widget.onItemTap(message), + getMessageResponse: getMessageResponse, + onTap: () => widget.onItemTap(getMessageResponse), ); } @@ -165,7 +167,7 @@ class _MessageSearchListViewState extends State { } Widget _buildListView(MessageSearchBlocState messageSearchBloc) { - return StreamBuilder>( + return StreamBuilder>( stream: messageSearchBloc.messagesStream, builder: (context, snapshot) { if (snapshot.hasError) { @@ -276,7 +278,7 @@ class _MessageSearchListViewState extends State { filter: widget.filters, sort: widget.sortOptions, pagination: widget.paginationParams.copyWith( - offset: messageSearchBloc.messages?.length ?? 0, + offset: messageSearchBloc.messageResponses?.length ?? 0, ), query: widget.messageQuery, ), diff --git a/pubspec.yaml b/pubspec.yaml index 5ecb60de..eee90a9c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -28,7 +28,7 @@ dependencies: file_picker: ^2.0.12 image_picker: ^0.6.7+2 flutter_keyboard_visibility: ^3.3.0 - stream_chat: ^0.2.13+1 + stream_chat: ^0.2.14 mime: ^0.9.6+3 video_compress: ^2.1.1 visibility_detector: ^0.1.5 From 3dd994d207107629aa12eb351fe328ef07ecc4eb Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Fri, 27 Nov 2020 12:54:29 +0100 Subject: [PATCH 11/12] fix name --- lib/src/message_search_item.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/src/message_search_item.dart b/lib/src/message_search_item.dart index 8857fcb8..9c7ea891 100644 --- a/lib/src/message_search_item.dart +++ b/lib/src/message_search_item.dart @@ -32,8 +32,6 @@ class MessageSearchItem extends StatelessWidget { final message = getMessageResponse.message; final channel = getMessageResponse.channel; final channelName = channel.extraData['name']; - print('channel.extraData: ${channel.extraData}'); - print('channelName: ${channelName}'); final user = message.user; return ListTile( onTap: onTap, @@ -48,7 +46,7 @@ class MessageSearchItem extends StatelessWidget { title: Row( children: [ Text( - user.name, + user.id == StreamChat.of(context).user.id ? 'You' : user.name, style: StreamChatTheme.of(context).channelPreviewTheme.title, ), if (channelName != null) From fa5b4ebab89742f38c212da362419c13134a6076 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Fri, 27 Nov 2020 17:32:08 +0530 Subject: [PATCH 12/12] minor changes Signed-off-by: xsahil03x --- lib/src/message_search_item.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/message_search_item.dart b/lib/src/message_search_item.dart index 9c7ea891..3e786b9a 100644 --- a/lib/src/message_search_item.dart +++ b/lib/src/message_search_item.dart @@ -49,7 +49,7 @@ class MessageSearchItem extends StatelessWidget { user.id == StreamChat.of(context).user.id ? 'You' : user.name, style: StreamChatTheme.of(context).channelPreviewTheme.title, ), - if (channelName != null) + if (channelName != null) ...[ Text( ' in ', style: StreamChatTheme.of(context) @@ -59,11 +59,11 @@ class MessageSearchItem extends StatelessWidget { fontWeight: FontWeight.normal, ), ), - if (channelName != null) Text( channelName, style: StreamChatTheme.of(context).channelPreviewTheme.title, ), + ], ], ), subtitle: Row(