diff --git a/CHANGELOG.md b/CHANGELOG.md index c808eeb..561b5ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 3.0.0 + +- Add theming +- add validator for group name +- fix spamming buttons +- fix user list flickering on the group creation screen + ## 2.0.0 - Add a serviceBuilder to the userstory configuration diff --git a/packages/flutter_chat/example/analysis_options.yaml b/packages/flutter_chat/example/analysis_options.yaml index 0d29021..31b4b51 100644 --- a/packages/flutter_chat/example/analysis_options.yaml +++ b/packages/flutter_chat/example/analysis_options.yaml @@ -1,28 +1,9 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. +include: package:flutter_iconica_analysis/analysis_options.yaml -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml +# Possible to overwrite the rules from the package + +analyzer: + exclude: linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at https://dart.dev/lints. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/packages/flutter_chat/example/lib/main.dart b/packages/flutter_chat/example/lib/main.dart index f8e92cc..5aa4ee8 100644 --- a/packages/flutter_chat/example/lib/main.dart +++ b/packages/flutter_chat/example/lib/main.dart @@ -1,5 +1,5 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_chat/flutter_chat.dart'; +import "package:flutter/material.dart"; +import "package:flutter_chat/flutter_chat.dart"; void main(List args) async { WidgetsFlutterBinding.ensureInitialized(); @@ -11,25 +11,22 @@ class App extends StatelessWidget { const App({super.key}); @override - Widget build(BuildContext context) { - return const MaterialApp( - home: Home(), - ); - } + Widget build(BuildContext context) => const MaterialApp( + home: Home(), + ); } class Home extends StatelessWidget { const Home({super.key}); @override - Widget build(BuildContext context) { - return Center( - child: chatNavigatorUserStory(context, - configuration: ChatUserStoryConfiguration( - chatService: LocalChatService(), - chatOptionsBuilder: (ctx) => ChatOptions( - noChatsPlaceholderBuilder: (translations) => - Text(translations.noUsersFound), - )))); - } + Widget build(BuildContext context) => Center( + child: chatNavigatorUserStory( + context, + configuration: ChatUserStoryConfiguration( + chatService: LocalChatService(), + chatOptionsBuilder: (ctx) => const ChatOptions(), + ), + ), + ); } diff --git a/packages/flutter_chat/example/pubspec.yaml b/packages/flutter_chat/example/pubspec.yaml index 61bf253..6b4e754 100644 --- a/packages/flutter_chat/example/pubspec.yaml +++ b/packages/flutter_chat/example/pubspec.yaml @@ -16,11 +16,23 @@ dependencies: path: ../ flutter_chat_firebase: path: ../../flutter_chat_firebase +dependency_overrides: + flutter_chat: + path: ../../flutter_chat + flutter_chat_interface: + path: ../../flutter_chat_interface + flutter_chat_local: + path: ../../flutter_chat_local + flutter_chat_view: + path: ../../flutter_chat_view dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.0 + flutter_iconica_analysis: + git: + url: https://github.com/Iconica-Development/flutter_iconica_analysis + ref: 7.0.0 flutter: uses-material-design: true diff --git a/packages/flutter_chat/example/test/widget_test.dart b/packages/flutter_chat/example/test/widget_test.dart index c1f4ea0..1f7b336 100644 --- a/packages/flutter_chat/example/test/widget_test.dart +++ b/packages/flutter_chat/example/test/widget_test.dart @@ -5,10 +5,10 @@ // 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_test/flutter_test.dart'; +import "package:flutter_test/flutter_test.dart"; void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { + testWidgets("Counter increments smoke test", (WidgetTester tester) async { expect(true, true); }); } 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 d2b812d..af7e574 100644 --- a/packages/flutter_chat/lib/src/flutter_chat_navigator_userstory.dart +++ b/packages/flutter_chat/lib/src/flutter_chat_navigator_userstory.dart @@ -284,7 +284,7 @@ Widget _newGroupChatOverviewScreenRoute( ), ); if (context.mounted) { - await Navigator.of(context).push( + await Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (context) => _chatDetailScreenRoute( configuration, diff --git a/packages/flutter_chat/lib/src/flutter_chat_userstory.dart b/packages/flutter_chat/lib/src/flutter_chat_userstory.dart index 9a1c267..3416f72 100644 --- a/packages/flutter_chat/lib/src/flutter_chat_userstory.dart +++ b/packages/flutter_chat/lib/src/flutter_chat_userstory.dart @@ -14,6 +14,7 @@ List getChatStoryRoutes( GoRoute( path: ChatUserStoryRoutes.chatScreen, pageBuilder: (context, state) { + var theme = Theme.of(context); var service = configuration.chatServiceBuilder?.call(context) ?? configuration.chatService; var chatScreen = ChatScreen( @@ -47,6 +48,7 @@ List getChatStoryRoutes( chatScreen, ) ?? Scaffold( + backgroundColor: theme.colorScheme.surface, body: chatScreen, ), ); @@ -58,6 +60,8 @@ List getChatStoryRoutes( var chatId = state.pathParameters['id']; var service = configuration.chatServiceBuilder?.call(context) ?? configuration.chatService; + var theme = Theme.of(context); + var chatDetailScreen = ChatDetailScreen( chatTitleBuilder: configuration.chatTitleBuilder, usernameBuilder: configuration.usernameBuilder, @@ -118,6 +122,7 @@ List getChatStoryRoutes( chatDetailScreen, ) ?? Scaffold( + backgroundColor: theme.colorScheme.surface, body: chatDetailScreen, ), ); @@ -128,6 +133,8 @@ List getChatStoryRoutes( pageBuilder: (context, state) { var service = configuration.chatServiceBuilder?.call(context) ?? configuration.chatService; + var theme = Theme.of(context); + var newChatScreen = NewChatScreen( options: configuration.chatOptionsBuilder(context), translations: configuration.translationsBuilder?.call(context) ?? @@ -165,6 +172,7 @@ List getChatStoryRoutes( newChatScreen, ) ?? Scaffold( + backgroundColor: theme.colorScheme.surface, body: newChatScreen, ), ); @@ -175,6 +183,8 @@ List getChatStoryRoutes( pageBuilder: (context, state) { var service = configuration.chatServiceBuilder?.call(context) ?? configuration.chatService; + var theme = Theme.of(context); + var newGroupChatScreen = NewGroupChatScreen( options: configuration.chatOptionsBuilder(context), translations: configuration.translationsBuilder?.call(context) ?? @@ -193,6 +203,7 @@ List getChatStoryRoutes( newGroupChatScreen, ) ?? Scaffold( + backgroundColor: theme.colorScheme.surface, body: newGroupChatScreen, ), ); @@ -204,6 +215,8 @@ List getChatStoryRoutes( var service = configuration.chatServiceBuilder?.call(context) ?? configuration.chatService; var users = state.extra! as List; + var theme = Theme.of(context); + var newGroupChatOverviewScreen = NewGroupChatOverviewScreen( options: configuration.chatOptionsBuilder(context), translations: configuration.translationsBuilder?.call(context) ?? @@ -223,7 +236,7 @@ List getChatStoryRoutes( ), ); if (context.mounted) { - await context.push( + context.go( ChatUserStoryRoutes.chatDetailViewPath(chat.id ?? ''), ); } @@ -237,6 +250,7 @@ List getChatStoryRoutes( newGroupChatOverviewScreen, ) ?? Scaffold( + backgroundColor: theme.colorScheme.surface, body: newGroupChatOverviewScreen, ), ); @@ -250,6 +264,8 @@ List getChatStoryRoutes( var id = userId == 'null' ? null : userId; var service = configuration.chatServiceBuilder?.call(context) ?? configuration.chatService; + var theme = Theme.of(context); + var profileScreen = ChatProfileScreen( translations: configuration.translationsBuilder?.call(context) ?? configuration.translations, @@ -274,6 +290,7 @@ List getChatStoryRoutes( profileScreen, ) ?? Scaffold( + backgroundColor: theme.colorScheme.surface, body: profileScreen, ), ); diff --git a/packages/flutter_chat/pubspec.yaml b/packages/flutter_chat/pubspec.yaml index 778afe6..9f26749 100644 --- a/packages/flutter_chat/pubspec.yaml +++ b/packages/flutter_chat/pubspec.yaml @@ -4,7 +4,7 @@ name: flutter_chat description: A new Flutter package project. -version: 2.0.0 +version: 3.0.0 publish_to: none @@ -20,23 +20,23 @@ dependencies: git: url: https://github.com/Iconica-Development/flutter_chat path: packages/flutter_chat_view - ref: 2.0.0 + ref: 3.0.0 flutter_chat_interface: git: url: https://github.com/Iconica-Development/flutter_chat path: packages/flutter_chat_interface - ref: 2.0.0 + ref: 3.0.0 flutter_chat_local: git: url: https://github.com/Iconica-Development/flutter_chat path: packages/flutter_chat_local - ref: 2.0.0 + ref: 3.0.0 uuid: ^4.3.3 dev_dependencies: flutter_iconica_analysis: git: url: https://github.com/Iconica-Development/flutter_iconica_analysis - ref: 6.0.0 + ref: 7.0.0 flutter: diff --git a/packages/flutter_chat_firebase/pubspec.yaml b/packages/flutter_chat_firebase/pubspec.yaml index d4ebeab..c9a8225 100644 --- a/packages/flutter_chat_firebase/pubspec.yaml +++ b/packages/flutter_chat_firebase/pubspec.yaml @@ -4,7 +4,7 @@ name: flutter_chat_firebase description: A new Flutter package project. -version: 2.0.0 +version: 3.0.0 publish_to: none environment: @@ -23,12 +23,12 @@ dependencies: git: url: https://github.com/Iconica-Development/flutter_chat path: packages/flutter_chat_interface - ref: 2.0.0 + ref: 3.0.0 dev_dependencies: flutter_iconica_analysis: git: url: https://github.com/Iconica-Development/flutter_iconica_analysis - ref: 6.0.0 + ref: 7.0.0 flutter: diff --git a/packages/flutter_chat_interface/lib/flutter_chat_interface.dart b/packages/flutter_chat_interface/lib/flutter_chat_interface.dart index 9cdb2fe..a490142 100644 --- a/packages/flutter_chat_interface/lib/flutter_chat_interface.dart +++ b/packages/flutter_chat_interface/lib/flutter_chat_interface.dart @@ -4,6 +4,6 @@ /// library flutter_chat_interface; -export 'package:flutter_chat_interface/src/chat_data_provider.dart'; -export 'package:flutter_chat_interface/src/model/model.dart'; -export 'package:flutter_chat_interface/src/service/service.dart'; +export "package:flutter_chat_interface/src/chat_data_provider.dart"; +export "package:flutter_chat_interface/src/model/model.dart"; +export "package:flutter_chat_interface/src/service/service.dart"; diff --git a/packages/flutter_chat_interface/lib/src/chat_data_provider.dart b/packages/flutter_chat_interface/lib/src/chat_data_provider.dart index 61bc4de..3e01507 100644 --- a/packages/flutter_chat_interface/lib/src/chat_data_provider.dart +++ b/packages/flutter_chat_interface/lib/src/chat_data_provider.dart @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'package:flutter_chat_interface/flutter_chat_interface.dart'; -import 'package:flutter_data_interface/flutter_data_interface.dart'; +import "package:flutter_chat_interface/flutter_chat_interface.dart"; +import "package:flutter_data_interface/flutter_data_interface.dart"; class ChatDataProvider extends DataInterface { ChatDataProvider({ diff --git a/packages/flutter_chat_interface/lib/src/model/chat.dart b/packages/flutter_chat_interface/lib/src/model/chat.dart index e839cfb..5a5e1f2 100644 --- a/packages/flutter_chat_interface/lib/src/model/chat.dart +++ b/packages/flutter_chat_interface/lib/src/model/chat.dart @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'package:flutter_chat_interface/flutter_chat_interface.dart'; +import "package:flutter_chat_interface/flutter_chat_interface.dart"; abstract class ChatModelInterface { ChatModelInterface copyWith(); diff --git a/packages/flutter_chat_interface/lib/src/model/chat_image_message.dart b/packages/flutter_chat_interface/lib/src/model/chat_image_message.dart index 5fc089b..54f3225 100644 --- a/packages/flutter_chat_interface/lib/src/model/chat_image_message.dart +++ b/packages/flutter_chat_interface/lib/src/model/chat_image_message.dart @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'package:flutter_chat_interface/flutter_chat_interface.dart'; +import "package:flutter_chat_interface/flutter_chat_interface.dart"; /// An abstract class defining the interface for an image message in a chat. abstract class ChatImageMessageModelInterface extends ChatMessageModel { diff --git a/packages/flutter_chat_interface/lib/src/model/chat_message.dart b/packages/flutter_chat_interface/lib/src/model/chat_message.dart index 7212144..ebc1b10 100644 --- a/packages/flutter_chat_interface/lib/src/model/chat_message.dart +++ b/packages/flutter_chat_interface/lib/src/model/chat_message.dart @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'package:flutter_chat_interface/src/model/chat_user.dart'; +import "package:flutter_chat_interface/src/model/chat_user.dart"; abstract class ChatMessageModelInterface { ChatUserModel get sender; diff --git a/packages/flutter_chat_interface/lib/src/model/chat_text_message.dart b/packages/flutter_chat_interface/lib/src/model/chat_text_message.dart index 52b7b3d..73a8fd3 100644 --- a/packages/flutter_chat_interface/lib/src/model/chat_text_message.dart +++ b/packages/flutter_chat_interface/lib/src/model/chat_text_message.dart @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'package:flutter_chat_interface/flutter_chat_interface.dart'; +import "package:flutter_chat_interface/flutter_chat_interface.dart"; abstract class ChatTextMessageModelInterface extends ChatMessageModel { ChatTextMessageModelInterface({ diff --git a/packages/flutter_chat_interface/lib/src/model/chat_user.dart b/packages/flutter_chat_interface/lib/src/model/chat_user.dart index 814c930..129f7d8 100644 --- a/packages/flutter_chat_interface/lib/src/model/chat_user.dart +++ b/packages/flutter_chat_interface/lib/src/model/chat_user.dart @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'package:flutter/material.dart'; +import "package:flutter/material.dart"; abstract class ChatUserModelInterface { String? get id; @@ -49,17 +49,17 @@ class ChatUserModel implements ChatUserModelInterface { @override String? get fullName { - var fullName = ''; + var fullName = ""; if (firstName != null && lastName != null) { - fullName += '$firstName $lastName'; + fullName += "$firstName $lastName"; } else if (firstName != null) { fullName += firstName!; } else if (lastName != null) { fullName += lastName!; } - return fullName == '' ? null : fullName; + return fullName == "" ? null : fullName; } @override 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 d3f114a..2c51d7e 100644 --- a/packages/flutter_chat_interface/lib/src/model/group_chat.dart +++ b/packages/flutter_chat_interface/lib/src/model/group_chat.dart @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'package:flutter_chat_interface/flutter_chat_interface.dart'; +import "package:flutter_chat_interface/flutter_chat_interface.dart"; abstract class GroupChatModelInterface extends ChatModel { GroupChatModelInterface({ diff --git a/packages/flutter_chat_interface/lib/src/model/model.dart b/packages/flutter_chat_interface/lib/src/model/model.dart index 2e23e5c..c11c337 100644 --- a/packages/flutter_chat_interface/lib/src/model/model.dart +++ b/packages/flutter_chat_interface/lib/src/model/model.dart @@ -1,7 +1,7 @@ -export 'chat.dart'; -export 'chat_image_message.dart'; -export 'chat_message.dart'; -export 'chat_text_message.dart'; -export 'chat_user.dart'; -export 'group_chat.dart'; -export 'personal_chat.dart'; +export "chat.dart"; +export "chat_image_message.dart"; +export "chat_message.dart"; +export "chat_text_message.dart"; +export "chat_user.dart"; +export "group_chat.dart"; +export "personal_chat.dart"; 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 91d6c00..0c907de 100644 --- a/packages/flutter_chat_interface/lib/src/model/personal_chat.dart +++ b/packages/flutter_chat_interface/lib/src/model/personal_chat.dart @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'package:flutter_chat_interface/flutter_chat_interface.dart'; +import "package:flutter_chat_interface/flutter_chat_interface.dart"; abstract class PersonalChatModelInterface extends ChatModel { PersonalChatModelInterface({ diff --git a/packages/flutter_chat_interface/lib/src/service/chat_detail_service.dart b/packages/flutter_chat_interface/lib/src/service/chat_detail_service.dart index 6522b97..b3d77ae 100644 --- a/packages/flutter_chat_interface/lib/src/service/chat_detail_service.dart +++ b/packages/flutter_chat_interface/lib/src/service/chat_detail_service.dart @@ -1,6 +1,6 @@ -import 'dart:typed_data'; -import 'package:flutter/material.dart'; -import 'package:flutter_chat_interface/flutter_chat_interface.dart'; +import "dart:typed_data"; +import "package:flutter/material.dart"; +import "package:flutter_chat_interface/flutter_chat_interface.dart"; /// An abstract class defining the interface for a chat detail service. abstract class ChatDetailService with ChangeNotifier { diff --git a/packages/flutter_chat_interface/lib/src/service/chat_overview_service.dart b/packages/flutter_chat_interface/lib/src/service/chat_overview_service.dart index d0c806f..7329abf 100644 --- a/packages/flutter_chat_interface/lib/src/service/chat_overview_service.dart +++ b/packages/flutter_chat_interface/lib/src/service/chat_overview_service.dart @@ -1,4 +1,4 @@ -import 'package:flutter_chat_interface/flutter_chat_interface.dart'; +import "package:flutter_chat_interface/flutter_chat_interface.dart"; abstract class ChatOverviewService { /// Retrieves a stream of chats. diff --git a/packages/flutter_chat_interface/lib/src/service/chat_service.dart b/packages/flutter_chat_interface/lib/src/service/chat_service.dart index c6b87f0..de8e208 100644 --- a/packages/flutter_chat_interface/lib/src/service/chat_service.dart +++ b/packages/flutter_chat_interface/lib/src/service/chat_service.dart @@ -1,5 +1,5 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first -import 'package:flutter_chat_interface/flutter_chat_interface.dart'; +import "package:flutter_chat_interface/flutter_chat_interface.dart"; class ChatService { final ChatUserService chatUserService; diff --git a/packages/flutter_chat_interface/lib/src/service/service.dart b/packages/flutter_chat_interface/lib/src/service/service.dart index 7eddcf4..79d3772 100644 --- a/packages/flutter_chat_interface/lib/src/service/service.dart +++ b/packages/flutter_chat_interface/lib/src/service/service.dart @@ -1,4 +1,4 @@ -export 'chat_detail_service.dart'; -export 'chat_overview_service.dart'; -export 'chat_service.dart'; -export 'user_service.dart'; +export "chat_detail_service.dart"; +export "chat_overview_service.dart"; +export "chat_service.dart"; +export "user_service.dart"; 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 17307d9..f1daa27 100644 --- a/packages/flutter_chat_interface/lib/src/service/user_service.dart +++ b/packages/flutter_chat_interface/lib/src/service/user_service.dart @@ -1,4 +1,4 @@ -import 'package:flutter_chat_interface/flutter_chat_interface.dart'; +import "package:flutter_chat_interface/flutter_chat_interface.dart"; abstract class ChatUserService { /// Retrieves a user based on the ID. diff --git a/packages/flutter_chat_interface/pubspec.yaml b/packages/flutter_chat_interface/pubspec.yaml index 40d2480..80fd053 100644 --- a/packages/flutter_chat_interface/pubspec.yaml +++ b/packages/flutter_chat_interface/pubspec.yaml @@ -4,7 +4,7 @@ name: flutter_chat_interface description: A new Flutter package project. -version: 2.0.0 +version: 3.0.0 publish_to: none environment: @@ -23,6 +23,6 @@ dev_dependencies: flutter_iconica_analysis: git: url: https://github.com/Iconica-Development/flutter_iconica_analysis - ref: 6.0.0 + ref: 7.0.0 flutter: diff --git a/packages/flutter_chat_local/pubspec.yaml b/packages/flutter_chat_local/pubspec.yaml index c37efa4..e77b94c 100644 --- a/packages/flutter_chat_local/pubspec.yaml +++ b/packages/flutter_chat_local/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_chat_local description: "A new Flutter package project." -version: 2.0.0 +version: 3.0.0 publish_to: none environment: @@ -14,7 +14,7 @@ dependencies: git: url: https://github.com/Iconica-Development/flutter_chat path: packages/flutter_chat_interface - ref: 2.0.0 + ref: 3.0.0 dev_dependencies: flutter_test: @@ -22,5 +22,5 @@ dev_dependencies: flutter_iconica_analysis: git: url: https://github.com/Iconica-Development/flutter_iconica_analysis - ref: 6.0.0 + ref: 7.0.0 flutter: diff --git a/packages/flutter_chat_view/lib/src/components/chat_bottom.dart b/packages/flutter_chat_view/lib/src/components/chat_bottom.dart index c924516..d4d830a 100644 --- a/packages/flutter_chat_view/lib/src/components/chat_bottom.dart +++ b/packages/flutter_chat_view/lib/src/components/chat_bottom.dart @@ -73,7 +73,7 @@ class _ChatBottomState extends State { IconButton( onPressed: widget.onPressSelectImage, icon: Icon( - Icons.image, + Icons.image_outlined, color: widget.iconColor, ), ), @@ -105,6 +105,7 @@ class _ChatBottomState extends State { ], ), widget.translations, + context, ), ), ); diff --git a/packages/flutter_chat_view/lib/src/components/chat_detail_row.dart b/packages/flutter_chat_view/lib/src/components/chat_detail_row.dart index 107a855..0f1d72a 100644 --- a/packages/flutter_chat_view/lib/src/components/chat_detail_row.dart +++ b/packages/flutter_chat_view/lib/src/components/chat_detail_row.dart @@ -54,7 +54,7 @@ class _ChatDetailRowState extends State { widget.message.timestamp.minute == widget.previousMessage?.timestamp.minute; var hasHeader = isNewDate || isSameSender; - + var theme = Theme.of(context); return Padding( padding: EdgeInsets.only( top: isNewDate || isSameSender ? 25.0 : 0, @@ -160,10 +160,7 @@ class _ChatDetailRowState extends State { ) .split(' ') .last, - style: const TextStyle( - fontSize: 12, - color: Color(0xFFBBBBBB), - ), + style: theme.textTheme.bodySmall, textAlign: TextAlign.end, ), ], diff --git a/packages/flutter_chat_view/lib/src/components/chat_row.dart b/packages/flutter_chat_view/lib/src/components/chat_row.dart index 9c8875a..4d533f7 100644 --- a/packages/flutter_chat_view/lib/src/components/chat_row.dart +++ b/packages/flutter_chat_view/lib/src/components/chat_row.dart @@ -30,84 +30,83 @@ class ChatRow extends StatelessWidget { final Widget? avatar; @override - Widget build(BuildContext context) => Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(left: 10.0), - child: avatar, - ), - Expanded( - child: Padding( - padding: const EdgeInsets.only(left: 16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - title, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 16, - fontWeight: unreadMessages > 0 - ? FontWeight.w900 - : FontWeight.w500, + Widget build(BuildContext context) { + var theme = Theme.of(context); + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 10.0), + child: avatar, + ), + Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: unreadMessages > 0 + ? theme.textTheme.bodyLarge + : theme.textTheme.bodyMedium, + ), + if (subTitle != null) ...[ + Padding( + padding: const EdgeInsets.only(top: 3.0), + child: Text( + subTitle!, + style: unreadMessages > 0 + ? theme.textTheme.bodyLarge + : theme.textTheme.bodyMedium, + overflow: TextOverflow.ellipsis, + maxLines: 2, ), ), - if (subTitle != null) - Padding( - padding: const EdgeInsets.only(top: 3.0), - child: Text( - subTitle!, - style: TextStyle( - fontSize: 16, - fontWeight: unreadMessages > 0 - ? FontWeight.w500 - : FontWeight.w300, - ), - overflow: TextOverflow.ellipsis, - maxLines: 2, - ), - ), ], - ), + ], ), ), - Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - if (lastUsed != null) // Check if lastUsed is not null - Padding( - padding: const EdgeInsets.only(bottom: 4.0), + ), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + if (lastUsed != null) ...[ + Padding( + padding: const EdgeInsets.only(bottom: 4.0), + child: Text( + lastUsed!, + style: const TextStyle( + color: Color(0xFFBBBBBB), + fontSize: 14, + ), + ), + ), + ], + if (unreadMessages > 0) ...[ + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary, + shape: BoxShape.circle, + ), + child: Center( child: Text( - lastUsed!, + unreadMessages.toString(), style: const TextStyle( - color: Color(0xFFBBBBBB), fontSize: 14, ), ), ), - if (unreadMessages > 0) ...[ - Container( - width: 20, - height: 20, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, - shape: BoxShape.circle, - ), - child: Center( - child: Text( - unreadMessages.toString(), - style: const TextStyle( - fontSize: 14, - ), - ), - ), - ), - ], + ), ], - ), - ], - ); + ], + ), + ], + ); + } } diff --git a/packages/flutter_chat_view/lib/src/config/chat_options.dart b/packages/flutter_chat_view/lib/src/config/chat_options.dart index ced7211..51a7b7f 100644 --- a/packages/flutter_chat_view/lib/src/config/chat_options.dart +++ b/packages/flutter_chat_view/lib/src/config/chat_options.dart @@ -59,7 +59,10 @@ Widget _createNewChatButton( ChatTranslations translations, ) => Padding( - padding: const EdgeInsets.fromLTRB(5, 24, 5, 24), + padding: const EdgeInsets.symmetric( + vertical: 24, + horizontal: 5, + ), child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, @@ -74,7 +77,7 @@ Widget _createNewChatButton( style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w800, - fontSize: 18, + fontSize: 20, ), ), ), @@ -84,43 +87,43 @@ Widget _createMessageInput( TextEditingController textEditingController, Widget suffixIcon, ChatTranslations translations, -) => - TextField( - textCapitalization: TextCapitalization.sentences, - controller: textEditingController, - decoration: InputDecoration( - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(26.5), - borderSide: const BorderSide( - color: Colors.black, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(26.5), - borderSide: const BorderSide( - color: Colors.black, - ), - ), - contentPadding: const EdgeInsets.symmetric( - vertical: 0, - horizontal: 30, + BuildContext context, +) { + var theme = Theme.of(context); + return TextField( + textCapitalization: TextCapitalization.sentences, + controller: textEditingController, + decoration: InputDecoration( + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(25), + borderSide: const BorderSide( + color: Colors.black, ), - hintText: translations.messagePlaceholder, - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(25), + borderSide: const BorderSide( color: Colors.black, ), - fillColor: Colors.white, - filled: true, - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(26.5), - ), - borderSide: BorderSide.none, + ), + contentPadding: const EdgeInsets.symmetric( + vertical: 0, + horizontal: 30, + ), + hintText: translations.messagePlaceholder, + hintStyle: theme.inputDecorationTheme.hintStyle, + fillColor: Colors.white, + filled: true, + border: const OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(25), ), - suffixIcon: suffixIcon, + borderSide: BorderSide.none, ), - ); + suffixIcon: suffixIcon, + ), + ); +} Widget _createChatRowContainer( Widget chatRow, @@ -130,7 +133,10 @@ Widget _createChatRowContainer( vertical: 12.0, horizontal: 10.0, ), - child: chatRow, + child: Container( + color: Colors.transparent, + child: chatRow, + ), ); Widget _createImagePickerContainer( @@ -166,6 +172,7 @@ Widget _createImagePickerContainer( Scaffold _createScaffold( AppBar appbar, Widget body, + Color backgroundColor, ) => Scaffold( appBar: appbar, @@ -196,31 +203,32 @@ Widget _createGroupAvatar( Widget _createNoChatsPlaceholder( ChatTranslations translations, -) => - Center( - child: Text( - translations.noChatsFound, - textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.white, - fontSize: 18, - ), - ), - ); + BuildContext context, +) { + var theme = Theme.of(context); + return Center( + child: Text( + translations.noChatsFound, + textAlign: TextAlign.center, + style: theme.textTheme.bodySmall, + ), + ); +} Widget _createNoUsersPlaceholder( ChatTranslations translations, -) => - Center( - child: Text( - translations.noUsersFound, - textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.white, - fontSize: 18, - ), - ), - ); + BuildContext context, +) { + var theme = Theme.of(context); + return Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: Text( + translations.noUsersFound, + textAlign: TextAlign.center, + style: theme.textTheme.bodySmall, + ), + ); +} typedef ButtonBuilder = Widget Function( BuildContext context, @@ -232,6 +240,7 @@ typedef TextInputBuilder = Widget Function( TextEditingController textEditingController, Widget suffixIcon, ChatTranslations translations, + BuildContext context, ); typedef ContainerBuilder = Widget Function( @@ -247,6 +256,7 @@ typedef ImagePickerContainerBuilder = Widget Function( typedef ScaffoldBuilder = Scaffold Function( AppBar appBar, Widget body, + Color backgroundColor, ); typedef UserAvatarBuilder = Widget Function( @@ -262,8 +272,10 @@ typedef GroupAvatarBuilder = Widget Function( typedef NoChatsPlaceholderBuilder = Widget Function( ChatTranslations translations, + BuildContext context, ); typedef NoUsersPlaceholderBuilder = Widget Function( ChatTranslations translations, + BuildContext context, ); 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 76cbb22..5ada33f 100644 --- a/packages/flutter_chat_view/lib/src/config/chat_translations.dart +++ b/packages/flutter_chat_view/lib/src/config/chat_translations.dart @@ -36,13 +36,17 @@ class ChatTranslations { required this.uploadFile, required this.takePicture, required this.anonymousUser, + required this.groupNameValidatorEmpty, + required this.groupNameValidatorTooLong, + required this.groupNameHintText, + required this.newGroupChatTitle, }); /// Default translations for the chat component view const ChatTranslations.empty({ this.chatsTitle = 'Chats', this.chatsUnread = 'unread', - this.newChatButton = 'Start a chat', + this.newChatButton = 'Start chat', this.newGroupChatButton = 'Create a group chat', this.newChatTitle = 'Start a chat', this.image = 'Image', @@ -68,6 +72,11 @@ class ChatTranslations { this.imagePickerTitle = 'Do you want to upload a file or take a picture?', this.uploadFile = 'UPLOAD FILE', this.takePicture = 'TAKE PICTURE', + this.groupNameHintText = 'Group chat name', + this.groupNameValidatorEmpty = 'Please enter a group chat name', + this.groupNameValidatorTooLong = + 'Group name is too long, max 15 characters', + this.newGroupChatTitle = 'New Group Chat', }); final String chatsTitle; @@ -98,6 +107,10 @@ class ChatTranslations { /// Shown when the user has no name final String anonymousUser; + final String groupNameValidatorEmpty; + final String groupNameValidatorTooLong; + final String groupNameHintText; + final String newGroupChatTitle; // copyWith method to override the default values ChatTranslations copyWith({ @@ -127,6 +140,10 @@ class ChatTranslations { String? uploadFile, String? takePicture, String? anonymousUser, + String? groupNameValidatorEmpty, + String? groupNameValidatorTooLong, + String? groupNameHintText, + String? newGroupChatTitle, }) => ChatTranslations( chatsTitle: chatsTitle ?? this.chatsTitle, @@ -160,5 +177,9 @@ class ChatTranslations { uploadFile: uploadFile ?? this.uploadFile, takePicture: takePicture ?? this.takePicture, anonymousUser: anonymousUser ?? this.anonymousUser, + groupNameValidatorEmpty: this.groupNameValidatorEmpty, + groupNameValidatorTooLong: this.groupNameValidatorTooLong, + groupNameHintText: this.groupNameHintText, + newGroupChatTitle: this.newGroupChatTitle, ); } 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 beb99e0..5df5fe9 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 @@ -168,8 +168,9 @@ class _ChatDetailScreenState extends State { builder: (context, AsyncSnapshot snapshot) { var chatModel = snapshot.data; return Scaffold( + backgroundColor: theme.colorScheme.surface, appBar: AppBar( - backgroundColor: theme.appBarTheme.backgroundColor ?? Colors.black, + backgroundColor: theme.appBarTheme.backgroundColor, iconTheme: theme.appBarTheme.iconTheme ?? const IconThemeData(color: Colors.white), centerTitle: true, @@ -188,14 +189,6 @@ class _ChatDetailScreenState extends State { children: chat == null ? [] : [ - if (chatModel is GroupChatModel) ...[ - widget.options.groupAvatarBuilder( - chatModel.title, - chatModel.imageUrl, - 36.0, - ), - ] else - ...[], Padding( padding: (chatModel is GroupChatModel) ? const EdgeInsets.only(left: 15.5) @@ -216,12 +209,7 @@ class _ChatDetailScreenState extends State { ? chatModel.user.firstName ?? widget.translations.anonymousUser : '', - style: theme.appBarTheme.titleTextStyle ?? - TextStyle( - fontWeight: FontWeight.w800, - fontSize: 24, - color: Theme.of(context).primaryColor, - ), + style: theme.appBarTheme.titleTextStyle, ), ), ], @@ -271,11 +259,7 @@ class _ChatDetailScreenState extends State { .writeFirstMessageInGroupChat : widget .translations.writeMessageToStartChat, - style: const TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.w400, - color: Color.fromRGBO(33, 33, 33, 1), - ), + style: theme.textTheme.bodySmall, ), ), ...detailRows, diff --git a/packages/flutter_chat_view/lib/src/screens/chat_profile_screen.dart b/packages/flutter_chat_view/lib/src/screens/chat_profile_screen.dart index b28ac96..4cefe91 100644 --- a/packages/flutter_chat_view/lib/src/screens/chat_profile_screen.dart +++ b/packages/flutter_chat_view/lib/src/screens/chat_profile_screen.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_chat_view/flutter_chat_view.dart'; -import 'package:flutter_chat_view/src/services/profile_service.dart'; import 'package:flutter_profile/flutter_profile.dart'; class ChatProfileScreen extends StatefulWidget { @@ -35,7 +34,6 @@ class ChatProfileScreen extends StatefulWidget { class _ProfileScreenState extends State { @override Widget build(BuildContext context) { - var size = MediaQuery.of(context).size; var hasUser = widget.userId == null; var theme = Theme.of(context); return FutureBuilder( @@ -67,10 +65,10 @@ class _ProfileScreenState extends State { imageUrl: data.imageUrl, ); } - return Scaffold( + backgroundColor: theme.colorScheme.surface, appBar: AppBar( - backgroundColor: theme.appBarTheme.backgroundColor ?? Colors.black, + backgroundColor: theme.appBarTheme.backgroundColor, iconTheme: theme.appBarTheme.iconTheme ?? const IconThemeData(color: Colors.white), title: Text( @@ -81,32 +79,28 @@ class _ProfileScreenState extends State { : (data is GroupChatModel) ? data.title : '', - style: theme.appBarTheme.titleTextStyle ?? - const TextStyle( - color: Colors.white, - ), + style: theme.appBarTheme.titleTextStyle, ), ), body: snapshot.hasData ? ListView( children: [ - const SizedBox( - height: 10, - ), - SizedBox( - height: 200, - width: size.width, - child: ProfilePage( - user: user!, - itemBuilderOptions: ItemBuilderOptions( - readOnly: true, - ), - service: ChatProfileService(), + Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: Avatar( + user: user, ), ), + const Divider( + color: Colors.white, + thickness: 10, + ), if (data is GroupChatModel) ...[ Padding( - padding: const EdgeInsets.symmetric(horizontal: 100), + padding: const EdgeInsets.symmetric( + horizontal: 100, + vertical: 20, + ), child: Text( widget.translations.chatProfileUsers, style: const TextStyle( diff --git a/packages/flutter_chat_view/lib/src/screens/chat_screen.dart b/packages/flutter_chat_view/lib/src/screens/chat_screen.dart index edafe45..2475847 100644 --- a/packages/flutter_chat_view/lib/src/screens/chat_screen.dart +++ b/packages/flutter_chat_view/lib/src/screens/chat_screen.dart @@ -77,16 +77,10 @@ class _ChatScreenState extends State { var theme = Theme.of(context); return widget.options.scaffoldBuilder( AppBar( - backgroundColor: - theme.appBarTheme.backgroundColor ?? const Color(0xff212121), + backgroundColor: theme.appBarTheme.backgroundColor, title: Text( translations.chatsTitle, - style: theme.appBarTheme.titleTextStyle ?? - TextStyle( - fontWeight: FontWeight.w800, - fontSize: 24, - color: Theme.of(context).primaryColor, - ), + style: theme.appBarTheme.titleTextStyle, ), centerTitle: true, actions: [ @@ -120,7 +114,7 @@ class _ChatScreenState extends State { controller: controller, physics: const AlwaysScrollableScrollPhysics(), padding: widget.options.paddingAroundChatList ?? - const EdgeInsets.fromLTRB(28, 16, 28, 0), + const EdgeInsets.symmetric(vertical: 16, horizontal: 28), children: [ StreamBuilder>( stream: widget.service.chatOverviewService.getChatsStream(), @@ -138,157 +132,172 @@ class _ChatScreenState extends State { return Center( child: Text( translations.noChatsFound, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w400, - ), + style: theme.textTheme.bodySmall, ), ); } else { - _hasCalledOnNoChats = - false; // Reset the flag if there are chats + _hasCalledOnNoChats = false; } return Column( children: [ for (ChatModel chat in (snapshot.data ?? []).where( (chat) => !deletedChats.contains(chat.id), )) ...[ - Builder( - builder: (context) => !(widget - .disableDismissForPermanentChats && - !chat.canBeDeleted) - ? Dismissible( - confirmDismiss: (_) async => - widget.deleteChatDialog - ?.call(context, chat) ?? - showModalBottomSheet( - context: context, - builder: (BuildContext context) => - Container( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: - CrossAxisAlignment.stretch, - children: [ - Text( - chat.canBeDeleted - ? translations - .deleteChatModalTitle - : translations - .chatCantBeDeleted, - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 24), - if (chat.canBeDeleted) - Padding( - padding: const EdgeInsets - .symmetric( - horizontal: 16, - ), - child: Text( - translations - .deleteChatModalDescription, - textAlign: - TextAlign.center, - style: const TextStyle( - fontSize: 18, - ), + Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Theme.of(context) + .colorScheme + .secondary + .withOpacity(0.3), + width: 0.5, + ), + ), + ), + child: Builder( + builder: (context) => !(widget + .disableDismissForPermanentChats && + !chat.canBeDeleted) + ? Dismissible( + confirmDismiss: (_) async => + widget.deleteChatDialog + ?.call(context, chat) ?? + showModalBottomSheet( + context: context, + builder: (BuildContext context) => + Container( + padding: + const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.stretch, + children: [ + Text( + chat.canBeDeleted + ? translations + .deleteChatModalTitle + : translations + .chatCantBeDeleted, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 24, + fontWeight: + FontWeight.bold, ), ), - const SizedBox( - height: 24, - ), - Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => - Navigator.of( - context, - ).pop(false), + const SizedBox(height: 24), + if (chat.canBeDeleted) + Padding( + padding: const EdgeInsets + .symmetric( + horizontal: 16, + ), child: Text( translations - .deleteChatModalCancel, + .deleteChatModalDescription, + textAlign: + TextAlign.center, style: const TextStyle( - color: Colors.black, fontSize: 18, ), ), ), - if (chat.canBeDeleted) - const SizedBox( - width: 16, - ), - if (chat.canBeDeleted) + const SizedBox( + height: 24, + ), + Row( + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ ElevatedButton( - style: ElevatedButton - .styleFrom( - backgroundColor: - Theme.of(context) - .primaryColor, - ), onPressed: () => Navigator.of( context, - ).pop( - true, - ), + ).pop(false), child: Text( translations - .deleteChatModalConfirm, + .deleteChatModalCancel, style: const TextStyle( - color: Colors.white, + color: Colors.black, fontSize: 18, ), ), ), - ], - ), - ], + if (chat.canBeDeleted) + const SizedBox( + width: 16, + ), + if (chat.canBeDeleted) + ElevatedButton( + style: ElevatedButton + .styleFrom( + backgroundColor: + Theme.of( + context, + ).primaryColor, + ), + onPressed: () => + Navigator.of( + context, + ).pop( + true, + ), + child: Text( + translations + .deleteChatModalConfirm, + style: + const TextStyle( + color: + Colors.white, + fontSize: 18, + ), + ), + ), + ], + ), + ], + ), ), ), - ), - onDismissed: (_) { - setState(() { - deletedChats.add(chat.id!); - }); - widget.onDeleteChat(chat); - }, - background: Container( - color: Colors.red, - child: Align( - alignment: Alignment.centerRight, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - translations.deleteChatButton, + onDismissed: (_) { + setState(() { + deletedChats.add(chat.id!); + }); + widget.onDeleteChat(chat); + }, + background: Container( + color: Colors.red, + child: Align( + alignment: Alignment.centerRight, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + translations.deleteChatButton, + ), ), ), ), - ), - key: ValueKey( - chat.id.toString(), - ), - child: ChatListItem( + key: ValueKey( + chat.id.toString(), + ), + child: ChatListItem( + widget: widget, + chat: chat, + translations: translations, + dateFormatter: _dateFormatter, + ), + ) + : ChatListItem( widget: widget, chat: chat, translations: translations, dateFormatter: _dateFormatter, ), - ) - : ChatListItem( - widget: widget, - chat: chat, - translations: translations, - dateFormatter: _dateFormatter, - ), + ), ), ], ], @@ -308,6 +317,7 @@ class _ChatScreenState extends State { ), ], ), + theme.colorScheme.surface, ); } } @@ -327,59 +337,52 @@ class ChatListItem extends StatelessWidget { final DateFormatter _dateFormatter; @override - Widget build(BuildContext context) => Column( - children: [ - GestureDetector( - onTap: () => widget.onPressChat(chat), - child: Container( - color: Colors.transparent, - child: widget.options.chatRowContainerBuilder( - (chat is PersonalChatModel) - ? ChatRow( - unreadMessages: chat.unreadMessages ?? 0, - avatar: widget.options.userAvatarBuilder( - (chat as PersonalChatModel).user, - 40.0, - ), - title: (chat as PersonalChatModel).user.fullName ?? - translations.anonymousUser, - subTitle: chat.lastMessage != null - ? chat.lastMessage is ChatTextMessageModel - ? (chat.lastMessage! as ChatTextMessageModel) - .text - : '📷 ' - '${translations.image}' - : '', - lastUsed: chat.lastUsed != null - ? _dateFormatter.format( - date: chat.lastUsed!, - ) - : null, - ) - : ChatRow( - title: (chat as GroupChatModel).title, - unreadMessages: chat.unreadMessages ?? 0, - subTitle: chat.lastMessage != null - ? chat.lastMessage is ChatTextMessageModel - ? (chat.lastMessage! as ChatTextMessageModel) - .text - : '📷 ' - '${translations.image}' - : '', - avatar: widget.options.groupAvatarBuilder( - (chat as GroupChatModel).title, - (chat as GroupChatModel).imageUrl, - 40.0, - ), - lastUsed: chat.lastUsed != null - ? _dateFormatter.format( - date: chat.lastUsed!, - ) - : null, - ), - ), - ), - ), - ], + Widget build(BuildContext context) => GestureDetector( + onTap: () { + widget.onPressChat(chat); + }, + child: widget.options.chatRowContainerBuilder( + (chat is PersonalChatModel) + ? ChatRow( + unreadMessages: chat.unreadMessages ?? 0, + avatar: widget.options.userAvatarBuilder( + (chat as PersonalChatModel).user, + 40.0, + ), + title: (chat as PersonalChatModel).user.fullName ?? + translations.anonymousUser, + subTitle: chat.lastMessage != null + ? chat.lastMessage is ChatTextMessageModel + ? (chat.lastMessage! as ChatTextMessageModel).text + : '📷 ' + '${translations.image}' + : '', + lastUsed: chat.lastUsed != null + ? _dateFormatter.format( + date: chat.lastUsed!, + ) + : null, + ) + : ChatRow( + title: (chat as GroupChatModel).title, + unreadMessages: chat.unreadMessages ?? 0, + subTitle: chat.lastMessage != null + ? chat.lastMessage is ChatTextMessageModel + ? (chat.lastMessage! as ChatTextMessageModel).text + : '📷 ' + '${translations.image}' + : '', + avatar: widget.options.groupAvatarBuilder( + (chat as GroupChatModel).title, + (chat as GroupChatModel).imageUrl, + 40.0, + ), + lastUsed: chat.lastUsed != null + ? _dateFormatter.format( + date: chat.lastUsed!, + ) + : null, + ), + ), ); } 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 9f27442..c0feb47 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 @@ -47,10 +47,11 @@ class _NewChatScreenState extends State { Widget build(BuildContext context) { var theme = Theme.of(context); return Scaffold( + backgroundColor: theme.colorScheme.surface, appBar: AppBar( iconTheme: theme.appBarTheme.iconTheme ?? const IconThemeData(color: Colors.white), - backgroundColor: theme.appBarTheme.backgroundColor ?? Colors.black, + backgroundColor: theme.appBarTheme.backgroundColor, title: _buildSearchField(), actions: [ _buildSearchIcon(), @@ -114,7 +115,8 @@ class _NewChatScreenState extends State { } else if (snapshot.hasData) { return _buildUserList(snapshot.data!); } else { - return Text(widget.translations.noUsersFound); + return widget.options + .noUsersPlaceholderBuilder(widget.translations, context); } }, ), @@ -128,44 +130,28 @@ class _NewChatScreenState extends State { 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, - ), + ? TextField( + focusNode: _textFieldFocusNode, + onChanged: (value) { + setState(() { + query = value; + }); + }, + decoration: InputDecoration( + hintText: widget.translations.searchPlaceholder, + hintStyle: theme.inputDecorationTheme.hintStyle, + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: theme.colorScheme.primary, ), ), - style: theme.inputDecorationTheme.hintStyle ?? - const TextStyle( - color: Colors.white, - ), - cursorColor: theme.textSelectionTheme.cursorColor ?? Colors.white, ), + style: theme.inputDecorationTheme.hintStyle, + cursorColor: theme.textSelectionTheme.cursorColor ?? Colors.white, ) : Text( widget.translations.newChatTitle, - style: theme.appBarTheme.titleTextStyle ?? - TextStyle( - fontWeight: FontWeight.w800, - fontSize: 24, - color: Theme.of(context).primaryColor, - ), + style: theme.appBarTheme.titleTextStyle, ); } @@ -191,6 +177,7 @@ class _NewChatScreenState extends State { } Widget _buildUserList(List users) { + var theme = Theme.of(context); var filteredUsers = users .where( (user) => @@ -204,60 +191,71 @@ class _NewChatScreenState extends State { if (_textFieldFocusNode.hasFocus && query.isEmpty) { return Padding( padding: const EdgeInsets.only(top: 20.0), - child: Center( - child: Text( - widget.translations.startTyping, - style: const TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.bold, - color: Colors.grey, - ), - ), + child: Text( + widget.translations.startTyping, + style: theme.textTheme.bodySmall, ), ); } if (filteredUsers.isEmpty) { - return widget.options.noChatsPlaceholderBuilder(widget.translations); + return widget.options + .noUsersPlaceholderBuilder(widget.translations, context); } - + var isPressed = false; return ListView.builder( itemCount: filteredUsers.length, itemBuilder: (context, index) { var user = filteredUsers[index]; - return GestureDetector( - child: widget.options.chatRowContainerBuilder( - Container( - color: Colors.transparent, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: Row( - children: [ - Padding( - padding: const EdgeInsets.only(left: 12.0, right: 12), - child: widget.options.userAvatarBuilder(user, 40.0), - ), - Expanded( - child: Container( - height: 40.0, - alignment: Alignment.centerLeft, - child: Text( - user.fullName ?? widget.translations.anonymousUser, - style: const TextStyle( - fontSize: 18.0, - fontWeight: FontWeight.w800, + return Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: theme.colorScheme.secondary.withOpacity(0.3), + width: 0.5, + ), + ), + ), + child: GestureDetector( + child: widget.options.chatRowContainerBuilder( + Padding( + padding: widget.options.paddingAroundChatList ?? + const EdgeInsets.symmetric(vertical: 8, horizontal: 28), + child: Container( + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: widget.options.userAvatarBuilder(user, 40.0), + ), + Expanded( + child: Container( + height: 40.0, + alignment: Alignment.centerLeft, + child: Text( + user.fullName ?? + widget.translations.anonymousUser, + style: theme.textTheme.bodyLarge, + ), ), ), - ), + ], ), - ], + ), ), ), ), + onTap: () async { + if (!isPressed) { + isPressed = true; + await widget.onPressCreateChat(user); + isPressed = false; + } + }, ), - onTap: () async { - await widget.onPressCreateChat(user); - }, ); }, ); 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 index 30fff15..d6dd7aa 100644 --- 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 @@ -33,34 +33,57 @@ class _NewGroupChatOverviewScreenState @override Widget build(BuildContext context) { var theme = Theme.of(context); + var formKey = GlobalKey(); + var isPressed = false; return Scaffold( + backgroundColor: theme.colorScheme.surface, 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), + backgroundColor: theme.appBarTheme.backgroundColor, + title: Text( + widget.translations.newGroupChatTitle, + style: theme.appBarTheme.titleTextStyle, ), ), body: Padding( padding: const EdgeInsets.all(16.0), - child: TextField( - controller: _textEditingController, - decoration: const InputDecoration( - hintText: 'Group chat name', + child: Form( + key: formKey, + child: TextFormField( + controller: _textEditingController, + decoration: InputDecoration( + hintText: widget.translations.groupNameHintText, + hintStyle: theme.inputDecorationTheme.hintStyle, + ), + validator: (value) { + if (value == null || value.isEmpty) { + return widget.translations.groupNameValidatorEmpty; + } + if (value.length > 15) + return widget.translations.groupNameValidatorTooLong; + return null; + }, ), ), ), floatingActionButton: FloatingActionButton( backgroundColor: Theme.of(context).primaryColor, onPressed: () async { - await widget.onPressCompleteGroupChatCreation( - widget.users, - _textEditingController.text, - ); + if (!isPressed) { + isPressed = true; + if (formKey.currentState!.validate()) { + await widget.onPressCompleteGroupChatCreation( + widget.users, + _textEditingController.text, + ); + } + isPressed = false; + } }, - child: const Icon(Icons.check_circle), + 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 index bca2bc3..f9b3ddb 100644 --- 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 @@ -32,10 +32,11 @@ class _NewGroupChatScreenState extends State { Widget build(BuildContext context) { var theme = Theme.of(context); return Scaffold( + backgroundColor: theme.colorScheme.surface, appBar: AppBar( iconTheme: theme.appBarTheme.iconTheme ?? const IconThemeData(color: Colors.white), - backgroundColor: theme.appBarTheme.backgroundColor ?? Colors.black, + backgroundColor: theme.appBarTheme.backgroundColor, title: _buildSearchField(), actions: [ _buildSearchIcon(), @@ -51,10 +52,8 @@ class _NewGroupChatScreenState extends State { return Text('Error: ${snapshot.error}'); } else if (snapshot.hasData) { return _buildUserList(snapshot.data!); - } else { - return widget.options - .noChatsPlaceholderBuilder(widget.translations); } + return const SizedBox.shrink(); }, ), floatingActionButton: FloatingActionButton( @@ -72,44 +71,28 @@ class _NewGroupChatScreenState extends State { 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, - ), + ? TextField( + focusNode: _textFieldFocusNode, + onChanged: (value) { + setState(() { + query = value; + }); + }, + decoration: InputDecoration( + hintText: widget.translations.searchPlaceholder, + hintStyle: theme.inputDecorationTheme.hintStyle, + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: theme.colorScheme.primary, ), ), - style: theme.inputDecorationTheme.hintStyle ?? - const TextStyle( - color: Colors.white, - ), - cursorColor: theme.textSelectionTheme.cursorColor ?? Colors.white, ), + style: theme.inputDecorationTheme.hintStyle, + cursorColor: theme.textSelectionTheme.cursorColor ?? Colors.white, ) : Text( widget.translations.newGroupChatButton, - style: theme.appBarTheme.titleTextStyle ?? - TextStyle( - fontWeight: FontWeight.w800, - fontSize: 24, - color: Theme.of(context).primaryColor, - ), + style: theme.appBarTheme.titleTextStyle, ); } @@ -146,65 +129,113 @@ class _NewGroupChatScreenState extends State { .toList(); if (filteredUsers.isEmpty) { - return widget.options.noChatsPlaceholderBuilder(widget.translations); + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + widget.options + .noUsersPlaceholderBuilder(widget.translations, context), + ], + ); } - return ListView.separated( - itemCount: filteredUsers.length, - separatorBuilder: (context, index) => const Padding( - padding: EdgeInsets.symmetric(horizontal: 28.0), - child: Divider(), - ), // Add Divider here - itemBuilder: (context, index) { - var user = filteredUsers[index]; - var isSelected = - selectedUserList.any((selectedUser) => selectedUser == user); - - return InkWell( - onTap: () { - setState(() { - if (selectedUserList.contains(user)) { - 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: Padding( - padding: - const EdgeInsets.symmetric(vertical: 12.0, horizontal: 30), - child: Row( - children: [ - Padding( - padding: const EdgeInsets.only(left: 12.0, right: 12), - child: widget.options.userAvatarBuilder(user, 40.0), - ), - Expanded( - child: Container( - height: 40.0, // Adjust the height as needed - alignment: Alignment.centerLeft, - child: Text( - user.fullName ?? widget.translations.anonymousUser, - style: const TextStyle( - fontWeight: FontWeight.w800, + return UserList( + filteredUsers: filteredUsers, + selectedUserList: selectedUserList, + options: widget.options, + translations: widget.translations, + ); + } +} + +class UserList extends StatefulWidget { + const UserList({ + required this.filteredUsers, + required this.selectedUserList, + required this.options, + required this.translations, + super.key, + }); + + final List filteredUsers; + final List selectedUserList; + final ChatOptions options; + final ChatTranslations translations; + + @override + State createState() => _UserListState(); +} + +class _UserListState extends State { + @override + Widget build(BuildContext context) => ListView.builder( + itemCount: widget.filteredUsers.length, + itemBuilder: (context, index) { + var user = widget.filteredUsers[index]; + var isSelected = widget.selectedUserList + .any((selectedUser) => selectedUser == user); + var theme = Theme.of(context); + return Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: theme.colorScheme.secondary.withOpacity(0.3), + width: 0.5, + ), + ), + ), + child: InkWell( + onTap: () { + setState(() { + if (widget.selectedUserList.contains(user)) { + widget.selectedUserList.remove(user); + } else { + widget.selectedUserList.add(user); + } + }); + }, + child: Padding( + padding: widget.options.paddingAroundChatList ?? + const EdgeInsets.fromLTRB(28, 8, 28, 8), + child: Container( + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 12.0, + horizontal: 30, + ), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: widget.options.userAvatarBuilder(user, 40.0), + ), + Expanded( + child: Container( + height: 40, + alignment: Alignment.centerLeft, + child: Text( + user.fullName ?? + widget.translations.anonymousUser, + style: theme.textTheme.bodyLarge, + ), + ), ), - ), + if (isSelected) ...[ + Padding( + padding: const EdgeInsets.only(right: 16.0), + child: Icon( + Icons.check_circle, + color: theme.colorScheme.primary, + ), + ), + ], + ], ), ), - 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 06de508..0c1b85d 100644 --- a/packages/flutter_chat_view/pubspec.yaml +++ b/packages/flutter_chat_view/pubspec.yaml @@ -4,7 +4,7 @@ name: flutter_chat_view description: A standard flutter package. -version: 2.0.0 +version: 3.0.0 publish_to: none @@ -20,7 +20,7 @@ dependencies: git: url: https://github.com/Iconica-Development/flutter_chat path: packages/flutter_chat_interface - ref: 2.0.0 + ref: 3.0.0 cached_network_image: ^3.2.2 flutter_image_picker: git: @@ -37,6 +37,6 @@ dev_dependencies: flutter_iconica_analysis: git: url: https://github.com/Iconica-Development/flutter_iconica_analysis - ref: 6.0.0 + ref: 7.0.0 flutter: