diff --git a/packages/stream_chat_flutter/lib/src/v4/channel_list_view/stream_channel_list_event_handler.dart b/packages/stream_chat_flutter/lib/src/v4/channel_list_view/stream_channel_list_event_handler.dart index bd6cf50d3..449dd9826 100644 --- a/packages/stream_chat_flutter/lib/src/v4/channel_list_view/stream_channel_list_event_handler.dart +++ b/packages/stream_chat_flutter/lib/src/v4/channel_list_view/stream_channel_list_event_handler.dart @@ -1,4 +1,4 @@ -import 'package:stream_chat/stream_chat.dart' hide Success; +import 'package:stream_chat/stream_chat.dart' show ChannelState, Event; import 'package:stream_chat_flutter/src/v4/channel_list_view/stream_channel_list_controller.dart'; /// Contains handlers that are called from [StreamChannelListController] for diff --git a/packages/stream_chat_flutter/lib/src/v4/channel_list_view/stream_channel_list_view.dart b/packages/stream_chat_flutter/lib/src/v4/channel_list_view/stream_channel_list_view.dart index af4938159..67d4ad569 100644 --- a/packages/stream_chat_flutter/lib/src/v4/channel_list_view/stream_channel_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/v4/channel_list_view/stream_channel_list_view.dart @@ -250,7 +250,12 @@ class _StreamChannelListViewState extends State { builder: (context, value, _) => value.when( (channels, nextPageKey, error) { if (channels.isEmpty) { - return const Center(child: Text('No channels')); + return const Center( + child: Padding( + padding: EdgeInsets.all(8), + child: StreamChannelListEmpty(), + ), + ); } return ListView.separated( @@ -321,7 +326,11 @@ class _StreamChannelListViewState extends State { separatorBuilder: widget.separatorBuilder, itemBuilder: (_, __) => const StreamChannelListLoadingTile(), ), - error: (error) => Center(child: Text('Error: $error')), + error: (error) => Center( + child: StreamChannelListError( + onPressed: _controller.refresh, + ), + ), ), ); } @@ -347,11 +356,11 @@ class ChannelListLoadMoreError extends StatelessWidget { /// Creates a new instance of [ChannelListLoadMoreError]. const ChannelListLoadMoreError({ Key? key, - required this.onTap, + this.onTap, }) : super(key: key); /// The callback to invoke when the user taps on the error indicator. - final GestureTapCallback onTap; + final GestureTapCallback? onTap; @override Widget build(BuildContext context) { @@ -395,3 +404,92 @@ class StreamChannelListSeparator extends StatelessWidget { ); } } + +/// A widget that is used to display an error screen +/// when [StreamChannelListController] fails to load initial channels. +class StreamChannelListError extends StatelessWidget { + /// Creates a new instance of [StreamChannelListError] widget. + const StreamChannelListError({ + Key? key, + this.onPressed, + }) : super(key: key); + + /// The callback to invoke when the user taps on the retry button. + final VoidCallback? onPressed; + + @override + Widget build(BuildContext context) => Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text.rich( + TextSpan( + children: [ + const WidgetSpan( + child: Padding( + padding: EdgeInsets.only(right: 2), + child: Icon(Icons.error_outline), + ), + ), + TextSpan(text: context.translations.loadingChannelsError), + ], + ), + style: Theme.of(context).textTheme.headline6, + ), + TextButton( + onPressed: onPressed, + child: Text(context.translations.retryLabel), + ), + ], + ); +} + +/// A widget that is used to display an empty state when +/// [StreamChannelListController] loads zero channels. +class StreamChannelListEmpty extends StatelessWidget { + /// Creates a new instance of [StreamChannelListEmpty] widget. + const StreamChannelListEmpty({ + Key? key, + this.onPressed, + }) : super(key: key); + + /// The callback to invoke when the user taps on the start a chat button. + final VoidCallback? onPressed; + + @override + Widget build(BuildContext context) { + final chatThemeData = StreamChatTheme.of(context); + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Spacer(), + StreamSvgIcon.message( + size: 148, + color: chatThemeData.colorTheme.disabled, + ), + const SizedBox(height: 28), + Text( + context.translations.letsStartChattingLabel, + style: chatThemeData.textTheme.headline, + ), + const SizedBox(height: 8), + Text( + context.translations.sendingFirstMessageLabel, + textAlign: TextAlign.center, + style: chatThemeData.textTheme.body.copyWith( + color: chatThemeData.colorTheme.textLowEmphasis, + ), + ), + const Spacer(), + TextButton( + onPressed: onPressed, + child: Text( + context.translations.startAChatLabel, + style: chatThemeData.textTheme.bodyBold.copyWith( + color: chatThemeData.colorTheme.accentPrimary, + ), + ), + ), + ], + ); + } +}