diff --git a/example/lib/group_chat_details_screen.dart b/example/lib/group_chat_details_screen.dart index 6aff5711..5fca35b4 100644 --- a/example/lib/group_chat_details_screen.dart +++ b/example/lib/group_chat_details_screen.dart @@ -3,7 +3,6 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:uuid/uuid.dart'; import 'main.dart'; -import 'neumorphic_button.dart'; class GroupChatDetailsScreen extends StatefulWidget { final List selectedUsers; @@ -103,7 +102,7 @@ class _GroupChatDetailsScreenState extends State { ), ), actions: [ - NeumorphicButton( + StreamNeumorphicButton( child: IconButton( padding: const EdgeInsets.all(0), icon: Icon(StreamIcons.check), diff --git a/example/lib/main.dart b/example/lib/main.dart index a7d5e473..9bcf63b5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -69,12 +69,14 @@ class ChannelListPage extends StatelessWidget { Widget build(BuildContext context) { final user = StreamChat.of(context).user; return Scaffold( - appBar: AppBar( - backgroundColor: Colors.white, - title: Text( - 'Stream Chat', - style: TextStyle(color: Colors.black), - ), + appBar: ChannelListHeader( + onNewChatButtonTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => NewChatScreen(), + ), + ); + }, ), drawer: _buildDrawer(context, user), drawerEdgeDragWidth: 50, diff --git a/example/lib/new_chat_screen.dart b/example/lib/new_chat_screen.dart index 9ec2ca7e..deff2a3c 100644 --- a/example/lib/new_chat_screen.dart +++ b/example/lib/new_chat_screen.dart @@ -5,7 +5,6 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'chips_input_text_field.dart'; import 'main.dart'; -import 'neumorphic_button.dart'; import 'new_group_chat_screen.dart'; class NewChatScreen extends StatefulWidget { @@ -193,7 +192,7 @@ class _NewChatScreenState extends State { padding: const EdgeInsets.symmetric(vertical: 8), child: Row( children: [ - NeumorphicButton( + StreamNeumorphicButton( child: Icon( StreamIcons.group, color: Color(0xFF006CFF), diff --git a/lib/src/channel_list_header.dart b/lib/src/channel_list_header.dart new file mode 100644 index 00000000..0a01733b --- /dev/null +++ b/lib/src/channel_list_header.dart @@ -0,0 +1,223 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +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 'stream_chat.dart'; + +typedef _TitleBuilder = Widget Function( + BuildContext context, + ConnectionStatus status, + Client client, +); + +/// +/// It shows the current [Client] status. +/// +/// ```dart +/// class MyApp extends StatelessWidget { +/// final Client client; +/// +/// MyApp(this.client); +/// +/// @override +/// Widget build(BuildContext context) { +/// return MaterialApp( +/// home: StreamChat( +/// client: client, +/// child: Scaffold( +/// appBar: ChannelListHeader(), +/// ), +/// ), +/// ); +/// } +/// } +/// ``` +/// +/// Usually you would use this widget as an [AppBar] inside a [Scaffold]. +/// However you can also use it as a normal widget. +/// +/// The widget by default uses the inherited [Client] to fetch information about the status. +/// However you can also pass your own [Client] if you don't have it in the widget tree. +/// +/// The widget components render the ui based on the first ancestor of type [StreamChatTheme] and on its [ChannelTheme.channelHeaderTheme] property. +/// Modify it to change the widget appearance. +class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { + /// Instantiates a ChannelListHeader + const ChannelListHeader({ + Key key, + this.client, + this.titleBuilder, + this.onUserAvatarTap, + this.onNewChatButtonTap, + }) : super(key: key); + + /// Pass this if you don't have a [Client] in your widget tree. + final Client client; + + /// Use this to build your own title as per different [ConnectionStatus] + final _TitleBuilder titleBuilder; + + /// Callback to call when pressing the user avatar button. + /// By default it calls Scaffold.of(context).openDrawer() + final Function(User) onUserAvatarTap; + + /// Callback to call when pressing the new chat button. + final VoidCallback onNewChatButtonTap; + + @override + 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; + switch (status) { + case ConnectionStatus.connected: + color = Color(0xFF006CFF); + 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: 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(); + } + }, + ), + ); + } + + Widget _buildConnectedTitleState(BuildContext context) => Text( + 'Stream Chat', + style: StreamChatTheme.of(context) + .channelTheme + .channelHeaderTheme + .title + .copyWith( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ); + + Widget _buildConnectingTitleState(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + height: 16, + width: 16, + child: Center( + child: CircularProgressIndicator(), + ), + ), + SizedBox(width: 10), + Text( + 'Searching for Network', + style: StreamChatTheme.of(context) + .channelTheme + .channelHeaderTheme + .title + .copyWith( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ], + ); + } + + Widget _buildDisconnectedTitleState(BuildContext context, Client client) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Offline...', + style: StreamChatTheme.of(context) + .channelTheme + .channelHeaderTheme + .title + .copyWith( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + TextButton( + onPressed: () async { + await client.disconnect(); + return client.connect(); + }, + child: Text( + 'Try Again', + style: StreamChatTheme.of(context) + .channelTheme + .channelHeaderTheme + .title + .copyWith( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Color(0xFF006CFF), + ), + ), + ), + ], + ); + } + + @override + Size get preferredSize => Size.fromHeight(kToolbarHeight); +} diff --git a/example/lib/neumorphic_button.dart b/lib/src/stream_neumorphic_button.dart similarity index 90% rename from example/lib/neumorphic_button.dart rename to lib/src/stream_neumorphic_button.dart index 69454fb2..bf3e8e97 100644 --- a/example/lib/neumorphic_button.dart +++ b/lib/src/stream_neumorphic_button.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; -class NeumorphicButton extends StatelessWidget { +class StreamNeumorphicButton extends StatelessWidget { final Widget child; final Color backgroundColor; - const NeumorphicButton({ + const StreamNeumorphicButton({ Key key, @required this.child, this.backgroundColor = Colors.white, diff --git a/lib/stream_chat_flutter.dart b/lib/stream_chat_flutter.dart index 91ac68c8..1399bf08 100644 --- a/lib/stream_chat_flutter.dart +++ b/lib/stream_chat_flutter.dart @@ -33,3 +33,5 @@ export 'src/video_attachment.dart'; export 'src/users_bloc.dart'; export 'src/user_list_view.dart'; export 'src/user_item.dart'; +export 'src/stream_neumorphic_button.dart'; +export 'src/channel_list_header.dart'; diff --git a/svgs/icon_pen_write.svg b/svgs/icon_pen_write.svg new file mode 100644 index 00000000..b2711dad --- /dev/null +++ b/svgs/icon_pen_write.svg @@ -0,0 +1,5 @@ + + +