diff --git a/packages/chatty/lib/data/local/image_picker_impl.dart b/packages/chatty/lib/data/local/image_picker_impl.dart index 21a1b15..90c63e0 100644 --- a/packages/chatty/lib/data/local/image_picker_impl.dart +++ b/packages/chatty/lib/data/local/image_picker_impl.dart @@ -6,7 +6,8 @@ class ImagePickerImpl extends ImagePickerRepository { @override Future pickImage() async { final picker = ImagePicker(); - final pickedFile = await picker.getImage(source: ImageSource.gallery, maxWidth: 400); + final pickedFile = + await picker.getImage(source: ImageSource.gallery, maxWidth: 400); return File(pickedFile.path); } } diff --git a/packages/chatty/lib/data/local/stream_api_local_impl.dart b/packages/chatty/lib/data/local/stream_api_local_impl.dart index f772a26..a2d2e35 100644 --- a/packages/chatty/lib/data/local/stream_api_local_impl.dart +++ b/packages/chatty/lib/data/local/stream_api_local_impl.dart @@ -46,7 +46,9 @@ class StreamApiLocalImpl extends StreamApiRepository { } @override - Future createGroupChat(String channelId, String name, List members, {String image}) async { + Future createGroupChat( + String channelId, String name, List members, + {String image}) async { final channel = _client.channel('messaging', id: channelId, extraData: { 'name': name, 'image': image, @@ -58,13 +60,14 @@ class StreamApiLocalImpl extends StreamApiRepository { @override Future createSimpleChat(String friendId) async { - final channel = - _client.channel('messaging', id: '${_client.state.user.id.hashCode}${friendId.hashCode}', extraData: { - 'members': [ - friendId, - _client.state.user.id, - ], - }); + final channel = _client.channel('messaging', + id: '${_client.state.user.id.hashCode}${friendId.hashCode}', + extraData: { + 'members': [ + friendId, + _client.state.user.id, + ], + }); await channel.watch(); return channel; } diff --git a/packages/chatty/lib/data/prod/auth_impl.dart b/packages/chatty/lib/data/prod/auth_impl.dart index 90547bb..71bb3c6 100644 --- a/packages/chatty/lib/data/prod/auth_impl.dart +++ b/packages/chatty/lib/data/prod/auth_impl.dart @@ -20,8 +20,10 @@ class AuthImpl extends AuthRepository { try { UserCredential userCredential; final GoogleSignInAccount googleUser = await GoogleSignIn().signIn(); - final GoogleSignInAuthentication googleAuth = await googleUser.authentication; - final GoogleAuthCredential googleAuthCredential = GoogleAuthProvider.credential( + final GoogleSignInAuthentication googleAuth = + await googleUser.authentication; + final GoogleAuthCredential googleAuthCredential = + GoogleAuthProvider.credential( accessToken: googleAuth.accessToken, idToken: googleAuth.idToken, ); diff --git a/packages/chatty/lib/data/prod/stream_api_impl.dart b/packages/chatty/lib/data/prod/stream_api_impl.dart index 1bcab14..a60cd1c 100644 --- a/packages/chatty/lib/data/prod/stream_api_impl.dart +++ b/packages/chatty/lib/data/prod/stream_api_impl.dart @@ -62,7 +62,8 @@ class StreamApiImpl extends StreamApiRepository { } @override - Future createGroupChat(String id, String name, List members, {String image}) async { + Future createGroupChat(String id, String name, List members, + {String image}) async { final channel = _client.channel('messaging', id: id, extraData: { 'name': name, 'image': image, @@ -74,13 +75,14 @@ class StreamApiImpl extends StreamApiRepository { @override Future createSimpleChat(String friendId) async { - final channel = - _client.channel('messaging', id: '${_client.state.user.id.hashCode}${friendId.hashCode}', extraData: { - 'members': [ - friendId, - _client.state.user.id, - ], - }); + final channel = _client.channel('messaging', + id: '${_client.state.user.id.hashCode}${friendId.hashCode}', + extraData: { + 'members': [ + friendId, + _client.state.user.id, + ], + }); await channel.watch(); return channel; } diff --git a/packages/chatty/lib/data/stream_api_repository.dart b/packages/chatty/lib/data/stream_api_repository.dart index 4ae31cb..3d5c215 100644 --- a/packages/chatty/lib/data/stream_api_repository.dart +++ b/packages/chatty/lib/data/stream_api_repository.dart @@ -6,7 +6,9 @@ abstract class StreamApiRepository { Future getToken(String userId); Future connectIfExist(String userId); Future connectUser(ChatUser user, String token); - Future createGroupChat(String channelId, String name, List members, {String image}); + Future createGroupChat( + String channelId, String name, List members, + {String image}); Future createSimpleChat(String friendId); Future logout(); } diff --git a/packages/chatty/lib/dependencies.dart b/packages/chatty/lib/dependencies.dart index f6d6414..d60194c 100644 --- a/packages/chatty/lib/dependencies.dart +++ b/packages/chatty/lib/dependencies.dart @@ -18,10 +18,13 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; List buildRepositories(StreamChatClient client) { //TODO: Here you can use your local implementations of your repositories return [ - RepositoryProvider(create: (_) => StreamApiImpl(client)), - RepositoryProvider(create: (_) => PersistentStorageImpl()), + RepositoryProvider( + create: (_) => StreamApiImpl(client)), + RepositoryProvider( + create: (_) => PersistentStorageImpl()), RepositoryProvider(create: (_) => AuthImpl()), - RepositoryProvider(create: (_) => UploadStorageImpl()), + RepositoryProvider( + create: (_) => UploadStorageImpl()), RepositoryProvider(create: (_) => ImagePickerImpl()), RepositoryProvider( create: (context) => ProfileSignInUseCase( diff --git a/packages/chatty/lib/domain/usecases/create_group_usecase.dart b/packages/chatty/lib/domain/usecases/create_group_usecase.dart index 82122a2..0e19d80 100644 --- a/packages/chatty/lib/domain/usecases/create_group_usecase.dart +++ b/packages/chatty/lib/domain/usecases/create_group_usecase.dart @@ -25,7 +25,8 @@ class CreateGroupUseCase { final channelId = Uuid().v4(); String image; if (input.imageFile != null) { - image = await _uploadStorageRepository.uploadPhoto(input.imageFile, 'channels/$channelId'); + image = await _uploadStorageRepository.uploadPhoto( + input.imageFile, 'channels/$channelId'); } final channel = await _streamApiRepository.createGroupChat( channelId, diff --git a/packages/chatty/lib/domain/usecases/profile_sign_in_usecase.dart b/packages/chatty/lib/domain/usecases/profile_sign_in_usecase.dart index 8450581..1c18918 100644 --- a/packages/chatty/lib/domain/usecases/profile_sign_in_usecase.dart +++ b/packages/chatty/lib/domain/usecases/profile_sign_in_usecase.dart @@ -27,8 +27,10 @@ class ProfileSignInUseCase { final token = await _streamApiRepository.getToken(auth.id); String image; if (input.imageFile != null) { - image = await _uploadStorageRepository.uploadPhoto(input.imageFile, 'users/${auth.id}'); + image = await _uploadStorageRepository.uploadPhoto( + input.imageFile, 'users/${auth.id}'); } - await _streamApiRepository.connectUser(ChatUser(name: input.name, id: auth.id, image: image), token); + await _streamApiRepository.connectUser( + ChatUser(name: input.name, id: auth.id, image: image), token); } } diff --git a/packages/chatty/lib/main.dart b/packages/chatty/lib/main.dart index 479c1c8..817010d 100644 --- a/packages/chatty/lib/main.dart +++ b/packages/chatty/lib/main.dart @@ -34,7 +34,8 @@ class MyApp extends StatelessWidget { return StreamChat( child: child, client: _streamChatClient, - streamChatThemeData: StreamChatThemeData.fromTheme(Theme.of(context)).copyWith( + streamChatThemeData: + StreamChatThemeData.fromTheme(Theme.of(context)).copyWith( ownMessageTheme: MessageTheme( messageBackgroundColor: Theme.of(context).accentColor, messageText: TextStyle(color: Colors.white), diff --git a/packages/chatty/lib/navigator_utils.dart b/packages/chatty/lib/navigator_utils.dart index 2d5c27f..731c4c1 100644 --- a/packages/chatty/lib/navigator_utils.dart +++ b/packages/chatty/lib/navigator_utils.dart @@ -18,5 +18,7 @@ Future pushAndReplaceToPage(BuildContext context, Widget widget) async { Future popAllAndPush(BuildContext context, Widget widget) async { await Navigator.pushAndRemoveUntil( - context, MaterialPageRoute(builder: (BuildContext context) => widget), ModalRoute.withName('/')); + context, + MaterialPageRoute(builder: (BuildContext context) => widget), + ModalRoute.withName('/')); } diff --git a/packages/chatty/lib/ui/common/my_channel_preview.dart b/packages/chatty/lib/ui/common/my_channel_preview.dart index b561e45..8886b47 100644 --- a/packages/chatty/lib/ui/common/my_channel_preview.dart +++ b/packages/chatty/lib/ui/common/my_channel_preview.dart @@ -90,7 +90,8 @@ class MyChannelPreview extends StatelessWidget { children: [ Flexible( child: ChannelName( - textStyle: StreamChatTheme.of(context).channelPreviewTheme.title, + textStyle: + StreamChatTheme.of(context).channelPreviewTheme.title, ), ), StreamBuilder>( @@ -99,7 +100,8 @@ class MyChannelPreview extends StatelessWidget { builder: (context, snapshot) { if (!snapshot.hasData || snapshot.data.isEmpty || - !snapshot.data.any((Member e) => e.user.id == channel.client.state.user.id)) { + !snapshot.data.any((Member e) => + e.user.id == channel.client.state.user.id)) { return SizedBox(); } return ChannelUnreadIndicator( @@ -118,15 +120,21 @@ class MyChannelPreview extends StatelessWidget { (m) => !m.isDeleted && m.shadowed != true, orElse: () => null, ); - if (lastMessage?.user?.id == StreamChat.of(context).user.id) { + if (lastMessage?.user?.id == + StreamChat.of(context).user.id) { return Padding( padding: const EdgeInsets.only(right: 4.0), child: SendingIndicator( message: lastMessage, - size: StreamChatTheme.of(context).channelPreviewTheme.indicatorIconSize, + size: StreamChatTheme.of(context) + .channelPreviewTheme + .indicatorIconSize, isMessageRead: channel.state.read - ?.where((element) => element.user.id != channel.client.state.user.id) - ?.where((element) => element.lastRead.isAfter(lastMessage.createdAt)) + ?.where((element) => + element.user.id != + channel.client.state.user.id) + ?.where((element) => element.lastRead + .isAfter(lastMessage.createdAt)) ?.isNotEmpty == true, ), @@ -158,7 +166,8 @@ class MyChannelPreview extends StatelessWidget { var startOfDay = DateTime(now.year, now.month, now.day); - if (lastMessageAt.millisecondsSinceEpoch >= startOfDay.millisecondsSinceEpoch) { + if (lastMessageAt.millisecondsSinceEpoch >= + startOfDay.millisecondsSinceEpoch) { stringDate = Jiffy(lastMessageAt.toLocal()).format('HH:mm'); } else if (lastMessageAt.millisecondsSinceEpoch >= startOfDay.subtract(Duration(days: 1)).millisecondsSinceEpoch) { @@ -187,8 +196,14 @@ class MyChannelPreview extends StatelessWidget { ), Text( ' Channel is muted', - style: StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith( - color: StreamChatTheme.of(context).channelPreviewTheme.subtitle.color, + style: StreamChatTheme.of(context) + .channelPreviewTheme + .subtitle + .copyWith( + color: StreamChatTheme.of(context) + .channelPreviewTheme + .subtitle + .color, ), ), ], @@ -198,7 +213,8 @@ class MyChannelPreview extends StatelessWidget { channel: channel, alternativeWidget: _buildLastMessage(context), style: StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith( - color: StreamChatTheme.of(context).channelPreviewTheme.subtitle.color, + color: + StreamChatTheme.of(context).channelPreviewTheme.subtitle.color, ), ); } @@ -208,7 +224,9 @@ class MyChannelPreview extends StatelessWidget { stream: channel.state.messagesStream, initialData: channel.state.messages, builder: (context, snapshot) { - final lastMessage = snapshot.data?.lastWhere((m) => m.shadowed != true && !m.isDeleted, orElse: () => null); + final lastMessage = snapshot.data?.lastWhere( + (m) => m.shadowed != true && !m.isDeleted, + orElse: () => null); if (lastMessage == null) { return SizedBox(); } @@ -224,7 +242,9 @@ class MyChannelPreview extends StatelessWidget { } else if (e.type == 'giphy') { return '[GIF]'; } - return e == lastMessage.attachments.last ? (e.title ?? 'File') : '${e.title ?? 'File'} , '; + return e == lastMessage.attachments.last + ? (e.title ?? 'File') + : '${e.title ?? 'File'} , '; }).where((e) => e != null), lastMessage.text ?? '', ]; @@ -238,11 +258,21 @@ class MyChannelPreview extends StatelessWidget { lastMessage.mentionedUsers, lastMessage.attachments, StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith( - color: StreamChatTheme.of(context).channelPreviewTheme.subtitle.color, - fontStyle: (lastMessage.isSystem || lastMessage.isDeleted) ? FontStyle.italic : FontStyle.normal), + color: StreamChatTheme.of(context) + .channelPreviewTheme + .subtitle + .color, + fontStyle: (lastMessage.isSystem || lastMessage.isDeleted) + ? FontStyle.italic + : FontStyle.normal), StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith( - color: StreamChatTheme.of(context).channelPreviewTheme.subtitle.color, - fontStyle: (lastMessage.isSystem || lastMessage.isDeleted) ? FontStyle.italic : FontStyle.normal, + color: StreamChatTheme.of(context) + .channelPreviewTheme + .subtitle + .color, + fontStyle: (lastMessage.isSystem || lastMessage.isDeleted) + ? FontStyle.italic + : FontStyle.normal, fontWeight: FontWeight.bold), ), maxLines: 1, @@ -252,19 +282,27 @@ class MyChannelPreview extends StatelessWidget { ); } - TextSpan _getDisplayText(String text, List mentions, List attachments, TextStyle normalTextStyle, + TextSpan _getDisplayText( + String text, + List mentions, + List attachments, + TextStyle normalTextStyle, TextStyle mentionsTextStyle) { var textList = text.split(' '); var resList = []; for (var e in textList) { - if (mentions != null && mentions.isNotEmpty && mentions.any((element) => '@${element.name}' == e)) { + if (mentions != null && + mentions.isNotEmpty && + mentions.any((element) => '@${element.name}' == e)) { resList.add(TextSpan( text: '$e ', style: mentionsTextStyle, )); } else if (attachments != null && attachments.isNotEmpty && - attachments.where((e) => e.title != null).any((element) => element.title == e)) { + attachments + .where((e) => e.title != null) + .any((element) => element.title == e)) { resList.add(TextSpan( text: '$e ', style: normalTextStyle.copyWith(fontStyle: FontStyle.italic), @@ -301,7 +339,9 @@ class ChannelUnreadIndicator extends StatelessWidget { return Material( borderRadius: BorderRadius.circular(8), - color: StreamChatTheme.of(context).channelPreviewTheme.unreadCounterColor, + color: StreamChatTheme.of(context) + .channelPreviewTheme + .unreadCounterColor, child: Padding( padding: const EdgeInsets.only( left: 5.0, diff --git a/packages/chatty/lib/ui/home/chat/chat_view.dart b/packages/chatty/lib/ui/home/chat/chat_view.dart index b9f4ea7..5267162 100644 --- a/packages/chatty/lib/ui/home/chat/chat_view.dart +++ b/packages/chatty/lib/ui/home/chat/chat_view.dart @@ -43,8 +43,10 @@ class ChatView extends StatelessWidget { name = channel.extraData['name']; image = channel.extraData['image']; } else { - final friend = - channel.state.members.where((element) => element.userId != currentUser.id).first.user; + final friend = channel.state.members + .where((element) => element.userId != currentUser.id) + .first + .user; name = friend.name; image = friend.extraData['image']; } diff --git a/packages/chatty/lib/ui/home/chat/selection/friends_selection_cubit.dart b/packages/chatty/lib/ui/home/chat/selection/friends_selection_cubit.dart index 3f35080..d43c222 100644 --- a/packages/chatty/lib/ui/home/chat/selection/friends_selection_cubit.dart +++ b/packages/chatty/lib/ui/home/chat/selection/friends_selection_cubit.dart @@ -13,21 +13,27 @@ class FriendsSelectionCubit extends Cubit> { FriendsSelectionCubit(this._streamApiRepository) : super([]); final StreamApiRepository _streamApiRepository; - List get selectedUsers => state.where((element) => element.selected).toList(); + List get selectedUsers => + state.where((element) => element.selected).toList(); Future init() async { - final chatUsers = (await _streamApiRepository.getChatUsers()).map((e) => ChatUserState(e)).toList(); + final chatUsers = (await _streamApiRepository.getChatUsers()) + .map((e) => ChatUserState(e)) + .toList(); emit(chatUsers); } void selectUser(ChatUserState chatUser) { - final index = state.indexWhere((element) => element.chatUser.id == chatUser.chatUser.id); - state[index] = ChatUserState(state[index].chatUser, selected: !chatUser.selected); + final index = state + .indexWhere((element) => element.chatUser.id == chatUser.chatUser.id); + state[index] = + ChatUserState(state[index].chatUser, selected: !chatUser.selected); emit(List.from(state)); } Future createFriendChannel(ChatUserState chatUserState) async { - return await _streamApiRepository.createSimpleChat(chatUserState.chatUser.id); + return await _streamApiRepository + .createSimpleChat(chatUserState.chatUser.id); } } diff --git a/packages/chatty/lib/ui/home/chat/selection/friends_selection_view.dart b/packages/chatty/lib/ui/home/chat/selection/friends_selection_view.dart index 4cafcef..d7c0d1f 100644 --- a/packages/chatty/lib/ui/home/chat/selection/friends_selection_view.dart +++ b/packages/chatty/lib/ui/home/chat/selection/friends_selection_view.dart @@ -7,8 +7,11 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; class FriendsSelectionView extends StatelessWidget { - void _createFriendChannel(BuildContext context, ChatUserState chatUserState) async { - final channel = await context.read().createFriendChannel(chatUserState); + void _createFriendChannel( + BuildContext context, ChatUserState chatUserState) async { + final channel = await context + .read() + .createFriendChannel(chatUserState); pushAndReplaceToPage( context, Scaffold( @@ -26,19 +29,23 @@ class FriendsSelectionView extends StatelessWidget { final accentColor = Theme.of(context).accentColor; return MultiBlocProvider( providers: [ - BlocProvider(create: (context) => FriendsSelectionCubit(context.read())..init()), + BlocProvider( + create: (context) => FriendsSelectionCubit(context.read())..init()), BlocProvider(create: (_) => FriendsGroupCubit()), ], child: BlocBuilder(builder: (context, isGroup) { - return BlocBuilder>(builder: (context, snapshot) { - final selectedUsers = context.read().selectedUsers; + return BlocBuilder>( + builder: (context, snapshot) { + final selectedUsers = + context.read().selectedUsers; return Scaffold( floatingActionButton: isGroup && selectedUsers.isNotEmpty ? FloatingActionButton( child: Icon(Icons.arrow_right_alt_rounded), onPressed: () { - pushAndReplaceToPage(context, GroupSelectionView(selectedUsers)); + pushAndReplaceToPage( + context, GroupSelectionView(selectedUsers)); }) : null, backgroundColor: Theme.of(context).canvasColor, @@ -91,12 +98,14 @@ class FriendsSelectionView extends StatelessWidget { backgroundColor: accentColor, child: Icon(Icons.group_outlined), ), - title: Text('Create group', style: TextStyle(fontWeight: FontWeight.w700)), + title: Text('Create group', + style: TextStyle(fontWeight: FontWeight.w700)), subtitle: Text('Talk with 2 or more contacts'), ) else if (isGroup && selectedUsers.isEmpty) Padding( - padding: const EdgeInsets.only(top: 15.0, left: 20.0, bottom: 20), + padding: const EdgeInsets.only( + top: 15.0, left: 20.0, bottom: 20), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -123,7 +132,8 @@ class FriendsSelectionView extends StatelessWidget { itemBuilder: (context, index) { final chatUserState = selectedUsers[index]; return Padding( - padding: const EdgeInsets.symmetric(horizontal: 13.0), + padding: const EdgeInsets.symmetric( + horizontal: 13.0), child: Stack( clipBehavior: Clip.none, children: [ @@ -132,7 +142,8 @@ class FriendsSelectionView extends StatelessWidget { children: [ CircleAvatar( radius: 30, - backgroundImage: NetworkImage(chatUserState.chatUser.image), + backgroundImage: NetworkImage( + chatUserState.chatUser.image), ), Text(chatUserState.chatUser.name), ], @@ -141,11 +152,14 @@ class FriendsSelectionView extends StatelessWidget { bottom: 40, right: -4, child: InkWell( - onTap: () => context.read().selectUser(chatUserState), + onTap: () => context + .read() + .selectUser(chatUserState), child: CircleAvatar( radius: 9, backgroundColor: accentColor, - child: Icon(Icons.close_rounded, size: 12), + child: Icon(Icons.close_rounded, + size: 12), ), ), ), @@ -163,7 +177,8 @@ class FriendsSelectionView extends StatelessWidget { _createFriendChannel(context, chatUserState); }, leading: CircleAvatar( - backgroundImage: NetworkImage(chatUserState.chatUser.image), + backgroundImage: + NetworkImage(chatUserState.chatUser.image), ), title: Text(chatUserState.chatUser.name), trailing: isGroup @@ -171,7 +186,9 @@ class FriendsSelectionView extends StatelessWidget { value: chatUserState.selected, onChanged: (val) { print('select user for group'); - context.read().selectUser(chatUserState); + context + .read() + .selectUser(chatUserState); }, ) : null, diff --git a/packages/chatty/lib/ui/home/chat/selection/group_selection_view.dart b/packages/chatty/lib/ui/home/chat/selection/group_selection_view.dart index 7f336f9..70c100b 100644 --- a/packages/chatty/lib/ui/home/chat/selection/group_selection_view.dart +++ b/packages/chatty/lib/ui/home/chat/selection/group_selection_view.dart @@ -21,7 +21,8 @@ class GroupSelectionView extends StatelessWidget { context.read(), context.read(), ), - child: BlocConsumer(listener: (context, snapshot) { + child: BlocConsumer( + listener: (context, snapshot) { if (snapshot.channel != null) { pushAndReplaceToPage( context, @@ -76,9 +77,12 @@ class GroupSelectionView extends StatelessWidget { vertical: 20, ), child: TextField( - controller: context.read().nameTextController, + controller: + context.read().nameTextController, decoration: InputDecoration( - fillColor: Theme.of(context).bottomNavigationBarTheme.backgroundColor, + fillColor: Theme.of(context) + .bottomNavigationBarTheme + .backgroundColor, hintText: 'Name of the group', hintStyle: TextStyle( fontSize: 13, @@ -99,7 +103,8 @@ class GroupSelectionView extends StatelessWidget { children: [ CircleAvatar( radius: 30, - backgroundImage: NetworkImage(chatUserState.chatUser.image), + backgroundImage: + NetworkImage(chatUserState.chatUser.image), ), Text(chatUserState.chatUser.name), ], diff --git a/packages/chatty/lib/ui/home/home_view.dart b/packages/chatty/lib/ui/home/home_view.dart index 5199415..5d30d87 100644 --- a/packages/chatty/lib/ui/home/home_view.dart +++ b/packages/chatty/lib/ui/home/home_view.dart @@ -61,7 +61,9 @@ class HomeNavigationBar extends StatelessWidget { child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(25), - color: Theme.of(context).bottomNavigationBarTheme.backgroundColor, + color: Theme.of(context) + .bottomNavigationBarTheme + .backgroundColor, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, @@ -122,8 +124,10 @@ class _HomeNavItem extends StatelessWidget { @override Widget build(BuildContext context) { - final selectedColor = Theme.of(context).bottomNavigationBarTheme.selectedItemColor; - final unselectedColor = Theme.of(context).bottomNavigationBarTheme.unselectedItemColor; + final selectedColor = + Theme.of(context).bottomNavigationBarTheme.selectedItemColor; + final unselectedColor = + Theme.of(context).bottomNavigationBarTheme.unselectedItemColor; final color = selected ? selectedColor : unselectedColor; return GestureDetector( onTap: onTap, diff --git a/packages/chatty/lib/ui/home/settings/settings_view.dart b/packages/chatty/lib/ui/home/settings/settings_view.dart index 77ce96a..17020ff 100644 --- a/packages/chatty/lib/ui/home/settings/settings_view.dart +++ b/packages/chatty/lib/ui/home/settings/settings_view.dart @@ -16,7 +16,8 @@ class SettingsView extends StatelessWidget { return MultiBlocProvider( providers: [ BlocProvider( - create: (_) => SettingsSwitchCubit(context.read().isDark), + create: (_) => + SettingsSwitchCubit(context.read().isDark), ), BlocProvider( create: (_) => SettingsLogoutCubit(context.read()), @@ -75,11 +76,14 @@ class SettingsView extends StatelessWidget { ), ), Spacer(), - BlocBuilder(builder: (context, snapshot) { + BlocBuilder( + builder: (context, snapshot) { return Switch( value: snapshot, onChanged: (val) { - context.read().onChangeDarkMode(val); + context + .read() + .onChangeDarkMode(val); context.read().updateTheme(val); }, ); diff --git a/packages/chatty/lib/ui/profile_verify/profile_verify_view.dart b/packages/chatty/lib/ui/profile_verify/profile_verify_view.dart index 3a475c3..4000202 100644 --- a/packages/chatty/lib/ui/profile_verify/profile_verify_view.dart +++ b/packages/chatty/lib/ui/profile_verify/profile_verify_view.dart @@ -13,7 +13,8 @@ class ProfileVerifyView extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (context) => ProfileVerifyCubit(context.read(), context.read()), - child: BlocConsumer(listener: (context, snapshot) { + child: BlocConsumer( + listener: (context, snapshot) { if (snapshot.success) { pushAndReplaceToPage(context, HomeView()); } @@ -59,9 +60,12 @@ class ProfileVerifyView extends StatelessWidget { vertical: 20, ), child: TextField( - controller: context.read().nameController, + controller: + context.read().nameController, decoration: InputDecoration( - fillColor: Theme.of(context).bottomNavigationBarTheme.backgroundColor, + fillColor: Theme.of(context) + .bottomNavigationBarTheme + .backgroundColor, hintText: 'Or just how people now you', hintStyle: TextStyle( fontSize: 13, diff --git a/packages/chatty/lib/ui/sign_in/sign_in_view.dart b/packages/chatty/lib/ui/sign_in/sign_in_view.dart index 41ff317..d8f2a9e 100644 --- a/packages/chatty/lib/ui/sign_in/sign_in_view.dart +++ b/packages/chatty/lib/ui/sign_in/sign_in_view.dart @@ -11,7 +11,8 @@ class SignInView extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (context) => SignInCubit(context.read()), - child: BlocConsumer(listener: (context, snapshot) { + child: + BlocConsumer(listener: (context, snapshot) { if (snapshot == SignInState.none) { pushAndReplaceToPage(context, ProfileVerifyView()); } else { @@ -59,7 +60,9 @@ class SignInView extends StatelessWidget { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), - color: Theme.of(context).bottomNavigationBarTheme.backgroundColor, + color: Theme.of(context) + .bottomNavigationBarTheme + .backgroundColor, child: InkWell( onTap: () { context.read().signIn(); diff --git a/packages/imessage/lib/utils.dart b/packages/imessage/lib/utils.dart index f78e450..4600512 100644 --- a/packages/imessage/lib/utils.dart +++ b/packages/imessage/lib/utils.dart @@ -49,7 +49,6 @@ class CupertinoCircleAvatar extends StatelessWidget { } } - class Divider extends StatelessWidget { const Divider({ Key key, diff --git a/packages/stream_chat_v1/lib/advanced_options_page.dart b/packages/stream_chat_v1/lib/advanced_options_page.dart index a654f3d..29fe270 100644 --- a/packages/stream_chat_v1/lib/advanced_options_page.dart +++ b/packages/stream_chat_v1/lib/advanced_options_page.dart @@ -17,13 +17,13 @@ class _AdvancedOptionsPageState extends State { final _formKey = GlobalKey(); final TextEditingController _apiKeyController = TextEditingController(); - String _apiKeyError; + String? _apiKeyError; final TextEditingController _userIdController = TextEditingController(); - String _userIdError; + String? _userIdError; final TextEditingController _userTokenController = TextEditingController(); - String _userTokenError; + String? _userTokenError; final TextEditingController _usernameController = TextEditingController(); @@ -73,7 +73,7 @@ class _AdvancedOptionsPageState extends State { } }, validator: (value) { - if (value.isEmpty) { + if (value!.isEmpty) { setState(() { _apiKeyError = 'Please enter the Chat API Key'.toUpperCase(); @@ -119,7 +119,7 @@ class _AdvancedOptionsPageState extends State { } }, validator: (value) { - if (value.isEmpty) { + if (value!.isEmpty) { setState(() { _userIdError = 'Please enter the User ID'.toUpperCase(); @@ -165,7 +165,7 @@ class _AdvancedOptionsPageState extends State { }, controller: _userTokenController, validator: (value) { - if (value.isEmpty) { + if (value!.isEmpty) { setState(() { _userTokenError = 'Please enter the user token'.toUpperCase(); @@ -221,14 +221,22 @@ class _AdvancedOptionsPageState extends State { ), ), Spacer(), - RaisedButton( - color: Theme.of(context).brightness == Brightness.light - ? StreamChatTheme.of(context).colorTheme.accentBlue - : Colors.white, - elevation: 0, - padding: const EdgeInsets.symmetric(vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(26), + ElevatedButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + Theme.of(context).brightness == Brightness.light + ? StreamChatTheme.of(context) + .colorTheme + .accentBlue + : Colors.white), + elevation: MaterialStateProperty.all(0), + padding: MaterialStateProperty.all( + const EdgeInsets.symmetric(vertical: 16)), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(26), + ), + ), ), child: Text( 'Login', @@ -243,7 +251,7 @@ class _AdvancedOptionsPageState extends State { if (loading) { return; } - if (_formKey.currentState.validate()) { + if (_formKey.currentState!.validate()) { final apiKey = _apiKeyController.text; final userId = _userIdController.text; final userToken = _userTokenController.text; diff --git a/packages/stream_chat_v1/lib/channel_file_display_screen.dart b/packages/stream_chat_v1/lib/channel_file_display_screen.dart index a3e1c67..806f184 100644 --- a/packages/stream_chat_v1/lib/channel_file_display_screen.dart +++ b/packages/stream_chat_v1/lib/channel_file_display_screen.dart @@ -6,16 +6,16 @@ class ChannelFileDisplayScreen extends StatefulWidget { /// Sorting is based on field and direction, multiple sorting options can be provided. /// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count. /// Direction can be ascending or descending. - final List sortOptions; + final List? sortOptions; /// Pagination parameters /// limit: the number of users to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams paginationParams; + final PaginationParams? paginationParams; /// The builder used when the file list is empty. - final WidgetBuilder emptyBuilder; + final WidgetBuilder? emptyBuilder; const ChannelFileDisplayScreen({ this.sortOptions, @@ -34,16 +34,14 @@ class _ChannelFileDisplayScreenState extends State { super.initState(); final messageSearchBloc = MessageSearchBloc.of(context); messageSearchBloc.search( - filter: { - 'cid': { - r'$in': [StreamChannel.of(context).channel.cid] - } - }, - messageFilter: { - 'attachments.type': { - r'$in': ['file'], - }, - }, + filter: Filter.in_( + 'cid', + [StreamChannel.of(context).channel.cid!], + ), + messageFilter: Filter.in_( + 'attachments.type', + ['file'], + ), sort: widget.sortOptions, pagination: widget.paginationParams, ); @@ -95,9 +93,9 @@ class _ChannelFileDisplayScreenState extends State { ); } - if (snapshot.data.isEmpty) { + if (snapshot.data!.isEmpty) { if (widget.emptyBuilder != null) { - return widget.emptyBuilder(context); + return widget.emptyBuilder!(context); } return Center( child: Column( @@ -134,7 +132,7 @@ class _ChannelFileDisplayScreenState extends State { final media = {}; - for (var item in snapshot.data) { + for (var item in snapshot.data!) { item.message.attachments.where((e) => e.type == 'file').forEach((e) { media[e] = item.message; }); @@ -142,18 +140,16 @@ class _ChannelFileDisplayScreenState extends State { return LazyLoadScrollView( onEndOfPage: () => messageSearchBloc.search( - filter: { - 'cid': { - r'$in': [StreamChannel.of(context).channel.cid] - } - }, - messageFilter: { - 'attachments.type': { - r'$in': ['file'] - }, - }, + filter: Filter.in_( + 'cid', + [StreamChannel.of(context).channel.cid!], + ), + messageFilter: Filter.in_( + 'attachments.type', + ['file'], + ), sort: widget.sortOptions, - pagination: widget.paginationParams.copyWith( + pagination: widget.paginationParams!.copyWith( offset: messageSearchBloc.messageResponses?.length ?? 0, ), ), diff --git a/packages/stream_chat_v1/lib/channel_media_display_screen.dart b/packages/stream_chat_v1/lib/channel_media_display_screen.dart index a0ed072..5c33622 100644 --- a/packages/stream_chat_v1/lib/channel_media_display_screen.dart +++ b/packages/stream_chat_v1/lib/channel_media_display_screen.dart @@ -7,20 +7,23 @@ class ChannelMediaDisplayScreen extends StatefulWidget { /// Sorting is based on field and direction, multiple sorting options can be provided. /// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count. /// Direction can be ascending or descending. - final List sortOptions; + final List? sortOptions; /// Pagination parameters /// limit: the number of users to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams paginationParams; + final PaginationParams? paginationParams; /// The builder used when the file list is empty. - final WidgetBuilder emptyBuilder; + final WidgetBuilder? emptyBuilder; - final ShowMessageCallback onShowMessage; + final ShowMessageCallback? onShowMessage; + + final MessageTheme messageTheme; const ChannelMediaDisplayScreen({ + required this.messageTheme, this.sortOptions, this.paginationParams, this.emptyBuilder, @@ -33,23 +36,21 @@ class ChannelMediaDisplayScreen extends StatefulWidget { } class _ChannelMediaDisplayScreenState extends State { - Map controllerCache = {}; + Map controllerCache = {}; @override void initState() { super.initState(); final messageSearchBloc = MessageSearchBloc.of(context); messageSearchBloc.search( - filter: { - 'cid': { - r'$in': [StreamChannel.of(context).channel.cid], - } - }, - messageFilter: { - 'attachments.type': { - r'$in': ['image', 'video'] - }, - }, + filter: Filter.in_( + 'cid', + [StreamChannel.of(context).channel.cid!], + ), + messageFilter: Filter.in_( + 'attachments.type', + ['image', 'video'], + ), sort: widget.sortOptions, pagination: widget.paginationParams, ); @@ -102,9 +103,9 @@ class _ChannelMediaDisplayScreenState extends State { ); } - if (snapshot.data.isEmpty) { + if (snapshot.data!.isEmpty) { if (widget.emptyBuilder != null) { - return widget.emptyBuilder(context); + return widget.emptyBuilder!(context); } return Center( child: Column( @@ -141,18 +142,18 @@ class _ChannelMediaDisplayScreenState extends State { final media = <_AssetPackage>[]; - for (var item in snapshot.data) { + for (var item in snapshot.data!) { item.message.attachments .where((e) => (e.type == 'image' || e.type == 'video') && e.ogScrapeUrl == null) .forEach((e) { - VideoPlayerController controller; + VideoPlayerController? controller; if (e.type == 'video') { var cachedController = controllerCache[e.assetUrl]; if (cachedController == null) { - controller = VideoPlayerController.network(e.assetUrl); + controller = VideoPlayerController.network(e.assetUrl!); controller.initialize(); controllerCache[e.assetUrl] = controller; } else { @@ -165,18 +166,16 @@ class _ChannelMediaDisplayScreenState extends State { return LazyLoadScrollView( onEndOfPage: () => messageSearchBloc.search( - filter: { - 'cid': { - r'$in': [StreamChannel.of(context).channel.cid] - } - }, - messageFilter: { - 'attachments.type': { - r'$in': ['image', 'video'] - }, - }, + filter: Filter.in_( + 'cid', + [StreamChannel.of(context).channel.cid!], + ), + messageFilter: Filter.in_( + 'attachments.type', + ['image', 'video'], + ), sort: widget.sortOptions, - pagination: widget.paginationParams.copyWith( + pagination: widget.paginationParams!.copyWith( offset: messageSearchBloc.messageResponses?.length ?? 0, ), ), @@ -199,8 +198,7 @@ class _ChannelMediaDisplayScreenState extends State { media.map((e) => e.attachment).toList(), startIndex: position, message: media[position].message, - sentAt: media[position].message.createdAt, - userName: media[position].message.user.name, + userName: media[position].message.user!.name, onShowMessage: widget.onShowMessage, ), ), @@ -217,9 +215,10 @@ class _ChannelMediaDisplayScreenState extends State { MediaQuery.of(context).size.width * 0.8, MediaQuery.of(context).size.height * 0.3, ), + messageTheme: widget.messageTheme, ), ) - : VideoPlayer(media[position].videoPlayer), + : VideoPlayer(media[position].videoPlayer!), ), ); }, @@ -235,7 +234,7 @@ class _ChannelMediaDisplayScreenState extends State { void dispose() { super.dispose(); for (var c in controllerCache.values) { - c.dispose(); + c!.dispose(); } } } @@ -243,7 +242,7 @@ class _ChannelMediaDisplayScreenState extends State { class _AssetPackage { Attachment attachment; Message message; - VideoPlayerController videoPlayer; + VideoPlayerController? videoPlayer; _AssetPackage(this.attachment, this.message, this.videoPlayer); } diff --git a/packages/stream_chat_v1/lib/chat_info_screen.dart b/packages/stream_chat_v1/lib/chat_info_screen.dart index 38fbb70..2ba2839 100644 --- a/packages/stream_chat_v1/lib/chat_info_screen.dart +++ b/packages/stream_chat_v1/lib/chat_info_screen.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart' show IterableExtension; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; @@ -11,16 +12,22 @@ import 'routes/routes.dart'; /// Detail screen for a 1:1 chat correspondence class ChatInfoScreen extends StatefulWidget { /// User in consideration - final User user; + final User? user; - const ChatInfoScreen({Key key, this.user}) : super(key: key); + final MessageTheme messageTheme; + + const ChatInfoScreen({ + Key? key, + required this.messageTheme, + this.user, + }) : super(key: key); @override _ChatInfoScreenState createState() => _ChatInfoScreenState(); } class _ChatInfoScreenState extends State { - ValueNotifier mutedBool = ValueNotifier(false); + ValueNotifier mutedBool = ValueNotifier(false); @override void initState() { @@ -48,9 +55,9 @@ class _ChatInfoScreenState extends State { if ([ 'admin', 'owner', - ].contains(channel.state.members - .firstWhere((m) => m.userId == channel.client.state.user.id, - orElse: () => null) + ].contains(channel.state!.members + .firstWhereOrNull( + (m) => m.userId == channel.client.state.user!.id) ?.role)) _buildDeleteListTile(), ], @@ -70,7 +77,7 @@ class _ChatInfoScreenState extends State { Padding( padding: const EdgeInsets.all(16.0), child: UserAvatar( - user: widget.user, + user: widget.user!, constraints: BoxConstraints( maxWidth: 72.0, maxHeight: 72.0, @@ -80,19 +87,19 @@ class _ChatInfoScreenState extends State { ), ), Text( - widget.user.name, + widget.user!.name, style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold), ), SizedBox(height: 7.0), _buildConnectedTitleState(), SizedBox(height: 15.0), OptionListTile( - title: '@${widget.user.id}', + title: '@${widget.user!.id}', tileColor: StreamChatTheme.of(context).colorTheme.whiteSnow, trailing: Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Text( - widget.user.name, + widget.user!.name, style: TextStyle( color: StreamChatTheme.of(context) .colorTheme @@ -155,15 +162,15 @@ class _ChatInfoScreenState extends State { ), trailing: snapshot.data == null ? CircularProgressIndicator() - : ValueListenableBuilder( + : ValueListenableBuilder( valueListenable: mutedBool, builder: (context, value, _) { return CupertinoSwitch( - value: value, + value: value!, onChanged: (val) { mutedBool.value = val; - if (snapshot.data) { + if (snapshot.data!) { channel.channel.unmute(); } else { channel.channel.mute(); @@ -217,6 +224,7 @@ class _ChatInfoScreenState extends State { channel: channel, child: MessageSearchBloc( child: ChannelMediaDisplayScreen( + messageTheme: widget.messageTheme, sortOptions: [ SortOption( 'created_at', @@ -387,7 +395,7 @@ class _ChatInfoScreenState extends State { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - if (widget.user.online) + if (widget.user!.online) Material( type: MaterialType.circle, child: Container( @@ -404,7 +412,7 @@ class _ChatInfoScreenState extends State { color: StreamChatTheme.of(context).colorTheme.white, ), alternativeWidget, - if (widget.user.online) + if (widget.user!.online) SizedBox( width: 24.0, ), @@ -414,8 +422,8 @@ class _ChatInfoScreenState extends State { } class _SharedGroupsScreen extends StatefulWidget { - final User mainUser; - final User otherUser; + final User? mainUser; + final User? otherUser; _SharedGroupsScreen(this.mainUser, this.otherUser); @@ -445,20 +453,10 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> { ), body: StreamBuilder>( stream: chat.client.queryChannels( - filter: { - r'$and': [ - { - 'members': { - r'$in': [widget.otherUser.id], - }, - }, - { - 'members': { - r'$in': [widget.mainUser.id], - }, - } - ], - }, + filter: Filter.and([ + Filter.in_('members', [widget.otherUser!.id]), + Filter.in_('members', [widget.mainUser!.id]), + ]), ), builder: (context, snapshot) { if (!snapshot.hasData) { @@ -467,7 +465,7 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> { ); } - if (snapshot.data.isEmpty) { + if (snapshot.data!.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -501,11 +499,11 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> { ); } - final channels = snapshot.data + final channels = snapshot.data! .where((c) => - c.state.members.any((m) => - m.userId != widget.mainUser.id && - m.userId != widget.otherUser.id) || + c.state!.members.any((m) => + m.userId != widget.mainUser!.id && + m.userId != widget.otherUser!.id) || !c.isDistinct) .toList(); @@ -525,24 +523,24 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> { Widget _buildListTile(Channel channel) { var extraData = channel.extraData; - var members = channel.state.members; + var members = channel.state!.members; var textStyle = TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold); return Container( height: 64.0, child: LayoutBuilder(builder: (context, constraints) { - String title; + String? title; if (extraData['name'] == null) { final otherMembers = members.where( - (member) => member.userId != StreamChat.of(context).user.id); + (member) => member.userId != StreamChat.of(context).user!.id); if (otherMembers.isNotEmpty) { final maxWidth = constraints.maxWidth; - final maxChars = maxWidth / textStyle.fontSize; + final maxChars = maxWidth / textStyle.fontSize!; var currentChars = 0; final currentMembers = []; otherMembers.forEach((element) { - final newLength = currentChars + element.user.name.length; + final newLength = currentChars + element.user!.name.length; if (newLength < maxChars) { currentChars = newLength; currentMembers.add(element); @@ -552,7 +550,7 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> { final exceedingMembers = otherMembers.length - currentMembers.length; title = - '${currentMembers.map((e) => e.user.name).join(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}'; + '${currentMembers.map((e) => e.user!.name).join(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}'; } else { title = 'No title'; } @@ -575,7 +573,7 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> { ), Expanded( child: Text( - title, + title!, style: textStyle, )), Padding( diff --git a/packages/stream_chat_v1/lib/chips_input_text_field.dart b/packages/stream_chat_v1/lib/chips_input_text_field.dart index 0fc0791..c36e4ae 100644 --- a/packages/stream_chat_v1/lib/chips_input_text_field.dart +++ b/packages/stream_chat_v1/lib/chips_input_text_field.dart @@ -6,18 +6,18 @@ typedef OnChipAdded = void Function(T chip); typedef OnChipRemoved = void Function(T chip); class ChipsInputTextField extends StatefulWidget { - final TextEditingController controller; - final FocusNode focusNode; - final ValueChanged onInputChanged; + final TextEditingController? controller; + final FocusNode? focusNode; + final ValueChanged? onInputChanged; final ChipBuilder chipBuilder; - final OnChipAdded onChipAdded; - final OnChipRemoved onChipRemoved; + final OnChipAdded? onChipAdded; + final OnChipRemoved? onChipRemoved; final String hint; const ChipsInputTextField({ - Key key, - @required this.chipBuilder, - @required this.controller, + Key? key, + required this.chipBuilder, + required this.controller, this.onInputChanged, this.focusNode, this.onChipAdded, @@ -35,7 +35,7 @@ class ChipInputTextFieldState extends State> { void addItem(T item) { setState(() => _chips.add(item)); - if (widget.onChipAdded != null) widget.onChipAdded(item); + if (widget.onChipAdded != null) widget.onChipAdded!(item); } void removeItem(T item) { @@ -43,7 +43,7 @@ class ChipInputTextFieldState extends State> { _chips.remove(item); if (_chips.isEmpty) resumeItemAddition(); }); - if (widget.onChipRemoved != null) widget.onChipRemoved(item); + if (widget.onChipRemoved != null) widget.onChipRemoved!(item); } void pauseItemAddition() { diff --git a/packages/stream_chat_v1/lib/group_chat_details_screen.dart b/packages/stream_chat_v1/lib/group_chat_details_screen.dart index afc9d12..a1a92b8 100644 --- a/packages/stream_chat_v1/lib/group_chat_details_screen.dart +++ b/packages/stream_chat_v1/lib/group_chat_details_screen.dart @@ -6,11 +6,11 @@ import 'main.dart'; import 'routes/routes.dart'; class GroupChatDetailsScreen extends StatefulWidget { - final List selectedUsers; + final List? selectedUsers; const GroupChatDetailsScreen({ - Key key, - @required this.selectedUsers, + Key? key, + required this.selectedUsers, }) : super(key: key); @override @@ -20,14 +20,14 @@ class GroupChatDetailsScreen extends StatefulWidget { class _GroupChatDetailsScreenState extends State { final _selectedUsers = []; - TextEditingController _groupNameController; + TextEditingController? _groupNameController; bool _isGroupNameEmpty = true; int get _totalUsers => _selectedUsers.length; void _groupNameListener() { - final name = _groupNameController.text; + final name = _groupNameController!.text; if (mounted) { setState(() { _isGroupNameEmpty = name.isEmpty; @@ -38,7 +38,7 @@ class _GroupChatDetailsScreenState extends State { @override void initState() { super.initState(); - _selectedUsers.addAll(widget.selectedUsers); + _selectedUsers.addAll(widget.selectedUsers!); _groupNameController = TextEditingController() ..addListener(_groupNameListener); } @@ -124,13 +124,13 @@ class _GroupChatDetailsScreenState extends State { ? null : () async { try { - final groupName = _groupNameController.text; + final groupName = _groupNameController!.text; final client = StreamChat.of(context).client; final channel = client.channel('messaging', id: Uuid().v4(), extraData: { 'members': [ - client.state.user.id, + client.state.user!.id, ..._selectedUsers.map((e) => e.id), ], 'name': groupName, @@ -306,7 +306,7 @@ class _GroupChatDetailsScreenState extends State { Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - FlatButton( + TextButton( child: Text( 'OK', style: StreamChatTheme.of(context) diff --git a/packages/stream_chat_v1/lib/group_info_screen.dart b/packages/stream_chat_v1/lib/group_info_screen.dart index efe1808..00e3966 100644 --- a/packages/stream_chat_v1/lib/group_info_screen.dart +++ b/packages/stream_chat_v1/lib/group_info_screen.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:collection/collection.dart' show IterableExtension; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; @@ -14,34 +15,41 @@ import 'main.dart'; import 'routes/routes.dart'; class GroupInfoScreen extends StatefulWidget { + final MessageTheme messageTheme; + + const GroupInfoScreen({ + Key? key, + required this.messageTheme, + }) : super(key: key); + @override _GroupInfoScreenState createState() => _GroupInfoScreenState(); } class _GroupInfoScreenState extends State { - TextEditingController _nameController; + TextEditingController? _nameController; - TextEditingController _searchController; + TextEditingController? _searchController; String _userNameQuery = ''; - Timer _debounce; - Function modalSetStateCallback; + Timer? _debounce; + Function? modalSetStateCallback; final FocusNode _focusNode = FocusNode(); bool listExpanded = false; - ValueNotifier mutedBool = ValueNotifier(false); + ValueNotifier mutedBool = ValueNotifier(false); void _userNameListener() { - if (_searchController.text == _userNameQuery) { + if (_searchController!.text == _userNameQuery) { return; } - if (_debounce?.isActive ?? false) _debounce.cancel(); + if (_debounce?.isActive ?? false) _debounce!.cancel(); _debounce = Timer(const Duration(milliseconds: 350), () { if (mounted && modalSetStateCallback != null) { - modalSetStateCallback(() { - _userNameQuery = _searchController.text; + modalSetStateCallback!(() { + _userNameQuery = _searchController!.text; }); } }); @@ -55,7 +63,7 @@ class _GroupInfoScreenState extends State { TextEditingValue(text: channel.channel.extraData['name'] ?? '')); _searchController = TextEditingController()..addListener(_userNameListener); - _nameController.addListener(() { + _nameController!.addListener(() { setState(() {}); }); mutedBool = ValueNotifier(StreamChannel.of(context).channel.isMuted); @@ -66,7 +74,7 @@ class _GroupInfoScreenState extends State { var channel = StreamChannel.of(context); return StreamBuilder>( - stream: channel.channel.state.membersStream, + stream: channel.channel.state!.membersStream, builder: (context, snapshot) { if (!snapshot.hasData) { return Container( @@ -75,9 +83,8 @@ class _GroupInfoScreenState extends State { ); } - var userMember = snapshot.data.firstWhere( - (e) => e.user.id == StreamChat.of(context).user.id, - orElse: () => null, + var userMember = snapshot.data!.firstWhereOrNull( + (e) => e.user!.id == StreamChat.of(context).user!.id, ); var isOwner = userMember?.role == 'owner'; @@ -111,9 +118,9 @@ class _GroupInfoScreenState extends State { _getChannelName( 2 * MediaQuery.of(context).size.width / 3, members: snapshot.data, - extraData: state.data.channel.extraData, + extraData: state.data!.channel!.extraData, maxFontSize: 16.0, - ), + )!, style: TextStyle( color: StreamChatTheme.of(context).colorTheme.black, fontSize: 16, @@ -126,7 +133,7 @@ class _GroupInfoScreenState extends State { height: 3.0, ), Text( - '${channel.channel.memberCount} Members, ${snapshot?.data?.where((e) => e.user.online)?.length ?? 0} Online', + '${channel.channel.memberCount} Members, ${snapshot.data?.where((e) => e.user!.online).length ?? 0} Online', style: TextStyle( color: StreamChatTheme.of(context) .colorTheme @@ -158,7 +165,7 @@ class _GroupInfoScreenState extends State { ), body: ListView( children: [ - _buildMembers(snapshot.data), + _buildMembers(snapshot.data!), Container( height: 8.0, color: StreamChatTheme.of(context).colorTheme.greyGainsboro, @@ -197,9 +204,8 @@ class _GroupInfoScreenState extends State { return Material( child: InkWell( onTap: () { - final userMember = groupMembers.firstWhere( - (e) => e.user.id == StreamChat.of(context).user.id, - orElse: () => null, + final userMember = groupMembers.firstWhereOrNull( + (e) => e.user!.id == StreamChat.of(context).user!.id, ); _showUserInfoModal(member.user, userMember?.role == 'owner'); }, @@ -213,7 +219,7 @@ class _GroupInfoScreenState extends State { padding: const EdgeInsets.symmetric( horizontal: 8.0, vertical: 12.0), child: UserAvatar( - user: member.user, + user: member.user!, constraints: BoxConstraints( maxHeight: 40.0, maxWidth: 40.0), ), @@ -224,14 +230,14 @@ class _GroupInfoScreenState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - member.user.name, + member.user!.name, style: TextStyle(fontWeight: FontWeight.bold), ), SizedBox( height: 1.0, ), Text( - _getLastSeen(member.user), + _getLastSeen(member.user!), style: TextStyle( color: StreamChatTheme.of(context) .colorTheme @@ -371,7 +377,7 @@ class _GroupInfoScreenState extends State { ), ), if ((channelName == null) || - (channelName != _nameController.text.trim())) + (channelName != _nameController!.text.trim())) Row( mainAxisSize: MainAxisSize.min, children: [ @@ -379,12 +385,12 @@ class _GroupInfoScreenState extends State { child: StreamSvgIcon.closeSmall(), onTap: () { setState(() { - _nameController.text = _getChannelName( + _nameController!.text = _getChannelName( 2 * MediaQuery.of(context).size.width / 3, - members: channel.state.members, + members: channel.state!.members, extraData: channel.extraData, maxFontSize: 16.0, - ); + )!; _focusNode.unfocus(); }); }, @@ -399,10 +405,10 @@ class _GroupInfoScreenState extends State { ), onTap: () { StreamChannel.of(context).channel.update({ - 'name': _nameController.text.trim(), + 'name': _nameController!.text.trim(), }).catchError((err) { setState(() { - _nameController.text = channelName; + _nameController!.text = channelName; _focusNode.unfocus(); }); }); @@ -457,15 +463,15 @@ class _GroupInfoScreenState extends State { ), trailing: snapshot.data == null ? CircularProgressIndicator() - : ValueListenableBuilder( + : ValueListenableBuilder( valueListenable: mutedBool, builder: (context, value, _) { return CupertinoSwitch( - value: value, + value: value!, onChanged: (val) { mutedBool.value = val; - if (snapshot.data) { + if (snapshot.data!) { channel.channel.unmute(); } else { channel.channel.mute(); @@ -502,6 +508,7 @@ class _GroupInfoScreenState extends State { channel: channel, child: MessageSearchBloc( child: ChannelMediaDisplayScreen( + messageTheme: widget.messageTheme, sortOptions: [ SortOption( 'created_at', @@ -609,7 +616,7 @@ class _GroupInfoScreenState extends State { ); if (res == true) { final channel = StreamChannel.of(context).channel; - await channel.removeMembers([StreamChat.of(context).user.id]); + await channel.removeMembers([StreamChat.of(context).user!.id]); Navigator.pop(context); } }, @@ -647,7 +654,7 @@ class _GroupInfoScreenState extends State { child: UserListView( selectedUsers: {}, onUserTap: (user, _) async { - _searchController.clear(); + _searchController!.clear(); await channel.addMembers([user.id]); Navigator.pop(context); @@ -657,18 +664,17 @@ class _GroupInfoScreenState extends State { pagination: PaginationParams( limit: 25, ), - filter: { - if (_searchController.text.isNotEmpty) - 'name': { - r'$autocomplete': _userNameQuery, - }, - 'id': { - r'$nin': [ - StreamChat.of(context).user.id, - ...channel.state.members.map((e) => e.userId), - ], - }, - }, + filter: Filter.and( + [ + if (_searchController!.text.isNotEmpty) + Filter.autoComplete('name', _userNameQuery), + Filter.notIn('id', [ + StreamChat.of(context).user!.id, + ...channel.state!.members.map(((e) => e.userId!) + as Object Function(Member)), + ]), + ], + ), sort: [ SortOption( 'name', @@ -778,7 +784,7 @@ class _GroupInfoScreenState extends State { ); } - void _showUserInfoModal(User user, bool isUserAdmin) { + void _showUserInfoModal(User? user, bool isUserAdmin) { var channel = StreamChannel.of(context).channel; showModalBottomSheet( @@ -798,7 +804,7 @@ class _GroupInfoScreenState extends State { ), Center( child: Text( - user.name, + user!.name, style: TextStyle( fontSize: 16.0, fontWeight: FontWeight.bold, @@ -808,7 +814,7 @@ class _GroupInfoScreenState extends State { SizedBox( height: 5.0, ), - _buildConnectedTitleState(user), + _buildConnectedTitleState(user)!, Center( child: Padding( padding: const EdgeInsets.all(16.0), @@ -822,7 +828,7 @@ class _GroupInfoScreenState extends State { ), ), ), - if (StreamChat.of(context).user.id != user.id) + if (StreamChat.of(context).user!.id != user.id) _buildModalListTile( context, StreamSvgIcon.user( @@ -836,7 +842,7 @@ class _GroupInfoScreenState extends State { var c = client.channel('messaging', extraData: { 'members': [ user.id, - StreamChat.of(context).user.id, + StreamChat.of(context).user!.id, ], }); @@ -848,6 +854,7 @@ class _GroupInfoScreenState extends State { builder: (context) => StreamChannel( channel: c, child: ChatInfoScreen( + messageTheme: widget.messageTheme, user: user, ), ), @@ -855,7 +862,7 @@ class _GroupInfoScreenState extends State { ); }, ), - if (StreamChat.of(context).user.id != user.id) + if (StreamChat.of(context).user!.id != user.id) _buildModalListTile( context, StreamSvgIcon.message( @@ -869,7 +876,7 @@ class _GroupInfoScreenState extends State { var c = client.channel('messaging', extraData: { 'members': [ user.id, - StreamChat.of(context).user.id, + StreamChat.of(context).user!.id, ], }); @@ -887,7 +894,7 @@ class _GroupInfoScreenState extends State { }, ), if (!channel.isDistinct && - StreamChat.of(context).user.id != user.id && + StreamChat.of(context).user!.id != user.id && isUserAdmin) _buildModalListTile( context, @@ -899,7 +906,7 @@ class _GroupInfoScreenState extends State { // TODO: Add make owner implementation (Remaining from backend) }), if (!channel.isDistinct && - StreamChat.of(context).user.id != user.id && + StreamChat.of(context).user!.id != user.id && isUserAdmin) _buildModalListTile( context, @@ -934,7 +941,7 @@ class _GroupInfoScreenState extends State { ); } - Widget _buildConnectedTitleState(User user) { + Widget? _buildConnectedTitleState(User? user) { var alternativeWidget; final otherMember = user; @@ -966,7 +973,7 @@ class _GroupInfoScreenState extends State { Widget _buildModalListTile( BuildContext context, Widget leading, String title, VoidCallback onTap, - {Color color}) { + {Color? color}) { color ??= StreamChatTheme.of(context).colorTheme.black; return Material( @@ -1003,24 +1010,24 @@ class _GroupInfoScreenState extends State { ); } - String _getChannelName( + String? _getChannelName( double width, { - List members, - Map extraData, - double maxFontSize, + List? members, + required Map extraData, + double? maxFontSize, }) { - String title; + String? title; var client = StreamChat.of(context); if (extraData['name'] == null) { final otherMembers = - members.where((member) => member.user.id != client.user.id); + members!.where((member) => member.user!.id != client.user!.id); if (otherMembers.isNotEmpty) { final maxWidth = width; - final maxChars = maxWidth / maxFontSize; + final maxChars = maxWidth / maxFontSize!; var currentChars = 0; final currentMembers = []; otherMembers.forEach((element) { - final newLength = currentChars + element.user.name.length; + final newLength = currentChars + element.user!.name.length; if (newLength < maxChars) { currentChars = newLength; currentMembers.add(element); @@ -1029,7 +1036,7 @@ class _GroupInfoScreenState extends State { final exceedingMembers = otherMembers.length - currentMembers.length; title = - '${currentMembers.map((e) => e.user.name).join(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}'; + '${currentMembers.map((e) => e.user!.name).join(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}'; } else { title = 'No title'; } diff --git a/packages/stream_chat_v1/lib/main.dart b/packages/stream_chat_v1/lib/main.dart index 4e93955..ecd9957 100644 --- a/packages/stream_chat_v1/lib/main.dart +++ b/packages/stream_chat_v1/lib/main.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:collection/collection.dart' show IterableExtension; import 'package:example/chat_info_screen.dart'; import 'package:example/choose_user_page.dart'; import 'package:example/group_info_screen.dart'; @@ -38,15 +39,15 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State with TickerProviderStateMixin { - InitData _initData; + InitData? _initData; bool _animCompleted = false; - Animation _animation, _scaleAnimation; - AnimationController _animationController, _scaleAnimationController; - Animation _colorAnimation; - int timeOfStartMs; + Animation? _animation, _scaleAnimation; + AnimationController? _animationController, _scaleAnimationController; + Animation? _colorAnimation; + late int timeOfStartMs; Future _initConnection() async { - String apiKey, userId, token; + String? apiKey, userId, token; if (!kIsWeb) { final secureStorage = FlutterSecureStorage(); @@ -57,7 +58,7 @@ class _MyAppState extends State with TickerProviderStateMixin { final client = StreamChatClient( apiKey ?? kDefaultStreamApiKey, - logLevel: Level.SEVERE, + logLevel: Level.INFO, )..chatPersistenceClient = chatPersistentClient; if (userId != null) { @@ -84,7 +85,7 @@ class _MyAppState extends State with TickerProviderStateMixin { begin: 1.0, end: 1.5, ).animate(CurvedAnimation( - parent: _scaleAnimationController, + parent: _scaleAnimationController!, curve: Curves.easeInOutBack, )); @@ -98,21 +99,21 @@ class _MyAppState extends State with TickerProviderStateMixin { begin: 0.0, end: 1000.0, ).animate(CurvedAnimation( - parent: _animationController, + parent: _animationController!, curve: Curves.easeInOut, )); _colorAnimation = ColorTween( begin: Color(0xff005FFF), end: Color(0xff005FFF), ).animate(CurvedAnimation( - parent: _animationController, + parent: _animationController!, curve: Curves.easeInOut, )); _colorAnimation = ColorTween( begin: Color(0xff005FFF), end: Colors.transparent, ).animate(CurvedAnimation( - parent: _animationController, + parent: _animationController!, curve: Curves.easeInOut, )); } @@ -132,22 +133,22 @@ class _MyAppState extends State with TickerProviderStateMixin { var now = DateTime.now().millisecondsSinceEpoch; if (now - timeOfStartMs > 1500) { - SchedulerBinding.instance.addPostFrameCallback((timeStamp) { - _scaleAnimationController.forward().whenComplete(() { - _animationController.forward(); + SchedulerBinding.instance!.addPostFrameCallback((timeStamp) { + _scaleAnimationController?.forward().whenComplete(() { + _animationController?.forward(); }); }); } else { Future.delayed(Duration(milliseconds: 1500)).then((value) { - _scaleAnimationController.forward().whenComplete(() { - _animationController.forward(); + _scaleAnimationController?.forward().whenComplete(() { + _animationController?.forward(); }); }); } if (!kIsWeb) { - _initData.client.state?.totalUnreadCountStream?.listen((count) { - if (count > 0) { + _initData!.client.state.totalUnreadCountStream.listen((count) { + if (count! > 0) { FlutterAppBadger.updateBadgeCount(count); } else { FlutterAppBadger.removeBadge(); @@ -156,7 +157,7 @@ class _MyAppState extends State with TickerProviderStateMixin { } }, ); - _animationController.addStatusListener((status) { + _animationController?.addStatusListener((status) { if (status == AnimationStatus.completed) { setState(() { _animCompleted = true; @@ -174,20 +175,20 @@ class _MyAppState extends State with TickerProviderStateMixin { alignment: Alignment.center, children: [ AnimatedBuilder( - animation: _scaleAnimation, + animation: _scaleAnimation!, builder: (context, _) { return Transform.scale( - scale: _scaleAnimation.value, + scale: _scaleAnimation!.value, child: AnimatedBuilder( - animation: _colorAnimation, + animation: _colorAnimation!, builder: (context, snapshot) { return Container( alignment: Alignment.center, constraints: BoxConstraints.expand(), color: _colorAnimation == null ? Color(0xff005FFF) - : _colorAnimation.value, - child: !_animationController.isAnimating + : _colorAnimation!.value, + child: !_animationController!.isAnimating ? Lottie.asset( 'assets/floating_boat.json', alignment: Alignment.center, @@ -199,16 +200,16 @@ class _MyAppState extends State with TickerProviderStateMixin { }, ), AnimatedBuilder( - animation: _animation, + animation: _animation!, builder: (context, snapshot) { return Transform.scale( - scale: _animation.value, + scale: _animation!.value, child: Container( width: 1.0, height: 1.0, decoration: BoxDecoration( color: Colors.white - .withOpacity(1 - _animationController.value), + .withOpacity(1 - _animationController!.value), shape: BoxShape.circle, ), ), @@ -227,19 +228,19 @@ class _MyAppState extends State with TickerProviderStateMixin { children: [ if (_initData != null) PreferenceBuilder( - preference: _initData.preferences.getInt( + preference: _initData!.preferences.getInt( 'theme', defaultValue: 0, ), builder: (context, snapshot) => MaterialApp( builder: (context, child) { return StreamChat( - client: _initData.client, - onBackgroundEventReceived: (e) => - showLocalNotification(e, _initData.client.state.user.id), + client: _initData!.client, + onBackgroundEventReceived: (e) => showLocalNotification( + e, _initData!.client.state.user!.id), child: Builder( builder: (context) => AnnotatedRegion( - child: child, + child: child!, value: SystemUiOverlayStyle( systemNavigationBarColor: StreamChatTheme.of(context).colorTheme.white, @@ -260,7 +261,7 @@ class _MyAppState extends State with TickerProviderStateMixin { 1: ThemeMode.light, }[snapshot], onGenerateRoute: AppRoutes.generateRoute, - initialRoute: _initData.client.state.user == null + initialRoute: _initData!.client.state.user == null ? Routes.CHOOSE_USER : Routes.HOME, ), @@ -319,7 +320,7 @@ class _HomePageState extends State { @override Widget build(BuildContext context) { - final user = StreamChat.of(context).user; + final user = StreamChat.of(context).user!; return Scaffold( backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow, appBar: ChannelListHeader( @@ -495,19 +496,15 @@ class _HomePageState extends State { class UserMentionPage extends StatelessWidget { @override Widget build(BuildContext context) { - final user = StreamChat.of(context).user; + final user = StreamChat.of(context).user!; return MessageSearchBloc( child: MessageSearchListView( - filters: { - 'members': { - r'$in': [user.id], - }, - }, - messageFilters: { - 'mentioned_users.id': { - r'$contains': user.id, - }, - }, + filters: Filter.in_('members', [user.id]), + messageFilters: Filter.custom( + operator: r'$contains', + key: 'mentioned_users.id', + value: user.id, + ), sortOptions: [ SortOption( 'created_at', @@ -559,8 +556,8 @@ class UserMentionPage extends StatelessWidget { final client = StreamChat.of(context).client; final message = messageResponse.message; final channel = client.channel( - messageResponse.channel.type, - id: messageResponse.channel.id, + messageResponse.channel!.type, + id: messageResponse.channel!.id, ); if (channel.state == null) { await channel.watch(); @@ -585,20 +582,20 @@ class ChannelListPage extends StatefulWidget { } class _ChannelListPageState extends State { - TextEditingController _controller; + TextEditingController? _controller; String _channelQuery = ''; bool _isSearchActive = false; - Timer _debounce; + Timer? _debounce; void _channelQueryListener() { - if (_debounce?.isActive ?? false) _debounce.cancel(); + if (_debounce?.isActive ?? false) _debounce!.cancel(); _debounce = Timer(const Duration(milliseconds: 350), () { if (mounted) { setState(() { - _channelQuery = _controller.text; + _channelQuery = _controller!.text; _isSearchActive = _channelQuery.isNotEmpty; }); } @@ -624,7 +621,7 @@ class _ChannelListPageState extends State { return WillPopScope( onWillPop: () async { if (_isSearchActive) { - _controller.clear(); + _controller!.clear(); setState(() => _isSearchActive = false); return false; } @@ -651,11 +648,7 @@ class _ChannelListPageState extends State { ? MessageSearchListView( showErrorTile: true, messageQuery: _channelQuery, - filters: { - 'members': { - r'$in': [user.id] - }, - }, + filters: Filter.in_('members', [user!.id]), sortOptions: [ SortOption( 'created_at', @@ -699,8 +692,8 @@ class _ChannelListPageState extends State { final client = StreamChat.of(context).client; final message = messageResponse.message; final channel = client.channel( - messageResponse.channel.type, - id: messageResponse.channel.id, + messageResponse.channel!.type, + id: messageResponse.channel!.id, ); if (channel.state == null) { await channel.watch(); @@ -720,11 +713,7 @@ class _ChannelListPageState extends State { Navigator.pushNamed(context, Routes.NEW_CHAT); }, swipeToAction: true, - filter: { - 'members': { - r'$in': [user.id], - }, - }, + filter: Filter.in_('members', [user!.id]), options: { 'presence': true, }, @@ -741,10 +730,12 @@ class _ChannelListPageState extends State { builder: (context) => StreamChannel( channel: channel, child: ChatInfoScreen( - user: channel.state.members + messageTheme: StreamChatTheme.of(context) + .ownMessageTheme, + user: channel.state!.members .where((m) => m.userId != - channel.client.state.user.id) + channel.client.state.user!.id) .first .user, ), @@ -757,7 +748,10 @@ class _ChannelListPageState extends State { MaterialPageRoute( builder: (context) => StreamChannel( channel: channel, - child: GroupInfoScreen(), + child: GroupInfoScreen( + messageTheme: StreamChatTheme.of(context) + .ownMessageTheme, + ), ), ), ); @@ -774,8 +768,8 @@ class _ChannelListPageState extends State { } class ChannelPageArgs { - final Channel channel; - final Message initialMessage; + final Channel? channel; + final Message? initialMessage; const ChannelPageArgs({ this.channel, @@ -784,12 +778,12 @@ class ChannelPageArgs { } class ChannelPage extends StatefulWidget { - final int initialScrollIndex; - final double initialAlignment; + final int? initialScrollIndex; + final double? initialAlignment; final bool highlightInitialMessage; const ChannelPage({ - Key key, + Key? key, this.initialScrollIndex, this.initialAlignment, this.highlightInitialMessage = false, @@ -800,8 +794,8 @@ class ChannelPage extends StatefulWidget { } class _ChannelPageState extends State { - Message _quotedMessage; - FocusNode _focusNode; + Message? _quotedMessage; + FocusNode? _focusNode; @override void initState() { @@ -811,14 +805,14 @@ class _ChannelPageState extends State { @override void dispose() { - _focusNode.dispose(); + _focusNode!.dispose(); super.dispose(); } void _reply(Message message) { setState(() => _quotedMessage = message); - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - _focusNode.requestFocus(); + WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { + _focusNode!.requestFocus(); }); } @@ -833,9 +827,8 @@ class _ChannelPageState extends State { if (channel.memberCount == 2 && channel.isDistinct) { final currentUser = StreamChat.of(context).user; - final otherUser = channel.state.members.firstWhere( - (element) => element.user.id != currentUser.id, - orElse: () => null, + final otherUser = channel.state!.members.firstWhereOrNull( + (element) => element.user!.id != currentUser!.id, ); if (otherUser != null) { final pop = await Navigator.push( @@ -843,6 +836,7 @@ class _ChannelPageState extends State { MaterialPageRoute( builder: (context) => StreamChannel( child: ChatInfoScreen( + messageTheme: StreamChatTheme.of(context).ownMessageTheme, user: otherUser.user, ), channel: channel, @@ -859,7 +853,9 @@ class _ChannelPageState extends State { context, MaterialPageRoute( builder: (context) => StreamChannel( - child: GroupInfoScreen(), + child: GroupInfoScreen( + messageTheme: StreamChatTheme.of(context).ownMessageTheme, + ), channel: channel, ), ), @@ -936,7 +932,7 @@ class _ChannelPageState extends State { quotedMessage: _quotedMessage, onQuotedMessageCleared: () { setState(() => _quotedMessage = null); - _focusNode.unfocus(); + _focusNode!.unfocus(); }, ), ], @@ -946,12 +942,12 @@ class _ChannelPageState extends State { } class ThreadPage extends StatefulWidget { - final Message parent; - final int initialScrollIndex; - final double initialAlignment; + final Message? parent; + final int? initialScrollIndex; + final double? initialAlignment; ThreadPage({ - Key key, + Key? key, this.parent, this.initialScrollIndex, this.initialAlignment, @@ -962,7 +958,7 @@ class ThreadPage extends StatefulWidget { } class _ThreadPageState extends State { - Message _quotedMessage; + Message? _quotedMessage; FocusNode _focusNode = FocusNode(); @override @@ -973,7 +969,7 @@ class _ThreadPageState extends State { void _reply(Message message) { setState(() => _quotedMessage = message); - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { _focusNode.requestFocus(); }); } @@ -983,7 +979,7 @@ class _ThreadPageState extends State { return Scaffold( backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow, appBar: ThreadHeader( - parent: widget.parent, + parent: widget.parent!, ), body: Column( children: [ @@ -996,7 +992,7 @@ class _ThreadPageState extends State { onReplyTap: _reply, ), ), - if (widget.parent.type != 'deleted') + if (widget.parent!.type != 'deleted') MessageInput( parentMessage: widget.parent, focusNode: _focusNode, @@ -1021,8 +1017,8 @@ class InitData { class HolePainter extends CustomPainter { HolePainter({ - @required this.color, - @required this.holeSize, + required this.color, + required this.holeSize, }); Color color; diff --git a/packages/stream_chat_v1/lib/new_chat_screen.dart b/packages/stream_chat_v1/lib/new_chat_screen.dart index d4a6888..d708b00 100644 --- a/packages/stream_chat_v1/lib/new_chat_screen.dart +++ b/packages/stream_chat_v1/lib/new_chat_screen.dart @@ -16,9 +16,9 @@ class _NewChatScreenState extends State { final _chipInputTextFieldStateKey = GlobalKey>(); - TextEditingController _controller; + late TextEditingController _controller; - ChipInputTextFieldState get _chipInputTextFieldState => + ChipInputTextFieldState? get _chipInputTextFieldState => _chipInputTextFieldStateKey.currentState; String _userNameQuery = ''; @@ -30,14 +30,14 @@ class _NewChatScreenState extends State { bool _isSearchActive = false; - Channel channel; + Channel? channel; - Timer _debounce; + Timer? _debounce; bool _showUserList = true; void _userNameListener() { - if (_debounce?.isActive ?? false) _debounce.cancel(); + if (_debounce?.isActive ?? false) _debounce!.cancel(); _debounce = Timer(const Duration(milliseconds: 350), () { if (mounted) setState(() { @@ -70,13 +70,13 @@ class _NewChatScreenState extends State { 'state': false, 'watch': false, }, - filter: { + filter: Filter.raw(value: { 'members': [ ..._selectedUsers.map((e) => e.id), - chatState.user.id, + chatState.user!.id, ], 'distinct': true, - }, + }), messageLimit: 0, paginationParams: PaginationParams( limit: 1, @@ -86,14 +86,14 @@ class _NewChatScreenState extends State { final _channelExisted = res.length == 1; if (_channelExisted) { channel = res.first; - await channel.watch(); + await channel!.watch(); } else { channel = chatState.client.channel( 'messaging', extraData: { 'members': [ ..._selectedUsers.map((e) => e.id), - chatState.user.id, + chatState.user!.id, ], }, ); @@ -110,9 +110,9 @@ class _NewChatScreenState extends State { void dispose() { _searchFocusNode.dispose(); _messageInputFocusNode.dispose(); - _controller?.clear(); - _controller?.removeListener(_userNameListener); - _controller?.dispose(); + _controller.clear(); + _controller.removeListener(_userNameListener); + _controller.dispose(); super.dispose(); } @@ -158,7 +158,7 @@ class _NewChatScreenState extends State { message: statusString, child: StreamChannel( showLoading: false, - channel: channel, + channel: channel!, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -169,7 +169,7 @@ class _NewChatScreenState extends State { chipBuilder: (context, user) { return GestureDetector( onTap: () { - _chipInputTextFieldState.removeItem(user); + _chipInputTextFieldState?.removeItem(user); _searchFocusNode.requestFocus(); }, child: Stack( @@ -299,24 +299,21 @@ class _NewChatScreenState extends State { _controller.clear(); if (!_selectedUsers.contains(user)) { _chipInputTextFieldState - ..addItem(user) + ?..addItem(user) ..pauseItemAddition(); } else { - _chipInputTextFieldState.removeItem(user); + _chipInputTextFieldState!.removeItem(user); } }, pagination: PaginationParams( limit: 25, ), - filter: { + filter: Filter.and([ if (_userNameQuery.isNotEmpty) - 'name': { - r'$autocomplete': _userNameQuery, - }, - 'id': { - r'$ne': StreamChat.of(context).user.id, - }, - }, + Filter.autoComplete('name', _userNameQuery), + Filter.notEqual( + 'id', StreamChat.of(context).user!.id), + ]), sort: [ SortOption( 'name', @@ -370,7 +367,7 @@ class _NewChatScreenState extends State { ), ) : FutureBuilder( - future: channel.initialized, + future: channel!.initialized, builder: (context, snapshot) { if (snapshot.data == true) { return MessageListView(); @@ -394,7 +391,7 @@ class _NewChatScreenState extends State { MessageInput( focusNode: _messageInputFocusNode, preMessageSending: (message) async { - await channel.watch(); + await channel!.watch(); return message; }, onMessageSent: (m) { diff --git a/packages/stream_chat_v1/lib/new_group_chat_screen.dart b/packages/stream_chat_v1/lib/new_group_chat_screen.dart index 209d372..0db780e 100644 --- a/packages/stream_chat_v1/lib/new_group_chat_screen.dart +++ b/packages/stream_chat_v1/lib/new_group_chat_screen.dart @@ -12,7 +12,7 @@ class NewGroupChatScreen extends StatefulWidget { } class _NewGroupChatScreenState extends State { - TextEditingController _controller; + TextEditingController? _controller; String _userNameQuery = ''; @@ -20,14 +20,14 @@ class _NewGroupChatScreenState extends State { bool _isSearchActive = false; - Timer _debounce; + Timer? _debounce; void _userNameListener() { - if (_debounce?.isActive ?? false) _debounce.cancel(); + if (_debounce?.isActive ?? false) _debounce!.cancel(); _debounce = Timer(const Duration(milliseconds: 350), () { if (mounted) { setState(() { - _userNameQuery = _controller.text; + _userNameQuery = _controller!.text; _isSearchActive = _userNameQuery.isNotEmpty; }); } @@ -80,7 +80,7 @@ class _NewGroupChatScreenState extends State { setState(() { _selectedUsers ..clear() - ..addAll(updatedList); + ..addAll(updatedList as Iterable); }); } }, @@ -244,15 +244,11 @@ class _NewGroupChatScreenState extends State { pagination: PaginationParams( limit: 25, ), - filter: { + filter: Filter.and([ if (_userNameQuery.isNotEmpty) - 'name': { - r'$autocomplete': _userNameQuery, - }, - 'id': { - r'$ne': StreamChat.of(context).user.id, - } - }, + Filter.autoComplete('name', _userNameQuery), + Filter.notEqual('id', StreamChat.of(context).user!.id), + ]), sort: [ SortOption( 'name', @@ -315,8 +311,8 @@ class _HeaderDelegate extends SliverPersistentHeaderDelegate { final double height; const _HeaderDelegate({ - @required this.child, - @required this.height, + required this.child, + required this.height, }); @override diff --git a/packages/stream_chat_v1/lib/notifications_service.dart b/packages/stream_chat_v1/lib/notifications_service.dart index c72ac19..56f6ad7 100644 --- a/packages/stream_chat_v1/lib/notifications_service.dart +++ b/packages/stream_chat_v1/lib/notifications_service.dart @@ -7,7 +7,7 @@ void showLocalNotification(Event event, String currentUserId) async { EventType.messageNew, EventType.notificationMessageNew, ].contains(event.type) || - event.user.id == currentUserId) { + event.user!.id == currentUserId) { return; } if (event.message == null) return; @@ -21,9 +21,9 @@ void showLocalNotification(Event event, String currentUserId) async { ); await flutterLocalNotificationsPlugin.initialize(initializationSettings); await flutterLocalNotificationsPlugin.show( - event.message.id.hashCode, - event.message.user.name, - event.message.text, + event.message!.id.hashCode, + event.message!.user!.name, + event.message!.text, NotificationDetails( android: AndroidNotificationDetails( 'message channel', diff --git a/packages/stream_chat_v1/lib/routes/app_routes.dart b/packages/stream_chat_v1/lib/routes/app_routes.dart index c641b04..a8ce583 100644 --- a/packages/stream_chat_v1/lib/routes/app_routes.dart +++ b/packages/stream_chat_v1/lib/routes/app_routes.dart @@ -12,7 +12,7 @@ import '../group_info_screen.dart'; class AppRoutes { /// Add entry for new route here - static Route generateRoute(RouteSettings settings) { + static Route? generateRoute(RouteSettings settings) { final args = settings.arguments; switch (settings.name) { case Routes.APP: @@ -44,7 +44,7 @@ class AppRoutes { builder: (_) { final arg = args as ChannelPageArgs; return StreamChannel( - channel: arg.channel, + channel: arg.channel!, initialMessageId: arg.initialMessage?.id, child: ChannelPage( highlightInitialMessage: arg.initialMessage != null, @@ -68,22 +68,25 @@ class AppRoutes { settings: const RouteSettings(name: Routes.NEW_GROUP_CHAT_DETAILS), builder: (_) { return GroupChatDetailsScreen( - selectedUsers: args, + selectedUsers: args as List?, ); }); case Routes.CHAT_INFO_SCREEN: return MaterialPageRoute( settings: const RouteSettings(name: Routes.CHAT_INFO_SCREEN), - builder: (_) { + builder: (context) { return ChatInfoScreen( - user: args, + user: args as User?, + messageTheme: StreamChatTheme.of(context).ownMessageTheme, ); }); case Routes.GROUP_INFO_SCREEN: return MaterialPageRoute( settings: const RouteSettings(name: Routes.GROUP_INFO_SCREEN), - builder: (_) { - return GroupInfoScreen(); + builder: (context) { + return GroupInfoScreen( + messageTheme: StreamChatTheme.of(context).ownMessageTheme, + ); }); // Default case, should not reach here. default: diff --git a/packages/stream_chat_v1/lib/search_text_field.dart b/packages/stream_chat_v1/lib/search_text_field.dart index 9628ddb..c3850c2 100644 --- a/packages/stream_chat_v1/lib/search_text_field.dart +++ b/packages/stream_chat_v1/lib/search_text_field.dart @@ -2,15 +2,15 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; class SearchTextField extends StatelessWidget { - final TextEditingController controller; - final ValueChanged onChanged; + final TextEditingController? controller; + final ValueChanged? onChanged; final String hintText; - final VoidCallback onTap; + final VoidCallback? onTap; final bool showCloseButton; const SearchTextField({ - Key key, - @required this.controller, + Key? key, + required this.controller, this.onChanged, this.onTap, this.hintText = 'Search', @@ -76,11 +76,11 @@ class SearchTextField extends StatelessWidget { ), splashRadius: 24, onPressed: () { - if (controller.text.isNotEmpty) { + if (controller!.text.isNotEmpty) { Future.microtask( () => [ - controller.clear(), - if (onChanged != null) onChanged(''), + controller!.clear(), + if (onChanged != null) onChanged!(''), ], ); } diff --git a/packages/stream_chat_v1/lib/stream_version.dart b/packages/stream_chat_v1/lib/stream_version.dart index b3f926b..7401813 100644 --- a/packages/stream_chat_v1/lib/stream_version.dart +++ b/packages/stream_chat_v1/lib/stream_version.dart @@ -5,7 +5,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; class StreamVersion extends StatelessWidget { const StreamVersion({ - Key key, + Key? key, }) : super(key: key); @override @@ -20,13 +20,13 @@ class StreamVersion extends StatelessWidget { return SizedBox(); } - final pubspec = snapshot.data; + final pubspec = snapshot.data!; final yaml = loadYaml(pubspec); final streamChatDep = yaml['packages']['stream_chat_flutter']['version']; return Text( - 'Stream SDK v ${streamChatDep}', + 'Stream SDK v $streamChatDep', style: TextStyle( fontSize: 14, color: StreamChatTheme.of(context).colorTheme.greyGainsboro, diff --git a/packages/stream_chat_v1/pubspec.yaml b/packages/stream_chat_v1/pubspec.yaml index 35af177..9f1f30d 100644 --- a/packages/stream_chat_v1/pubspec.yaml +++ b/packages/stream_chat_v1/pubspec.yaml @@ -4,10 +4,10 @@ publish_to: 'none' version: 1.5.4+1 environment: - sdk: ">=2.2.2 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: - flutter_app_badger: ^1.1.2 + flutter_app_badger: ^1.2.0 flutter: sdk: flutter stream_chat_flutter: @@ -20,13 +20,14 @@ dependencies: url: https://github.com/GetStream/stream-chat-flutter.git ref: develop path: packages/stream_chat_persistence - flutter_local_notifications: ^2.0.2 - flutter_svg: ^0.19.3 - flutter_secure_storage: ^3.3.5 - yaml: ^2.2.1 - uuid: ^2.2.2 - streaming_shared_preferences: ^1.0.2 - lottie: ^0.7.0+1 + flutter_local_notifications: ^5.0.0+4 + flutter_svg: ^0.22.0 + flutter_secure_storage: ^4.2.0 + yaml: ^3.1.0 + uuid: ^3.0.4 + streaming_shared_preferences: ^2.0.0 + lottie: ^1.0.1 + collection: ^1.15.0-nullsafety.4 dependency_overrides: stream_chat: @@ -41,7 +42,7 @@ dependency_overrides: path: packages/stream_chat_flutter_core dev_dependencies: - flutter_launcher_icons: ^0.8.1 + flutter_launcher_icons: ^0.9.0 test: any flutter: diff --git a/packages/stream_chat_v1/test/widget_test.dart b/packages/stream_chat_v1/test/widget_test.dart deleted file mode 100644 index 2d257c0..0000000 --- a/packages/stream_chat_v1/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:stream_chat_v1/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -}