diff --git a/lib/src/channel_header.dart b/lib/src/channel_header.dart index 4517d7a0..fe624473 100644 --- a/lib/src/channel_header.dart +++ b/lib/src/channel_header.dart @@ -3,6 +3,7 @@ import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter/src/back_button.dart'; import 'package:stream_chat_flutter/src/channel_info.dart'; import 'package:stream_chat_flutter/src/channel_name.dart'; +import 'package:stream_chat_flutter/src/info_tile.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import '../stream_chat_flutter.dart'; @@ -82,54 +83,84 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { final channel = StreamChannel.of(context).channel; - return AppBar( - brightness: Theme.of(context).brightness, - elevation: 1, - leading: showBackButton - ? StreamBackButton( - onPressed: onBackPressed, - showUnreads: true, - ) - : SizedBox(), - backgroundColor: - StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color, - actions: [ - Padding( - padding: const EdgeInsets.only(right: 10.0), - child: Center( - child: ChannelImage( - onTap: onImageTap, - ), - ), - ), - ], - centerTitle: true, - title: InkWell( - onTap: onTitleTap, - child: Container( - height: preferredSize.height, - width: preferredSize.width, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ChannelName( - textStyle: StreamChatTheme.of(context) - .channelTheme - .channelHeaderTheme - .title, - ), - SizedBox(height: 2), - ChannelInfo( - showTypingIndicator: showTypingIndicator, - channel: channel, - textStyle: - StreamChatTheme.of(context).channelPreviewTheme.subtitle, + final _client = StreamChat.of(context).client; + + return ValueListenableBuilder( + valueListenable: _client.wsConnectionStatus, + builder: (context, status, _) { + String statusString = ''; + bool showStatus = true; + + switch (status) { + case ConnectionStatus.connected: + statusString = 'Connected'; + showStatus = false; + break; + case ConnectionStatus.connecting: + statusString = 'Connecting'; + break; + case ConnectionStatus.disconnected: + statusString = 'Disconnected'; + break; + } + + return InfoTile( + showMessage: showStatus, + message: statusString, + child: AppBar( + brightness: Theme.of(context).brightness, + elevation: 1, + leading: showBackButton + ? StreamBackButton( + onPressed: onBackPressed, + showUnreads: true, + ) + : SizedBox(), + backgroundColor: StreamChatTheme.of(context) + .channelTheme + .channelHeaderTheme + .color, + actions: [ + Padding( + padding: const EdgeInsets.only(right: 10.0), + child: Center( + child: ChannelImage( + onTap: onImageTap, + ), + ), ), ], + centerTitle: true, + title: InkWell( + onTap: onTitleTap, + child: Container( + height: preferredSize.height, + width: preferredSize.width, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ChannelName( + textStyle: StreamChatTheme.of(context) + .channelTheme + .channelHeaderTheme + .title, + ), + SizedBox(height: 2), + ChannelInfo( + showTypingIndicator: showTypingIndicator, + channel: channel, + textStyle: StreamChatTheme.of(context) + .channelPreviewTheme + .subtitle, + ), + ], + ), + ), + ), ), - ), - ), + ); + }, ); } diff --git a/lib/src/channel_list_header.dart b/lib/src/channel_list_header.dart index 34f3cc76..e7fc9f9a 100644 --- a/lib/src/channel_list_header.dart +++ b/lib/src/channel_list_header.dart @@ -6,6 +6,7 @@ import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter/src/stream_neumorphic_button.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import 'info_tile.dart'; import 'stream_chat.dart'; typedef _TitleBuilder = Widget Function( @@ -72,73 +73,101 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { Widget build(BuildContext context) { final _client = client ?? StreamChat.of(context).client; final user = _client.state.user; - return AppBar( - brightness: Theme.of(context).brightness, - elevation: 1, - backgroundColor: - StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color, - centerTitle: true, - leading: Center( - child: UserAvatar( - user: user, - showOnlineStatus: false, - onTap: onUserAvatarTap ?? (_) => Scaffold.of(context).openDrawer(), - borderRadius: BorderRadius.circular(20), - constraints: BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - ), - actions: [ - StreamNeumorphicButton( - child: IconButton( - icon: ValueListenableBuilder( - valueListenable: _client.wsConnectionStatus, - builder: (context, status, child) { - var color; + return ValueListenableBuilder( + valueListenable: _client.wsConnectionStatus, + builder: (context, status, child) { + String statusString = ''; + bool showStatus = true; + + switch (status) { + case ConnectionStatus.connected: + statusString = 'Connected'; + showStatus = false; + break; + case ConnectionStatus.connecting: + statusString = 'Connecting'; + break; + case ConnectionStatus.disconnected: + statusString = 'Disconnected'; + break; + } + + return InfoTile( + showMessage: showStatus, + message: statusString, + child: AppBar( + brightness: Theme.of(context).brightness, + elevation: 1, + backgroundColor: StreamChatTheme.of(context) + .channelTheme + .channelHeaderTheme + .color, + centerTitle: true, + leading: Center( + child: UserAvatar( + user: user, + showOnlineStatus: false, + onTap: + onUserAvatarTap ?? (_) => Scaffold.of(context).openDrawer(), + borderRadius: BorderRadius.circular(20), + constraints: BoxConstraints.tightFor( + height: 40, + width: 40, + ), + ), + ), + actions: [ + StreamNeumorphicButton( + child: IconButton( + icon: ValueListenableBuilder( + valueListenable: _client.wsConnectionStatus, + builder: (context, status, child) { + var color; + switch (status) { + case ConnectionStatus.connected: + color = + StreamChatTheme.of(context).colorTheme.accentBlue; + break; + case ConnectionStatus.connecting: + color = Colors.grey; + break; + case ConnectionStatus.disconnected: + color = Colors.grey; + break; + } + return SvgPicture.asset( + 'svgs/icon_pen_write.svg', + package: 'stream_chat_flutter', + width: 24.0, + height: 24.0, + color: color, + ); + }, + ), + onPressed: onNewChatButtonTap, + ), + ) + ], + title: Builder( + builder: (context) { + if (titleBuilder != null) { + return titleBuilder(context, status, _client); + } switch (status) { case ConnectionStatus.connected: - color = StreamChatTheme.of(context).colorTheme.accentBlue; - break; + return _buildConnectedTitleState(context); case ConnectionStatus.connecting: - color = Colors.grey; - break; + return _buildConnectingTitleState(context); case ConnectionStatus.disconnected: - color = Colors.grey; - break; + return _buildDisconnectedTitleState(context, _client); + default: + return Offstage(); } - return SvgPicture.asset( - 'svgs/icon_pen_write.svg', - package: 'stream_chat_flutter', - width: 24.0, - height: 24.0, - color: color, - ); }, ), - onPressed: onNewChatButtonTap, ), - ) - ], - title: ValueListenableBuilder( - valueListenable: _client.wsConnectionStatus, - builder: (context, status, child) { - if (titleBuilder != null) { - return titleBuilder(context, status, _client); - } - switch (status) { - case ConnectionStatus.connected: - return _buildConnectedTitleState(context); - case ConnectionStatus.connecting: - return _buildConnectingTitleState(context); - case ConnectionStatus.disconnected: - return _buildDisconnectedTitleState(context, _client); - default: - return Offstage(); - } - }, - ), + ); + }, ); } diff --git a/lib/src/info_tile.dart b/lib/src/info_tile.dart new file mode 100644 index 00000000..c3905b91 --- /dev/null +++ b/lib/src/info_tile.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_portal/flutter_portal.dart'; +import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; + +class InfoTile extends StatelessWidget { + final String message; + final Widget child; + final bool showMessage; + + InfoTile({this.message, this.child, this.showMessage}); + + @override + Widget build(BuildContext context) { + return PortalEntry( + visible: showMessage, + portalAnchor: Alignment.topCenter, + childAnchor: Alignment.bottomCenter, + portal: Container( + height: 25.0, + color: StreamChatTheme.of(context).colorTheme.grey, + child: Center( + child: Text( + message, + style: StreamChatTheme.of(context).textTheme.body.copyWith( + color: Colors.white, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ), + child: child, + ); + } +} diff --git a/lib/src/stream_chat.dart b/lib/src/stream_chat.dart index 317dfec6..3ebdf6e9 100644 --- a/lib/src/stream_chat.dart +++ b/lib/src/stream_chat.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_app_badger/flutter_app_badger.dart'; +import 'package:flutter_portal/flutter_portal.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; @@ -67,22 +68,24 @@ class StreamChatState extends State with WidgetsBindingObserver { @override Widget build(BuildContext context) { final theme = _getTheme(context, widget.streamChatThemeData); - return StreamChatTheme( - data: theme, - child: Builder( - builder: (context) { - final materialTheme = Theme.of(context); - final streamTheme = StreamChatTheme.of(context); - return Theme( - data: materialTheme.copyWith( - primaryIconTheme: streamTheme.primaryIconTheme, - accentColor: streamTheme.colorTheme.accentBlue, - scaffoldBackgroundColor: streamTheme.colorTheme.white, - buttonTheme: streamTheme.buttonTheme, - ), - child: widget.child, - ); - }, + return Portal( + child: StreamChatTheme( + data: theme, + child: Builder( + builder: (context) { + final materialTheme = Theme.of(context); + final streamTheme = StreamChatTheme.of(context); + return Theme( + data: materialTheme.copyWith( + primaryIconTheme: streamTheme.primaryIconTheme, + accentColor: streamTheme.colorTheme.accentBlue, + scaffoldBackgroundColor: streamTheme.colorTheme.white, + buttonTheme: streamTheme.buttonTheme, + ), + child: widget.child, + ); + }, + ), ), ); }