diff --git a/packages/flutter_widgets/example/lib/group_info_screen.dart b/packages/flutter_widgets/example/lib/group_info_screen.dart index 253901c9..2207c282 100644 --- a/packages/flutter_widgets/example/lib/group_info_screen.dart +++ b/packages/flutter_widgets/example/lib/group_info_screen.dart @@ -70,6 +70,10 @@ class _GroupInfoScreenState extends State { ); } + var userMember = snapshot.data + .firstWhere((e) => e.user.id == StreamChat.of(context).user.id); + var isOwner = userMember.role == 'owner'; + return Scaffold( backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow, appBar: AppBar( @@ -161,7 +165,7 @@ class _GroupInfoScreenState extends State { height: 8.0, color: StreamChatTheme.of(context).colorTheme.greyGainsboro, ), - _buildNameTile(), + if (isOwner) _buildNameTile(), _buildOptionListTiles(), ], ), diff --git a/packages/flutter_widgets/example/lib/main.dart b/packages/flutter_widgets/example/lib/main.dart index 460a80a3..86cfa52d 100644 --- a/packages/flutter_widgets/example/lib/main.dart +++ b/packages/flutter_widgets/example/lib/main.dart @@ -156,6 +156,9 @@ class _HomePageState extends State { onNewChatButtonTap: () { Navigator.pushNamed(context, Routes.NEW_CHAT); }, + preNavigationCallback: () { + FocusScope.of(context).requestFocus(FocusNode()); + }, ), drawer: _buildDrawer(context, user), drawerEdgeDragWidth: 50, @@ -519,6 +522,7 @@ class _ChannelListPageState extends State { ); }, onItemTap: (messageResponse) async { + FocusScope.of(context).requestFocus(FocusNode()); final client = StreamChat.of(context).client; final message = messageResponse.message; final channel = client.channel( diff --git a/packages/flutter_widgets/lib/src/channel_list_header.dart b/packages/flutter_widgets/lib/src/channel_list_header.dart index b6bf3ca7..e8583d11 100644 --- a/packages/flutter_widgets/lib/src/channel_list_header.dart +++ b/packages/flutter_widgets/lib/src/channel_list_header.dart @@ -55,6 +55,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { this.onUserAvatarTap, this.onNewChatButtonTap, this.showConnectionStateTile = false, + this.preNavigationCallback, }) : super(key: key); /// Pass this if you don't have a [Client] in your widget tree. @@ -72,6 +73,8 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { final bool showConnectionStateTile; + final VoidCallback preNavigationCallback; + @override Widget build(BuildContext context) { final _client = client ?? StreamChat.of(context).client; @@ -110,8 +113,13 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { child: UserAvatar( user: user, showOnlineStatus: false, - onTap: - onUserAvatarTap ?? (_) => Scaffold.of(context).openDrawer(), + onTap: onUserAvatarTap ?? + (_) { + if (preNavigationCallback != null) { + preNavigationCallback(); + } + Scaffold.of(context).openDrawer(); + }, borderRadius: BorderRadius.circular(20), constraints: BoxConstraints.tightFor( height: 40, diff --git a/packages/flutter_widgets/lib/src/message_input.dart b/packages/flutter_widgets/lib/src/message_input.dart index 6fafb5d8..d3623f01 100644 --- a/packages/flutter_widgets/lib/src/message_input.dart +++ b/packages/flutter_widgets/lib/src/message_input.dart @@ -433,83 +433,80 @@ class MessageInputState extends State { _buildAttachments(), LimitedBox( maxHeight: widget.maxHeight, - child: SizedBox( - height: 40, - child: TextField( - key: Key('messageInputText'), - enabled: _inputEnabled, - minLines: null, - maxLines: null, - onSubmitted: (_) => sendMessage(), - keyboardType: widget.keyboardType, - controller: textEditingController, - focusNode: _focusNode, - style: theme.textTheme.body, - autofocus: false, - textAlignVertical: TextAlignVertical.center, - decoration: InputDecoration( - isDense: true, - hintText: _getHint(), - hintStyle: theme.textTheme.body.copyWith( - color: theme.colorTheme.grey, - ), - border: OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent)), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent)), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent)), - errorBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent)), - disabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent)), - contentPadding: const EdgeInsets.fromLTRB(16, 12, 13, 11), - prefixIcon: _commandEnabled - ? Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: theme.colorTheme.accentBlue, - ), - height: 24, - margin: const EdgeInsets.all(8.0), - padding: const EdgeInsets.only(right: 8, left: 4), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - StreamSvgIcon.lightning( - color: Colors.white, - size: 16.0, - ), - Text( - _chosenCommand?.name?.toUpperCase() ?? '', - style: StreamChatTheme.of(context) - .textTheme - .footnoteBold - .copyWith( - color: Colors.white, - ), - ), - ], - ), - ) - : null, - suffixIcon: _commandEnabled - ? IconButton( - icon: StreamSvgIcon.close_small(), - splashRadius: 24, - padding: const EdgeInsets.all(0), - constraints: BoxConstraints.tightFor( - height: 24, - width: 24, - ), - onPressed: () { - setState(() => _commandEnabled = false); - }, - ) - : null, + child: TextField( + key: Key('messageInputText'), + enabled: _inputEnabled, + minLines: null, + maxLines: null, + onSubmitted: (_) => sendMessage(), + keyboardType: widget.keyboardType, + controller: textEditingController, + focusNode: _focusNode, + style: theme.textTheme.body, + autofocus: false, + textAlignVertical: TextAlignVertical.center, + decoration: InputDecoration( + isDense: true, + hintText: _getHint(), + hintStyle: theme.textTheme.body.copyWith( + color: theme.colorTheme.grey, ), - textCapitalization: TextCapitalization.sentences, + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.transparent)), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.transparent)), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.transparent)), + errorBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.transparent)), + disabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.transparent)), + contentPadding: const EdgeInsets.fromLTRB(16, 12, 13, 11), + prefixIcon: _commandEnabled + ? Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: theme.colorTheme.accentBlue, + ), + height: 24, + margin: const EdgeInsets.all(8.0), + padding: const EdgeInsets.only(right: 8, left: 4), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + StreamSvgIcon.lightning( + color: Colors.white, + size: 16.0, + ), + Text( + _chosenCommand?.name?.toUpperCase() ?? '', + style: StreamChatTheme.of(context) + .textTheme + .footnoteBold + .copyWith( + color: Colors.white, + ), + ), + ], + ), + ) + : null, + suffixIcon: _commandEnabled + ? IconButton( + icon: StreamSvgIcon.close_small(), + splashRadius: 24, + padding: const EdgeInsets.all(0), + constraints: BoxConstraints.tightFor( + height: 24, + width: 24, + ), + onPressed: () { + setState(() => _commandEnabled = false); + }, + ) + : null, ), + textCapitalization: TextCapitalization.sentences, ), ) ], @@ -1042,15 +1039,9 @@ class MessageInputState extends State { final mediaInfo = await CompressVideoService.compressVideo(file.path); if (mediaInfo.filesize / (1024 * 1024) > _kMaxAttachmentSize) { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( - SnackBar( - content: Text( - 'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.', - ), - ), + _showErrorAlert( + 'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.', ); - setState(() { _attachments.remove(attachment); }); @@ -1063,13 +1054,8 @@ class MessageInputState extends State { path: mediaInfo.path, ); } else { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( - SnackBar( - content: Text( - 'The file is too large to upload. The file size limit is 20MB', - ), - ), + _showErrorAlert( + 'The file is too large to upload. The file size limit is 20MB.', ); } } @@ -2021,12 +2007,8 @@ class MessageInputState extends State { }); } else { // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( - SnackBar( - content: Text( - 'The file is too large to upload. The file size limit is 20MB', - ), - ), + _showErrorAlert( + 'The file is too large to upload. The file size limit is 20MB.', ); setState(() { _attachments.remove(attachment); @@ -2279,6 +2261,77 @@ class MessageInputState extends State { }); } + void _showErrorAlert(String description) { + showModalBottomSheet( + backgroundColor: StreamChatTheme.of(context).colorTheme.white, + context: context, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16.0), + topRight: Radius.circular(16.0), + )), + builder: (context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 26.0, + ), + StreamSvgIcon.error( + color: StreamChatTheme.of(context).colorTheme.accentRed, + size: 24.0, + ), + SizedBox( + height: 26.0, + ), + Text( + 'Something went wrong', + style: StreamChatTheme.of(context).textTheme.headlineBold, + ), + SizedBox( + height: 7.0, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + description, + textAlign: TextAlign.center, + ), + ), + SizedBox( + height: 36.0, + ), + Container( + color: + StreamChatTheme.of(context).colorTheme.black.withOpacity(.08), + height: 1.0, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FlatButton( + child: Text( + 'OK', + style: StreamChatTheme.of(context) + .textTheme + .bodyBold + .copyWith( + color: StreamChatTheme.of(context) + .colorTheme + .accentBlue), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ), + ], + ); + }, + ); + } + void _parseExistingMessage(Message message) { textEditingController.text = message.text; diff --git a/packages/flutter_widgets/lib/src/message_list_view.dart b/packages/flutter_widgets/lib/src/message_list_view.dart index a0b5a60d..c990d2c7 100644 --- a/packages/flutter_widgets/lib/src/message_list_view.dart +++ b/packages/flutter_widgets/lib/src/message_list_view.dart @@ -956,7 +956,10 @@ class _MessageListViewState extends State { end: colorTheme.white.withOpacity(0), ), duration: const Duration(seconds: 3), - child: child, + child: Padding( + padding: const EdgeInsets.only(top: 4.0), + child: child, + ), onEnd: () => initialMessageHighlightComplete = true, builder: (_, color, child) { return Container( diff --git a/packages/flutter_widgets/lib/src/thread_header.dart b/packages/flutter_widgets/lib/src/thread_header.dart index eb748272..35d554a6 100644 --- a/packages/flutter_widgets/lib/src/thread_header.dart +++ b/packages/flutter_widgets/lib/src/thread_header.dart @@ -79,6 +79,7 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget { Widget build(BuildContext context) { return AppBar( automaticallyImplyLeading: false, + brightness: Theme.of(context).brightness, elevation: 1, leading: showBackButton ? StreamBackButton(