From 1badf8a851c41399c98a623ee4cdb089fbef8dd5 Mon Sep 17 00:00:00 2001 From: Vick Top Date: Fri, 22 Mar 2024 09:31:51 +0100 Subject: [PATCH] feat: add create group chat flow --- .../src/flutter_chat_navigator_userstory.dart | 78 +++++++ .../lib/src/flutter_chat_userstory.dart | 27 +++ .../lib/src/models/chat_configuration.dart | 4 + packages/flutter_chat/lib/src/routes.dart | 2 + packages/flutter_chat/pubspec.yaml | 15 +- .../service/firebase_chat_user_service.dart | 2 +- packages/flutter_chat_firebase/pubspec.yaml | 5 +- .../lib/src/model/chat.dart | 19 ++ .../lib/src/model/group_chat.dart | 1 + .../lib/src/model/personal_chat.dart | 1 + .../lib/src/service/user_service.dart | 2 +- .../service/local_chat_detail_service.dart | 4 +- .../service/local_chat_overview_service.dart | 48 +++-- .../lib/service/local_chat_user_service.dart | 2 +- packages/flutter_chat_local/pubspec.yaml | 5 +- .../lib/flutter_chat_view.dart | 2 + .../lib/src/config/chat_translations.dart | 2 + .../lib/src/screens/chat_detail_screen.dart | 10 + .../lib/src/screens/new_chat_screen.dart | 80 ++++++-- .../new_group_chat_overview_screen.dart | 67 ++++++ .../src/screens/new_group_chat_screen.dart | 193 ++++++++++++++++++ packages/flutter_chat_view/pubspec.yaml | 5 +- 22 files changed, 515 insertions(+), 59 deletions(-) create mode 100644 packages/flutter_chat_view/lib/src/screens/new_group_chat_overview_screen.dart create mode 100644 packages/flutter_chat_view/lib/src/screens/new_group_chat_screen.dart diff --git a/packages/flutter_chat/lib/src/flutter_chat_navigator_userstory.dart b/packages/flutter_chat/lib/src/flutter_chat_navigator_userstory.dart index 7a54863..4e0df9b 100644 --- a/packages/flutter_chat/lib/src/flutter_chat_navigator_userstory.dart +++ b/packages/flutter_chat/lib/src/flutter_chat_navigator_userstory.dart @@ -4,6 +4,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_chat/flutter_chat.dart'; +// ignore: depend_on_referenced_packages +import 'package:uuid/uuid.dart'; /// Navigates to the chat user story screen. /// @@ -199,11 +201,26 @@ Widget _newChatScreenRoute( options: configuration.chatOptionsBuilder(context), translations: configuration.translations, service: configuration.chatService, + onPressCreateGroupChat: () async { + configuration.onPressCreateGroupChat?.call(); + if (configuration.onPressCreateGroupChat != null) return; + if (context.mounted) { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => _newGroupChatScreenRoute( + configuration, + context, + ), + ), + ); + } + }, onPressCreateChat: (user) async { configuration.onPressCreateChat?.call(user); if (configuration.onPressCreateChat != null) return; var chat = await configuration.chatService.chatOverviewService .getChatByUser(user); + debugPrint('Chat is ${chat.id}'); if (chat.id == null) { chat = await configuration.chatService.chatOverviewService .storeChatIfNot( @@ -225,3 +242,64 @@ Widget _newChatScreenRoute( } }, ); + +Widget _newGroupChatScreenRoute( + ChatUserStoryConfiguration configuration, + BuildContext context, +) => + NewGroupChatScreen( + options: configuration.chatOptionsBuilder(context), + translations: configuration.translations, + service: configuration.chatService, + onPressGroupChatOverview: (users) async => Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => _newGroupChatOverviewScreenRoute( + configuration, + context, + users, + ), + ), + ), + ); + +Widget _newGroupChatOverviewScreenRoute( + ChatUserStoryConfiguration configuration, + BuildContext context, + List users, +) => + NewGroupChatOverviewScreen( + options: configuration.chatOptionsBuilder(context), + translations: configuration.translations, + service: configuration.chatService, + users: users, + onPressCompleteGroupChatCreation: (users, groupChatName) async { + configuration.onPressCompleteGroupChatCreation + ?.call(users, groupChatName); + if (configuration.onPressCreateGroupChat != null) return; + debugPrint('----------- The list of users = $users -----------'); + debugPrint('----------- Group chat name = $groupChatName -----------'); + + var chat = + await configuration.chatService.chatOverviewService.storeChatIfNot( + GroupChatModel( + id: const Uuid().v4(), + canBeDeleted: true, + title: groupChatName, + imageUrl: 'https://picsum.photos/200/300', + users: users, + ), + ); + debugPrint('----------- Chat id = ${chat.id} -----------'); + if (context.mounted) { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => _chatDetailScreenRoute( + configuration, + context, + chat.id!, + ), + ), + ); + } + }, + ); diff --git a/packages/flutter_chat/lib/src/flutter_chat_userstory.dart b/packages/flutter_chat/lib/src/flutter_chat_userstory.dart index a5f6a46..e5de04d 100644 --- a/packages/flutter_chat/lib/src/flutter_chat_userstory.dart +++ b/packages/flutter_chat/lib/src/flutter_chat_userstory.dart @@ -143,6 +143,33 @@ List getChatStoryRoutes( ); } }, + onPressCreateGroupChat: () async => context.push( + ChatUserStoryRoutes.newGroupChatScreen, + ), + ); + return buildScreenWithoutTransition( + context: context, + state: state, + child: configuration.chatPageBuilder?.call( + context, + newChatScreen, + ) ?? + Scaffold( + body: newChatScreen, + ), + ); + }, + ), + GoRoute( + path: ChatUserStoryRoutes.newGroupChatScreen, + pageBuilder: (context, state) { + var newChatScreen = NewGroupChatScreen( + options: configuration.chatOptionsBuilder(context), + translations: configuration.translations, + service: configuration.chatService, + onPressGroupChatOverview: (user) async => context.push( + ChatUserStoryRoutes.newGroupChatOverviewScreen, + ), ); return buildScreenWithoutTransition( context: context, diff --git a/packages/flutter_chat/lib/src/models/chat_configuration.dart b/packages/flutter_chat/lib/src/models/chat_configuration.dart index 8c325c2..cd47146 100644 --- a/packages/flutter_chat/lib/src/models/chat_configuration.dart +++ b/packages/flutter_chat/lib/src/models/chat_configuration.dart @@ -21,6 +21,8 @@ class ChatUserStoryConfiguration { this.onReadChat, this.onUploadImage, this.onPressCreateChat, + this.onPressCreateGroupChat, + this.onPressCompleteGroupChatCreation, this.iconColor = Colors.black, this.deleteChatDialog, this.disableDismissForPermanentChats = false, @@ -71,6 +73,8 @@ class ChatUserStoryConfiguration { final Function(ChatUserModel)? onPressCreateChat; /// Builder for chat options based on context. + final Function(List, String)? onPressCompleteGroupChatCreation; + final Function()? onPressCreateGroupChat; final ChatOptions Function(BuildContext context) chatOptionsBuilder; /// If true, the user will be routed to the new chat screen if there are diff --git a/packages/flutter_chat/lib/src/routes.dart b/packages/flutter_chat/lib/src/routes.dart index cad8ae1..fc399f7 100644 --- a/packages/flutter_chat/lib/src/routes.dart +++ b/packages/flutter_chat/lib/src/routes.dart @@ -13,6 +13,8 @@ mixin ChatUserStoryRoutes { static const String newChatScreen = '/new-chat'; /// Constructs the path for the chat profile screen. + static const String newGroupChatScreen = '/new-group-chat'; + static const String newGroupChatOverviewScreen = '/new-group-chat-overview'; static String chatProfileScreenPath(String chatId, String? userId) => '/chat-profile/$chatId/$userId'; diff --git a/packages/flutter_chat/pubspec.yaml b/packages/flutter_chat/pubspec.yaml index 88055b4..bcfc60c 100644 --- a/packages/flutter_chat/pubspec.yaml +++ b/packages/flutter_chat/pubspec.yaml @@ -17,20 +17,11 @@ dependencies: sdk: flutter go_router: any flutter_chat_view: - git: - url: https://github.com/Iconica-Development/flutter_chat - path: packages/flutter_chat_view - ref: 1.3.1 + path: ../flutter_chat_view flutter_chat_interface: - git: - url: https://github.com/Iconica-Development/flutter_chat - path: packages/flutter_chat_interface - ref: 1.3.1 + path: ../flutter_chat_interface flutter_chat_local: - git: - url: https://github.com/Iconica-Development/flutter_chat - path: packages/flutter_chat_local - ref: 1.3.1 + path: ../flutter_chat_local dev_dependencies: flutter_iconica_analysis: diff --git a/packages/flutter_chat_firebase/lib/service/firebase_chat_user_service.dart b/packages/flutter_chat_firebase/lib/service/firebase_chat_user_service.dart index e7f8a85..ccc14cb 100644 --- a/packages/flutter_chat_firebase/lib/service/firebase_chat_user_service.dart +++ b/packages/flutter_chat_firebase/lib/service/firebase_chat_user_service.dart @@ -83,7 +83,7 @@ class FirebaseChatUserService implements ChatUserService { : _currentUser; @override - Future> getAllUsers() async { + Future> get getAllUsers async { var currentUser = await getCurrentUser(); var query = _userCollection.where( diff --git a/packages/flutter_chat_firebase/pubspec.yaml b/packages/flutter_chat_firebase/pubspec.yaml index aca55af..a23a824 100644 --- a/packages/flutter_chat_firebase/pubspec.yaml +++ b/packages/flutter_chat_firebase/pubspec.yaml @@ -20,10 +20,7 @@ dependencies: firebase_auth: ^4.1.2 uuid: ^4.0.0 flutter_chat_interface: - git: - url: https://github.com/Iconica-Development/flutter_chat - path: packages/flutter_chat_interface - ref: 1.3.1 + path: ../flutter_chat_interface dev_dependencies: flutter_iconica_analysis: diff --git a/packages/flutter_chat_interface/lib/src/model/chat.dart b/packages/flutter_chat_interface/lib/src/model/chat.dart index 18636dc..e839cfb 100644 --- a/packages/flutter_chat_interface/lib/src/model/chat.dart +++ b/packages/flutter_chat_interface/lib/src/model/chat.dart @@ -6,6 +6,7 @@ import 'package:flutter_chat_interface/flutter_chat_interface.dart'; abstract class ChatModelInterface { + ChatModelInterface copyWith(); String? get id; List? get messages; int? get unreadMessages; @@ -55,4 +56,22 @@ class ChatModel implements ChatModelInterface { @override final bool canBeDeleted; + + @override + ChatModel copyWith({ + String? id, + List? messages, + int? unreadMessages, + DateTime? lastUsed, + ChatMessageModel? lastMessage, + bool? canBeDeleted, + }) => + ChatModel( + id: id ?? this.id, + messages: messages ?? this.messages, + unreadMessages: unreadMessages ?? this.unreadMessages, + lastUsed: lastUsed ?? this.lastUsed, + lastMessage: lastMessage ?? this.lastMessage, + canBeDeleted: canBeDeleted ?? this.canBeDeleted, + ); } diff --git a/packages/flutter_chat_interface/lib/src/model/group_chat.dart b/packages/flutter_chat_interface/lib/src/model/group_chat.dart index f94803a..d3f114a 100644 --- a/packages/flutter_chat_interface/lib/src/model/group_chat.dart +++ b/packages/flutter_chat_interface/lib/src/model/group_chat.dart @@ -19,6 +19,7 @@ abstract class GroupChatModelInterface extends ChatModel { String get imageUrl; List get users; + @override GroupChatModelInterface copyWith({ String? id, List? messages, diff --git a/packages/flutter_chat_interface/lib/src/model/personal_chat.dart b/packages/flutter_chat_interface/lib/src/model/personal_chat.dart index 7ae7732..91d6c00 100644 --- a/packages/flutter_chat_interface/lib/src/model/personal_chat.dart +++ b/packages/flutter_chat_interface/lib/src/model/personal_chat.dart @@ -17,6 +17,7 @@ abstract class PersonalChatModelInterface extends ChatModel { ChatUserModel get user; + @override PersonalChatModel copyWith({ String? id, List? messages, diff --git a/packages/flutter_chat_interface/lib/src/service/user_service.dart b/packages/flutter_chat_interface/lib/src/service/user_service.dart index ba65fd6..e88558e 100644 --- a/packages/flutter_chat_interface/lib/src/service/user_service.dart +++ b/packages/flutter_chat_interface/lib/src/service/user_service.dart @@ -3,5 +3,5 @@ import 'package:flutter_chat_interface/flutter_chat_interface.dart'; abstract class ChatUserService { Future getUser(String id); Future getCurrentUser(); - Future> getAllUsers(); + Future> get getAllUsers; } diff --git a/packages/flutter_chat_local/lib/service/local_chat_detail_service.dart b/packages/flutter_chat_local/lib/service/local_chat_detail_service.dart index 4a9aab5..edf2006 100644 --- a/packages/flutter_chat_local/lib/service/local_chat_detail_service.dart +++ b/packages/flutter_chat_local/lib/service/local_chat_detail_service.dart @@ -78,7 +78,7 @@ class LocalChatDetailService with ChangeNotifier implements ChatDetailService { await (chatOverviewService as LocalChatOverviewService).updateChat( chat.copyWith( - messages: [...chat.messages!, message], + messages: [...?chat.messages, message], lastMessage: message, lastUsed: DateTime.now(), ), @@ -110,7 +110,7 @@ class LocalChatDetailService with ChangeNotifier implements ChatDetailService { ); await (chatOverviewService as LocalChatOverviewService).updateChat( chat.copyWith( - messages: [...chat.messages!, message], + messages: [...?chat.messages, message], lastMessage: message, lastUsed: DateTime.now(), ), diff --git a/packages/flutter_chat_local/lib/service/local_chat_overview_service.dart b/packages/flutter_chat_local/lib/service/local_chat_overview_service.dart index 88a3379..d8aa2f2 100644 --- a/packages/flutter_chat_local/lib/service/local_chat_overview_service.dart +++ b/packages/flutter_chat_local/lib/service/local_chat_overview_service.dart @@ -8,10 +8,10 @@ class LocalChatOverviewService with ChangeNotifier implements ChatOverviewService { /// The list of personal chat models. - final List _chats = []; + final List _chats = []; /// Retrieves the list of personal chat models. - List get chats => _chats; + List get chats => _chats; /// The stream controller for chats. final StreamController> _chatsController = @@ -19,9 +19,10 @@ class LocalChatOverviewService Future updateChat(ChatModel chat) { var index = _chats.indexWhere((element) => element.id == chat.id); - _chats[index] = chat as PersonalChatModel; + _chats[index] = chat; _chatsController.addStream(Stream.value(_chats)); notifyListeners(); + debugPrint('Chat updated: $chat'); return Future.value(); } @@ -30,24 +31,27 @@ class LocalChatOverviewService _chats.removeWhere((element) => element.id == chat.id); _chatsController.add(_chats); notifyListeners(); + debugPrint('Chat deleted: $chat'); return Future.value(); } @override - Future getChatById(String id) => - Future.value(_chats.firstWhere((element) => element.id == id)); + Future getChatById(String id) { + var chat = _chats.firstWhere((element) => element.id == id); + debugPrint('Retrieved chat by ID: $chat'); + debugPrint('Messages are: ${chat.messages?.length}'); + return Future.value(chat); + } @override - Future getChatByUser(ChatUserModel user) { + Future getChatByUser(ChatUserModel user) async { PersonalChatModel? chat; try { - chat = _chats.firstWhere( - (element) => element.user.id == user.id, - orElse: () { - throw Exception(); - }, - ); - } on Exception catch (_) { + chat = _chats + .whereType() + .firstWhere((element) => element.user.id == user.id); + // ignore: avoid_catching_errors + } on StateError { chat = PersonalChatModel( user: user, messages: [], @@ -55,11 +59,12 @@ class LocalChatOverviewService ); chat.id = chat.hashCode.toString(); _chats.add(chat); + debugPrint('New chat created: $chat'); } _chatsController.add([..._chats]); notifyListeners(); - return Future.value(chat); + return chat; } @override @@ -72,5 +77,18 @@ class LocalChatOverviewService Future readChat(ChatModel chat) async => Future.value(); @override - Future storeChatIfNot(ChatModel chat) => Future.value(chat); + Future storeChatIfNot(ChatModel chat) { + var chatExists = _chats.any((element) => element.id == chat.id); + + if (!chatExists) { + _chats.add(chat); + _chatsController.add([..._chats]); + notifyListeners(); + debugPrint('Chat stored: $chat'); + } else { + debugPrint('Chat already exists: $chat'); + } + + return Future.value(chat); + } } diff --git a/packages/flutter_chat_local/lib/service/local_chat_user_service.dart b/packages/flutter_chat_local/lib/service/local_chat_user_service.dart index 5a0f5a7..aa2fa7d 100644 --- a/packages/flutter_chat_local/lib/service/local_chat_user_service.dart +++ b/packages/flutter_chat_local/lib/service/local_chat_user_service.dart @@ -25,7 +25,7 @@ class LocalChatUserService implements ChatUserService { ]; @override - Future> getAllUsers() => + Future> get getAllUsers => Future.value(users.where((element) => element.id != '3').toList()); @override diff --git a/packages/flutter_chat_local/pubspec.yaml b/packages/flutter_chat_local/pubspec.yaml index ec1382b..37d5021 100644 --- a/packages/flutter_chat_local/pubspec.yaml +++ b/packages/flutter_chat_local/pubspec.yaml @@ -12,10 +12,7 @@ dependencies: flutter: sdk: flutter flutter_chat_interface: - git: - url: https://github.com/Iconica-Development/flutter_chat - path: packages/flutter_chat_interface - ref: 1.3.1 + path: ../flutter_chat_interface dev_dependencies: flutter_test: diff --git a/packages/flutter_chat_view/lib/flutter_chat_view.dart b/packages/flutter_chat_view/lib/flutter_chat_view.dart index 4a5e7c4..2ca8abe 100644 --- a/packages/flutter_chat_view/lib/flutter_chat_view.dart +++ b/packages/flutter_chat_view/lib/flutter_chat_view.dart @@ -13,3 +13,5 @@ export 'src/screens/chat_detail_screen.dart'; export 'src/screens/chat_profile_screen.dart'; export 'src/screens/chat_screen.dart'; export 'src/screens/new_chat_screen.dart'; +export 'src/screens/new_group_chat_overview_screen.dart'; +export 'src/screens/new_group_chat_screen.dart'; diff --git a/packages/flutter_chat_view/lib/src/config/chat_translations.dart b/packages/flutter_chat_view/lib/src/config/chat_translations.dart index f4a2d56..3343857 100644 --- a/packages/flutter_chat_view/lib/src/config/chat_translations.dart +++ b/packages/flutter_chat_view/lib/src/config/chat_translations.dart @@ -7,6 +7,7 @@ class ChatTranslations { this.chatsTitle = 'Chats', this.chatsUnread = 'unread', this.newChatButton = 'Start chat', + this.newGroupChatButton = 'Start group chat', this.newChatTitle = 'Start chat', this.image = 'Image', this.searchPlaceholder = 'Search...', @@ -28,6 +29,7 @@ class ChatTranslations { final String chatsTitle; final String chatsUnread; final String newChatButton; + final String newGroupChatButton; final String newChatTitle; final String deleteChatButton; final String image; diff --git a/packages/flutter_chat_view/lib/src/screens/chat_detail_screen.dart b/packages/flutter_chat_view/lib/src/screens/chat_detail_screen.dart index 8a124c5..5987a06 100644 --- a/packages/flutter_chat_view/lib/src/screens/chat_detail_screen.dart +++ b/packages/flutter_chat_view/lib/src/screens/chat_detail_screen.dart @@ -172,6 +172,16 @@ class _ChatDetailScreenState extends State { iconTheme: theme.appBarTheme.iconTheme ?? const IconThemeData(color: Colors.white), centerTitle: true, + leading: (chatModel is GroupChatModel) + ? GestureDetector( + onTap: () { + Navigator.popUntil(context, (route) => route.isFirst); + }, + child: const Icon( + Icons.arrow_back, + ), + ) + : null, title: GestureDetector( onTap: () => widget.onPressChatTitle.call(context, chatModel!), child: Row( diff --git a/packages/flutter_chat_view/lib/src/screens/new_chat_screen.dart b/packages/flutter_chat_view/lib/src/screens/new_chat_screen.dart index 0e8e5b0..4bf2b1b 100644 --- a/packages/flutter_chat_view/lib/src/screens/new_chat_screen.dart +++ b/packages/flutter_chat_view/lib/src/screens/new_chat_screen.dart @@ -10,6 +10,7 @@ class NewChatScreen extends StatefulWidget { required this.options, required this.onPressCreateChat, required this.service, + required this.onPressCreateGroupChat, this.translations = const ChatTranslations(), super.key, }); @@ -22,6 +23,7 @@ class NewChatScreen extends StatefulWidget { /// Callback function for creating a new chat with a user. final Function(ChatUserModel) onPressCreateChat; + final Function() onPressCreateGroupChat; /// Translations for the chat. final ChatTranslations translations; @@ -48,21 +50,69 @@ class _NewChatScreenState extends State { _buildSearchIcon(), ], ), - body: FutureBuilder>( - // ignore: discarded_futures - future: widget.service.chatUserService.getAllUsers(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError) { - return Text('Error: ${snapshot.error}'); - } else if (snapshot.hasData) { - return _buildUserList(snapshot.data!); - } else { - return widget.options - .noChatsPlaceholderBuilder(widget.translations); - } - }, + body: Column( + children: [ + GestureDetector( + onTap: () async { + await widget.onPressCreateGroupChat(); + }, + child: Container( + color: Colors.grey[900], + child: SizedBox( + height: 60.0, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only( + left: 16.0, + ), + child: IconButton( + icon: const Icon( + Icons.group, + color: Colors.white, + ), + onPressed: () { + // Handle group chat creation + }, + ), + ), + const Text( + 'Create group chat', + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + ), + ), + ], + ), + ], + ), + ), + ), + ), + Expanded( + child: FutureBuilder>( + // ignore: discarded_futures + future: widget.service.chatUserService.getAllUsers, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else if (snapshot.hasData) { + return _buildUserList(snapshot.data!); + } else { + return widget.options + .noChatsPlaceholderBuilder(widget.translations); + } + }, + ), + ), + ], ), ); } diff --git a/packages/flutter_chat_view/lib/src/screens/new_group_chat_overview_screen.dart b/packages/flutter_chat_view/lib/src/screens/new_group_chat_overview_screen.dart new file mode 100644 index 0000000..64bf286 --- /dev/null +++ b/packages/flutter_chat_view/lib/src/screens/new_group_chat_overview_screen.dart @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2022 Iconica +// +// SPDX-License-Identifier: BSD-3-Clause + +import 'package:flutter/material.dart'; +import 'package:flutter_chat_view/flutter_chat_view.dart'; + +class NewGroupChatOverviewScreen extends StatefulWidget { + const NewGroupChatOverviewScreen({ + required this.options, + required this.onPressCompleteGroupChatCreation, + required this.service, + required this.users, + this.translations = const ChatTranslations(), + super.key, + }); + + final ChatOptions options; + final ChatTranslations translations; + final ChatService service; + final List users; + final Function(List, String) onPressCompleteGroupChatCreation; + + @override + State createState() => + _NewGroupChatOverviewScreenState(); +} + +class _NewGroupChatOverviewScreenState + extends State { + final TextEditingController _textEditingController = TextEditingController(); + + @override + Widget build(BuildContext context) { + var theme = Theme.of(context); + return Scaffold( + appBar: AppBar( + iconTheme: theme.appBarTheme.iconTheme ?? + const IconThemeData(color: Colors.white), + backgroundColor: theme.appBarTheme.backgroundColor ?? Colors.black, + title: const Text( + 'New Group Chat', + style: TextStyle(color: Colors.white), + ), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: TextField( + controller: _textEditingController, + decoration: const InputDecoration( + hintText: 'Group chat name', + ), + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () async { + await widget.onPressCompleteGroupChatCreation( + widget.users, + _textEditingController.text, + ); + }, + child: const Icon(Icons.check_circle), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, + ); + } +} diff --git a/packages/flutter_chat_view/lib/src/screens/new_group_chat_screen.dart b/packages/flutter_chat_view/lib/src/screens/new_group_chat_screen.dart new file mode 100644 index 0000000..1a6157f --- /dev/null +++ b/packages/flutter_chat_view/lib/src/screens/new_group_chat_screen.dart @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: BSD-3-Clause + +import 'package:flutter/material.dart'; +import 'package:flutter_chat_view/flutter_chat_view.dart'; + +class NewGroupChatScreen extends StatefulWidget { + const NewGroupChatScreen({ + required this.options, + required this.onPressGroupChatOverview, + required this.service, + this.translations = const ChatTranslations(), + super.key, + }); + + final ChatOptions options; + final ChatTranslations translations; + final ChatService service; + final Function(List) onPressGroupChatOverview; + + @override + State createState() => _NewGroupChatScreenState(); +} + +class _NewGroupChatScreenState extends State { + final FocusNode _textFieldFocusNode = FocusNode(); + List selectedUserList = []; + + bool _isSearching = false; + String query = ''; + + @override + Widget build(BuildContext context) { + var theme = Theme.of(context); + return Scaffold( + appBar: AppBar( + iconTheme: theme.appBarTheme.iconTheme ?? + const IconThemeData(color: Colors.white), + backgroundColor: theme.appBarTheme.backgroundColor ?? Colors.black, + title: _buildSearchField(), + actions: [ + _buildSearchIcon(), + ], + ), + body: FutureBuilder>( + future: widget.service.chatUserService.getAllUsers, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else if (snapshot.hasData) { + return _buildUserList(snapshot.data!); + } else { + return widget.options + .noChatsPlaceholderBuilder(widget.translations); + } + }, + ), + floatingActionButton: FloatingActionButton( + onPressed: () async { + await widget.onPressGroupChatOverview(selectedUserList); + }, + child: const Icon(Icons.arrow_circle_right), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, + ); + } + + Widget _buildSearchField() { + var theme = Theme.of(context); + + return _isSearching + ? Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: TextField( + focusNode: _textFieldFocusNode, + onChanged: (value) { + setState(() { + query = value; + }); + }, + decoration: InputDecoration( + hintText: widget.translations.searchPlaceholder, + hintStyle: theme.inputDecorationTheme.hintStyle ?? + const TextStyle( + color: Colors.white, + ), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: theme.inputDecorationTheme.focusedBorder?.borderSide + .color ?? + Colors.white, + ), + ), + ), + style: theme.inputDecorationTheme.hintStyle ?? + const TextStyle( + color: Colors.white, + ), + cursorColor: theme.textSelectionTheme.cursorColor ?? Colors.white, + ), + ) + : Text( + widget.translations.newGroupChatButton, + style: theme.appBarTheme.titleTextStyle ?? + const TextStyle( + color: Colors.white, + ), + ); + } + + Widget _buildSearchIcon() { + var theme = Theme.of(context); + + return IconButton( + onPressed: () { + setState(() { + _isSearching = !_isSearching; + query = ''; + }); + + if (_isSearching) { + _textFieldFocusNode.requestFocus(); + } + }, + icon: Icon( + _isSearching ? Icons.close : Icons.search, + color: theme.appBarTheme.iconTheme?.color ?? Colors.white, + ), + ); + } + + Widget _buildUserList(List users) { + var filteredUsers = users + .where( + (user) => + user.fullName?.toLowerCase().contains( + query.toLowerCase(), + ) ?? + false, + ) + .toList(); + + if (filteredUsers.isEmpty) { + return widget.options.noChatsPlaceholderBuilder(widget.translations); + } + + return ListView.builder( + itemCount: filteredUsers.length, + itemBuilder: (context, index) { + var user = filteredUsers[index]; + var isSelected = selectedUserList.contains(user); + + return InkWell( + onTap: () { + setState(() { + if (isSelected) { + selectedUserList.remove(user); + } else { + selectedUserList.add(user); + } + debugPrint('The list of selected users is $selectedUserList'); + }); + }, + child: Container( + color: isSelected ? Colors.amber.shade200 : Colors.white, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: widget.options.chatRowContainerBuilder( + ChatRow( + avatar: widget.options.userAvatarBuilder( + user, + 40.0, + ), + title: user.fullName ?? widget.translations.anonymousUser, + ), + ), + ), + if (isSelected) + const Padding( + padding: EdgeInsets.only(right: 16.0), + child: Icon(Icons.check_circle, color: Colors.green), + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/packages/flutter_chat_view/pubspec.yaml b/packages/flutter_chat_view/pubspec.yaml index 8cf1eae..8036f51 100644 --- a/packages/flutter_chat_view/pubspec.yaml +++ b/packages/flutter_chat_view/pubspec.yaml @@ -17,10 +17,7 @@ dependencies: sdk: flutter intl: any flutter_chat_interface: - git: - url: https://github.com/Iconica-Development/flutter_chat - path: packages/flutter_chat_interface - ref: 1.3.1 + path: ../flutter_chat_interface cached_network_image: ^3.2.2 flutter_image_picker: git: