diff --git a/packages/stream_chat_flutter/lib/src/channel_list_view.dart b/packages/stream_chat_flutter/lib/src/channel_list_view.dart index 2da9b434..90b40be9 100644 --- a/packages/stream_chat_flutter/lib/src/channel_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/channel_list_view.dart @@ -67,8 +67,6 @@ class ChannelListView extends StatefulWidget { this.channelWidget, this.channelPreviewBuilder, this.separatorBuilder, - this.errorBuilder, - this.emptyBuilder, this.onImageTap, this.onStartChatPressed, this.swipeToAction = false, @@ -77,17 +75,15 @@ class ChannelListView extends StatefulWidget { this.padding, this.selectedChannels = const [], this.onViewInfoTap, + this.errorBuilder, + this.emptyBuilder, + this.loadingBuilder, + this.listBuilder, }) : super(key: key); - /// 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; - /// 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. @@ -147,6 +143,18 @@ class ChannelListView extends StatefulWidget { final ViewInfoCallback onViewInfoTap; + /// The builder that will be used in case of error + final ErrorBuilder errorBuilder; + + /// The builder that will be used in case of loading + final WidgetBuilder loadingBuilder; + + /// The builder which is used when list of channels loads + final Function(BuildContext, List) listBuilder; + + /// The builder used when the channel list is empty. + final WidgetBuilder emptyBuilder; + @override _ChannelListViewState createState() => _ChannelListViewState(); } @@ -161,18 +169,22 @@ class _ChannelListViewState extends State Widget build(BuildContext context) { var child = ChannelListCore( channelListController: _channelListController, - listBuilder: (context, list) { - return _buildListView(list); - }, - emptyBuilder: (BuildContext context) { - return _buildEmptyWidget(); - }, - errorBuilder: (BuildContext context, dynamic error) { - return _buildErrorWidget(context); - }, - loadingBuilder: (BuildContext context) { - return _buildLoadingWidget(); - }, + listBuilder: widget.listBuilder ?? + (context, list) { + return _buildListView(list); + }, + emptyBuilder: widget.emptyBuilder ?? + (BuildContext context) { + return _buildEmptyWidget(); + }, + errorBuilder: widget.errorBuilder ?? + (BuildContext context, dynamic error) { + return _buildErrorWidget(context); + }, + loadingBuilder: widget.loadingBuilder ?? + (BuildContext context) { + return _buildLoadingWidget(); + }, pagination: widget.pagination, options: widget.options, sort: widget.sort, @@ -236,10 +248,6 @@ class _ChannelListViewState extends State } Widget _buildEmptyWidget() { - if (widget.emptyBuilder != null) { - return widget.emptyBuilder(context); - } - return LayoutBuilder( builder: (context, viewportConstraints) { return SingleChildScrollView( 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 2929c2d7..eedf9689 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -126,6 +126,10 @@ class MessageListView extends StatefulWidget { this.messageHighlightColor, this.onShowMessage, this.showConnectionStateTile = false, + this.loadingBuilder, + this.emptyBuilder, + this.messageListBuilder, + this.errorWidgetBuilder, }) : super(key: key); /// Function used to build a custom message widget @@ -185,6 +189,20 @@ class MessageListView extends StatefulWidget { final bool showConnectionStateTile; + /// Function called when messages are fetched + final Widget Function(BuildContext, List) messageListBuilder; + + /// Function used to build a loading widget + final WidgetBuilder loadingBuilder; + + /// 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 + /// of a connection failure. + final ErrorBuilder errorWidgetBuilder; + @override _MessageListViewState createState() => _MessageListViewState(); } @@ -244,41 +262,45 @@ class _MessageListViewState extends State { @override Widget build(BuildContext context) { return MessageListCore( - loadingBuilder: (context) { - return Center( - child: const CircularProgressIndicator(), - ); - }, - 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: (context, list) { - return _buildListView(list); - }, + 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: (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)), - ), - ); - }, + 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)), + ), + ); + }, ); } diff --git a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart index 490b3581..94a79eaf 100644 --- a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart @@ -53,14 +53,16 @@ class MessageSearchListView extends StatefulWidget { this.sortOptions, this.paginationParams, this.messageFilters, - this.emptyBuilder, - this.errorBuilder, this.separatorBuilder, this.itemBuilder, this.onItemTap, this.showResultCount = true, this.pullToRefresh = true, this.showErrorTile = false, + this.emptyBuilder, + this.errorBuilder, + this.loadingBuilder, + this.childBuilder, }) : super(key: key); /// Message String to search on @@ -94,12 +96,6 @@ class MessageSearchListView extends StatefulWidget { /// Function called when tapping on a [MessageSearchItem] final MessageSearchItemTapCallback onItemTap; - /// The builder used when the channel list is empty. - final EmptyMessageSearchBuilder 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; @@ -111,6 +107,18 @@ class MessageSearchListView extends StatefulWidget { final bool showErrorTile; + /// The builder that is used when the search messages are fetched + final Widget Function(List) childBuilder; + + /// The builder used when the channel list is empty. + final WidgetBuilder emptyBuilder; + + /// The builder that will be used in case of error + final ErrorBuilder errorBuilder; + + /// The builder that will be used in case of loading + final WidgetBuilder loadingBuilder; + @override _MessageSearchListViewState createState() => _MessageSearchListViewState(); } @@ -128,101 +136,98 @@ class _MessageSearchListViewState extends State { paginationParams: widget.paginationParams, messageFilters: widget.messageFilters, messageSearchListController: _messageSearchListController, - emptyBuilder: (context) { - if (widget.emptyBuilder != null) { - return widget.emptyBuilder(context, widget.messageQuery); - } - 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'), - ), - ), - ); - }, - ); - }, - errorBuilder: (BuildContext context, dynamic error) { - if (error is Error) { - print((error).stackTrace); - } - - if (widget.errorBuilder != null) { - return widget.errorBuilder(error); - } - - var message = error.toString(); - if (error is DioError) { - if (error.type == DioErrorType.RESPONSE) { - message = error.message; - } else { - message = 'Check your connection and retry'; - } - } - return InfoTile( - showMessage: widget.showErrorTile, - tileAnchor: Alignment.topCenter, - childAnchor: Alignment.topCenter, - message: 'An error occurred.', - child: 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'), - ], + emptyBuilder: widget.emptyBuilder ?? + (context) { + 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'), + ), ), - style: Theme.of(context).textTheme.headline6, - ), - Padding( - padding: const EdgeInsets.only(top: 16.0), - child: Text(message), - ), - RaisedButton( - onPressed: () { - _messageSearchListController.loadData(); - }, - child: Text('Retry'), - ), - ], - ), - ), - ); - }, - loadingBuilder: (context) { - return LayoutBuilder( - builder: (context, viewportConstraints) { - return SingleChildScrollView( - physics: AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Center( - child: CircularProgressIndicator(), + ); + }, + ); + }, + errorBuilder: widget.emptyBuilder ?? + (BuildContext context, dynamic error) { + if (error is Error) { + print((error).stackTrace); + } + + var message = error.toString(); + if (error is DioError) { + if (error.type == DioErrorType.RESPONSE) { + message = error.message; + } else { + message = 'Check your connection and retry'; + } + } + return InfoTile( + showMessage: widget.showErrorTile, + tileAnchor: Alignment.topCenter, + childAnchor: Alignment.topCenter, + message: 'An error occurred.', + child: 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), + ), + RaisedButton( + onPressed: () { + _messageSearchListController.loadData(); + }, + child: Text('Retry'), + ), + ], ), ), ); }, - ); - }, - childBuilder: (list) { - return _buildListView(list); - }, + loadingBuilder: widget.loadingBuilder ?? + (context) { + return LayoutBuilder( + builder: (context, viewportConstraints) { + return SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: viewportConstraints.maxHeight, + ), + child: Center( + child: CircularProgressIndicator(), + ), + ), + ); + }, + ); + }, + childBuilder: widget.childBuilder ?? + (list) { + return _buildListView(list); + }, ); } diff --git a/packages/stream_chat_flutter/lib/src/user_list_view.dart b/packages/stream_chat_flutter/lib/src/user_list_view.dart index a1819592..4b50559b 100644 --- a/packages/stream_chat_flutter/lib/src/user_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/user_list_view.dart @@ -45,8 +45,6 @@ class UserListView extends StatefulWidget { /// Instantiate a new UserListView const UserListView({ Key key, - this.errorBuilder, - this.emptyBuilder, this.filter, this.options, this.sort, @@ -61,18 +59,16 @@ class UserListView extends StatefulWidget { this.pullToRefresh = true, this.groupAlphabetically = false, this.crossAxisCount = 1, + this.errorBuilder, + this.emptyBuilder, + this.loadingBuilder, + this.listBuilder, }) : assert( crossAxisCount == 1 || groupAlphabetically == false, 'Cannot group alphabetically when crossAxisCount > 1', ), super(key: key); - /// The builder that will be used in case of error - final Widget Function(Error error) errorBuilder; - - /// The builder used when the channel list is empty. - final WidgetBuilder emptyBuilder; - /// 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. @@ -130,6 +126,18 @@ class UserListView extends StatefulWidget { /// The number of children in the cross axis. final int crossAxisCount; + /// The builder that will be used in case of error + final Widget Function(Error error) errorBuilder; + + /// The builder that will be used to build the list + final Widget Function(BuildContext context, List users) listBuilder; + + /// The builder that will be used for loading + final WidgetBuilder loadingBuilder; + + /// The builder used when the channel list is empty. + final WidgetBuilder emptyBuilder; + @override _UserListViewState createState() => _UserListViewState(); } @@ -143,32 +151,36 @@ class _UserListViewState extends State @override Widget build(BuildContext context) { var child = UserListCore( - errorBuilder: (err) { - return _buildError(err); - }, - emptyBuilder: (context) { - return _buildEmpty(); - }, - loadingBuilder: (context) { - return LayoutBuilder( - builder: (context, viewportConstraints) { - return SingleChildScrollView( - physics: AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Center( - child: CircularProgressIndicator(), - ), - ), + errorBuilder: widget.errorBuilder ?? + (err) { + return _buildError(err); + }, + emptyBuilder: widget.emptyBuilder ?? + (context) { + return _buildEmpty(); + }, + loadingBuilder: widget.loadingBuilder ?? + (context) { + return LayoutBuilder( + builder: (context, viewportConstraints) { + return SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: viewportConstraints.maxHeight, + ), + child: Center( + child: CircularProgressIndicator(), + ), + ), + ); + }, ); }, - ); - }, - listBuilder: (context, list) { - return _buildListView(list); - }, + listBuilder: widget.listBuilder ?? + (context, list) { + return _buildListView(list); + }, pagination: widget.pagination, options: widget.options, sort: widget.sort, @@ -195,10 +207,6 @@ class _UserListViewState extends State Widget _buildError(Error error) { print((error).stackTrace); - if (widget.errorBuilder != null) { - return widget.errorBuilder(error); - } - var message = error.toString(); if (error is DioError) { final dioError = error as DioError; @@ -246,10 +254,6 @@ class _UserListViewState extends State } Widget _buildEmpty() { - if (widget.emptyBuilder != null) { - return widget.emptyBuilder(context); - } - return LayoutBuilder( builder: (context, viewportConstraints) { return SingleChildScrollView(