fix: feedback

This commit is contained in:
mike doornenbal 2024-06-05 16:53:42 +02:00 committed by Freek van de Ven
parent 5464766747
commit 1141aea83c
38 changed files with 702 additions and 625 deletions

View file

@ -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 ## 2.0.0
- Add a serviceBuilder to the userstory configuration - Add a serviceBuilder to the userstory configuration

View file

@ -1,28 +1,9 @@
# This file configures the analyzer, which statically analyzes Dart code to include: package:flutter_iconica_analysis/analysis_options.yaml
# 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`.
# The following line activates a set of recommended lints for Flutter apps, # Possible to overwrite the rules from the package
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml analyzer:
exclude:
linter: 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: 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

View file

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import "package:flutter/material.dart";
import 'package:flutter_chat/flutter_chat.dart'; import "package:flutter_chat/flutter_chat.dart";
void main(List<String> args) async { void main(List<String> args) async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
@ -11,25 +11,22 @@ class App extends StatelessWidget {
const App({super.key}); const App({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => const MaterialApp(
return const MaterialApp(
home: Home(), home: Home(),
); );
} }
}
class Home extends StatelessWidget { class Home extends StatelessWidget {
const Home({super.key}); const Home({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => Center(
return Center( child: chatNavigatorUserStory(
child: chatNavigatorUserStory(context, context,
configuration: ChatUserStoryConfiguration( configuration: ChatUserStoryConfiguration(
chatService: LocalChatService(), chatService: LocalChatService(),
chatOptionsBuilder: (ctx) => ChatOptions( chatOptionsBuilder: (ctx) => const ChatOptions(),
noChatsPlaceholderBuilder: (translations) => ),
Text(translations.noUsersFound), ),
)))); );
}
} }

View file

@ -16,11 +16,23 @@ dependencies:
path: ../ path: ../
flutter_chat_firebase: flutter_chat_firebase:
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: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter 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: flutter:
uses-material-design: true uses-material-design: true

View file

@ -5,10 +5,10 @@
// gestures. You can also use WidgetTester to find child widgets in the widget // 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. // 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() { void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async { testWidgets("Counter increments smoke test", (WidgetTester tester) async {
expect(true, true); expect(true, true);
}); });
} }

View file

@ -284,7 +284,7 @@ Widget _newGroupChatOverviewScreenRoute(
), ),
); );
if (context.mounted) { if (context.mounted) {
await Navigator.of(context).push( await Navigator.of(context).pushReplacement(
MaterialPageRoute( MaterialPageRoute(
builder: (context) => _chatDetailScreenRoute( builder: (context) => _chatDetailScreenRoute(
configuration, configuration,

View file

@ -14,6 +14,7 @@ List<GoRoute> getChatStoryRoutes(
GoRoute( GoRoute(
path: ChatUserStoryRoutes.chatScreen, path: ChatUserStoryRoutes.chatScreen,
pageBuilder: (context, state) { pageBuilder: (context, state) {
var theme = Theme.of(context);
var service = configuration.chatServiceBuilder?.call(context) ?? var service = configuration.chatServiceBuilder?.call(context) ??
configuration.chatService; configuration.chatService;
var chatScreen = ChatScreen( var chatScreen = ChatScreen(
@ -47,6 +48,7 @@ List<GoRoute> getChatStoryRoutes(
chatScreen, chatScreen,
) ?? ) ??
Scaffold( Scaffold(
backgroundColor: theme.colorScheme.surface,
body: chatScreen, body: chatScreen,
), ),
); );
@ -58,6 +60,8 @@ List<GoRoute> getChatStoryRoutes(
var chatId = state.pathParameters['id']; var chatId = state.pathParameters['id'];
var service = configuration.chatServiceBuilder?.call(context) ?? var service = configuration.chatServiceBuilder?.call(context) ??
configuration.chatService; configuration.chatService;
var theme = Theme.of(context);
var chatDetailScreen = ChatDetailScreen( var chatDetailScreen = ChatDetailScreen(
chatTitleBuilder: configuration.chatTitleBuilder, chatTitleBuilder: configuration.chatTitleBuilder,
usernameBuilder: configuration.usernameBuilder, usernameBuilder: configuration.usernameBuilder,
@ -118,6 +122,7 @@ List<GoRoute> getChatStoryRoutes(
chatDetailScreen, chatDetailScreen,
) ?? ) ??
Scaffold( Scaffold(
backgroundColor: theme.colorScheme.surface,
body: chatDetailScreen, body: chatDetailScreen,
), ),
); );
@ -128,6 +133,8 @@ List<GoRoute> getChatStoryRoutes(
pageBuilder: (context, state) { pageBuilder: (context, state) {
var service = configuration.chatServiceBuilder?.call(context) ?? var service = configuration.chatServiceBuilder?.call(context) ??
configuration.chatService; configuration.chatService;
var theme = Theme.of(context);
var newChatScreen = NewChatScreen( var newChatScreen = NewChatScreen(
options: configuration.chatOptionsBuilder(context), options: configuration.chatOptionsBuilder(context),
translations: configuration.translationsBuilder?.call(context) ?? translations: configuration.translationsBuilder?.call(context) ??
@ -165,6 +172,7 @@ List<GoRoute> getChatStoryRoutes(
newChatScreen, newChatScreen,
) ?? ) ??
Scaffold( Scaffold(
backgroundColor: theme.colorScheme.surface,
body: newChatScreen, body: newChatScreen,
), ),
); );
@ -175,6 +183,8 @@ List<GoRoute> getChatStoryRoutes(
pageBuilder: (context, state) { pageBuilder: (context, state) {
var service = configuration.chatServiceBuilder?.call(context) ?? var service = configuration.chatServiceBuilder?.call(context) ??
configuration.chatService; configuration.chatService;
var theme = Theme.of(context);
var newGroupChatScreen = NewGroupChatScreen( var newGroupChatScreen = NewGroupChatScreen(
options: configuration.chatOptionsBuilder(context), options: configuration.chatOptionsBuilder(context),
translations: configuration.translationsBuilder?.call(context) ?? translations: configuration.translationsBuilder?.call(context) ??
@ -193,6 +203,7 @@ List<GoRoute> getChatStoryRoutes(
newGroupChatScreen, newGroupChatScreen,
) ?? ) ??
Scaffold( Scaffold(
backgroundColor: theme.colorScheme.surface,
body: newGroupChatScreen, body: newGroupChatScreen,
), ),
); );
@ -204,6 +215,8 @@ List<GoRoute> getChatStoryRoutes(
var service = configuration.chatServiceBuilder?.call(context) ?? var service = configuration.chatServiceBuilder?.call(context) ??
configuration.chatService; configuration.chatService;
var users = state.extra! as List<ChatUserModel>; var users = state.extra! as List<ChatUserModel>;
var theme = Theme.of(context);
var newGroupChatOverviewScreen = NewGroupChatOverviewScreen( var newGroupChatOverviewScreen = NewGroupChatOverviewScreen(
options: configuration.chatOptionsBuilder(context), options: configuration.chatOptionsBuilder(context),
translations: configuration.translationsBuilder?.call(context) ?? translations: configuration.translationsBuilder?.call(context) ??
@ -223,7 +236,7 @@ List<GoRoute> getChatStoryRoutes(
), ),
); );
if (context.mounted) { if (context.mounted) {
await context.push( context.go(
ChatUserStoryRoutes.chatDetailViewPath(chat.id ?? ''), ChatUserStoryRoutes.chatDetailViewPath(chat.id ?? ''),
); );
} }
@ -237,6 +250,7 @@ List<GoRoute> getChatStoryRoutes(
newGroupChatOverviewScreen, newGroupChatOverviewScreen,
) ?? ) ??
Scaffold( Scaffold(
backgroundColor: theme.colorScheme.surface,
body: newGroupChatOverviewScreen, body: newGroupChatOverviewScreen,
), ),
); );
@ -250,6 +264,8 @@ List<GoRoute> getChatStoryRoutes(
var id = userId == 'null' ? null : userId; var id = userId == 'null' ? null : userId;
var service = configuration.chatServiceBuilder?.call(context) ?? var service = configuration.chatServiceBuilder?.call(context) ??
configuration.chatService; configuration.chatService;
var theme = Theme.of(context);
var profileScreen = ChatProfileScreen( var profileScreen = ChatProfileScreen(
translations: configuration.translationsBuilder?.call(context) ?? translations: configuration.translationsBuilder?.call(context) ??
configuration.translations, configuration.translations,
@ -274,6 +290,7 @@ List<GoRoute> getChatStoryRoutes(
profileScreen, profileScreen,
) ?? ) ??
Scaffold( Scaffold(
backgroundColor: theme.colorScheme.surface,
body: profileScreen, body: profileScreen,
), ),
); );

View file

@ -4,7 +4,7 @@
name: flutter_chat name: flutter_chat
description: A new Flutter package project. description: A new Flutter package project.
version: 2.0.0 version: 3.0.0
publish_to: none publish_to: none
@ -20,23 +20,23 @@ dependencies:
git: git:
url: https://github.com/Iconica-Development/flutter_chat url: https://github.com/Iconica-Development/flutter_chat
path: packages/flutter_chat_view path: packages/flutter_chat_view
ref: 2.0.0 ref: 3.0.0
flutter_chat_interface: flutter_chat_interface:
git: git:
url: https://github.com/Iconica-Development/flutter_chat url: https://github.com/Iconica-Development/flutter_chat
path: packages/flutter_chat_interface path: packages/flutter_chat_interface
ref: 2.0.0 ref: 3.0.0
flutter_chat_local: flutter_chat_local:
git: git:
url: https://github.com/Iconica-Development/flutter_chat url: https://github.com/Iconica-Development/flutter_chat
path: packages/flutter_chat_local path: packages/flutter_chat_local
ref: 2.0.0 ref: 3.0.0
uuid: ^4.3.3 uuid: ^4.3.3
dev_dependencies: dev_dependencies:
flutter_iconica_analysis: flutter_iconica_analysis:
git: git:
url: https://github.com/Iconica-Development/flutter_iconica_analysis url: https://github.com/Iconica-Development/flutter_iconica_analysis
ref: 6.0.0 ref: 7.0.0
flutter: flutter:

View file

@ -4,7 +4,7 @@
name: flutter_chat_firebase name: flutter_chat_firebase
description: A new Flutter package project. description: A new Flutter package project.
version: 2.0.0 version: 3.0.0
publish_to: none publish_to: none
environment: environment:
@ -23,12 +23,12 @@ dependencies:
git: git:
url: https://github.com/Iconica-Development/flutter_chat url: https://github.com/Iconica-Development/flutter_chat
path: packages/flutter_chat_interface path: packages/flutter_chat_interface
ref: 2.0.0 ref: 3.0.0
dev_dependencies: dev_dependencies:
flutter_iconica_analysis: flutter_iconica_analysis:
git: git:
url: https://github.com/Iconica-Development/flutter_iconica_analysis url: https://github.com/Iconica-Development/flutter_iconica_analysis
ref: 6.0.0 ref: 7.0.0
flutter: flutter:

View file

@ -4,6 +4,6 @@
/// ///
library flutter_chat_interface; library flutter_chat_interface;
export 'package:flutter_chat_interface/src/chat_data_provider.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/model/model.dart";
export 'package:flutter_chat_interface/src/service/service.dart'; export "package:flutter_chat_interface/src/service/service.dart";

View file

@ -2,8 +2,8 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter_chat_interface/flutter_chat_interface.dart'; import "package:flutter_chat_interface/flutter_chat_interface.dart";
import 'package:flutter_data_interface/flutter_data_interface.dart'; import "package:flutter_data_interface/flutter_data_interface.dart";
class ChatDataProvider extends DataInterface { class ChatDataProvider extends DataInterface {
ChatDataProvider({ ChatDataProvider({

View file

@ -3,7 +3,7 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // 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 { abstract class ChatModelInterface {
ChatModelInterface copyWith(); ChatModelInterface copyWith();

View file

@ -3,7 +3,7 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // 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. /// An abstract class defining the interface for an image message in a chat.
abstract class ChatImageMessageModelInterface extends ChatMessageModel { abstract class ChatImageMessageModelInterface extends ChatMessageModel {

View file

@ -3,7 +3,7 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // 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 { abstract class ChatMessageModelInterface {
ChatUserModel get sender; ChatUserModel get sender;

View file

@ -3,7 +3,7 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // 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 { abstract class ChatTextMessageModelInterface extends ChatMessageModel {
ChatTextMessageModelInterface({ ChatTextMessageModelInterface({

View file

@ -3,7 +3,7 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart'; import "package:flutter/material.dart";
abstract class ChatUserModelInterface { abstract class ChatUserModelInterface {
String? get id; String? get id;
@ -49,17 +49,17 @@ class ChatUserModel implements ChatUserModelInterface {
@override @override
String? get fullName { String? get fullName {
var fullName = ''; var fullName = "";
if (firstName != null && lastName != null) { if (firstName != null && lastName != null) {
fullName += '$firstName $lastName'; fullName += "$firstName $lastName";
} else if (firstName != null) { } else if (firstName != null) {
fullName += firstName!; fullName += firstName!;
} else if (lastName != null) { } else if (lastName != null) {
fullName += lastName!; fullName += lastName!;
} }
return fullName == '' ? null : fullName; return fullName == "" ? null : fullName;
} }
@override @override

View file

@ -3,7 +3,7 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // 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 { abstract class GroupChatModelInterface extends ChatModel {
GroupChatModelInterface({ GroupChatModelInterface({

View file

@ -1,7 +1,7 @@
export 'chat.dart'; export "chat.dart";
export 'chat_image_message.dart'; export "chat_image_message.dart";
export 'chat_message.dart'; export "chat_message.dart";
export 'chat_text_message.dart'; export "chat_text_message.dart";
export 'chat_user.dart'; export "chat_user.dart";
export 'group_chat.dart'; export "group_chat.dart";
export 'personal_chat.dart'; export "personal_chat.dart";

View file

@ -3,7 +3,7 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // 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 { abstract class PersonalChatModelInterface extends ChatModel {
PersonalChatModelInterface({ PersonalChatModelInterface({

View file

@ -1,6 +1,6 @@
import 'dart:typed_data'; import "dart:typed_data";
import 'package:flutter/material.dart'; import "package:flutter/material.dart";
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 a chat detail service. /// An abstract class defining the interface for a chat detail service.
abstract class ChatDetailService with ChangeNotifier { abstract class ChatDetailService with ChangeNotifier {

View file

@ -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 { abstract class ChatOverviewService {
/// Retrieves a stream of chats. /// Retrieves a stream of chats.

View file

@ -1,5 +1,5 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first // 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 { class ChatService {
final ChatUserService chatUserService; final ChatUserService chatUserService;

View file

@ -1,4 +1,4 @@
export 'chat_detail_service.dart'; export "chat_detail_service.dart";
export 'chat_overview_service.dart'; export "chat_overview_service.dart";
export 'chat_service.dart'; export "chat_service.dart";
export 'user_service.dart'; export "user_service.dart";

View file

@ -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 { abstract class ChatUserService {
/// Retrieves a user based on the ID. /// Retrieves a user based on the ID.

View file

@ -4,7 +4,7 @@
name: flutter_chat_interface name: flutter_chat_interface
description: A new Flutter package project. description: A new Flutter package project.
version: 2.0.0 version: 3.0.0
publish_to: none publish_to: none
environment: environment:
@ -23,6 +23,6 @@ dev_dependencies:
flutter_iconica_analysis: flutter_iconica_analysis:
git: git:
url: https://github.com/Iconica-Development/flutter_iconica_analysis url: https://github.com/Iconica-Development/flutter_iconica_analysis
ref: 6.0.0 ref: 7.0.0
flutter: flutter:

View file

@ -1,6 +1,6 @@
name: flutter_chat_local name: flutter_chat_local
description: "A new Flutter package project." description: "A new Flutter package project."
version: 2.0.0 version: 3.0.0
publish_to: none publish_to: none
environment: environment:
@ -14,7 +14,7 @@ dependencies:
git: git:
url: https://github.com/Iconica-Development/flutter_chat url: https://github.com/Iconica-Development/flutter_chat
path: packages/flutter_chat_interface path: packages/flutter_chat_interface
ref: 2.0.0 ref: 3.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -22,5 +22,5 @@ dev_dependencies:
flutter_iconica_analysis: flutter_iconica_analysis:
git: git:
url: https://github.com/Iconica-Development/flutter_iconica_analysis url: https://github.com/Iconica-Development/flutter_iconica_analysis
ref: 6.0.0 ref: 7.0.0
flutter: flutter:

View file

@ -73,7 +73,7 @@ class _ChatBottomState extends State<ChatBottom> {
IconButton( IconButton(
onPressed: widget.onPressSelectImage, onPressed: widget.onPressSelectImage,
icon: Icon( icon: Icon(
Icons.image, Icons.image_outlined,
color: widget.iconColor, color: widget.iconColor,
), ),
), ),
@ -105,6 +105,7 @@ class _ChatBottomState extends State<ChatBottom> {
], ],
), ),
widget.translations, widget.translations,
context,
), ),
), ),
); );

View file

@ -54,7 +54,7 @@ class _ChatDetailRowState extends State<ChatDetailRow> {
widget.message.timestamp.minute == widget.message.timestamp.minute ==
widget.previousMessage?.timestamp.minute; widget.previousMessage?.timestamp.minute;
var hasHeader = isNewDate || isSameSender; var hasHeader = isNewDate || isSameSender;
var theme = Theme.of(context);
return Padding( return Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
top: isNewDate || isSameSender ? 25.0 : 0, top: isNewDate || isSameSender ? 25.0 : 0,
@ -160,10 +160,7 @@ class _ChatDetailRowState extends State<ChatDetailRow> {
) )
.split(' ') .split(' ')
.last, .last,
style: const TextStyle( style: theme.textTheme.bodySmall,
fontSize: 12,
color: Color(0xFFBBBBBB),
),
textAlign: TextAlign.end, textAlign: TextAlign.end,
), ),
], ],

View file

@ -30,7 +30,9 @@ class ChatRow extends StatelessWidget {
final Widget? avatar; final Widget? avatar;
@override @override
Widget build(BuildContext context) => Row( Widget build(BuildContext context) {
var theme = Theme.of(context);
return Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Padding( Padding(
@ -47,29 +49,24 @@ class ChatRow extends StatelessWidget {
title, title,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle( style: unreadMessages > 0
fontSize: 16, ? theme.textTheme.bodyLarge
fontWeight: unreadMessages > 0 : theme.textTheme.bodyMedium,
? FontWeight.w900
: FontWeight.w500,
), ),
), if (subTitle != null) ...[
if (subTitle != null)
Padding( Padding(
padding: const EdgeInsets.only(top: 3.0), padding: const EdgeInsets.only(top: 3.0),
child: Text( child: Text(
subTitle!, subTitle!,
style: TextStyle( style: unreadMessages > 0
fontSize: 16, ? theme.textTheme.bodyLarge
fontWeight: unreadMessages > 0 : theme.textTheme.bodyMedium,
? FontWeight.w500
: FontWeight.w300,
),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 2, maxLines: 2,
), ),
), ),
], ],
],
), ),
), ),
), ),
@ -77,7 +74,7 @@ class ChatRow extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
if (lastUsed != null) // Check if lastUsed is not null if (lastUsed != null) ...[
Padding( Padding(
padding: const EdgeInsets.only(bottom: 4.0), padding: const EdgeInsets.only(bottom: 4.0),
child: Text( child: Text(
@ -88,6 +85,7 @@ class ChatRow extends StatelessWidget {
), ),
), ),
), ),
],
if (unreadMessages > 0) ...[ if (unreadMessages > 0) ...[
Container( Container(
width: 20, width: 20,
@ -111,3 +109,4 @@ class ChatRow extends StatelessWidget {
], ],
); );
} }
}

View file

@ -59,7 +59,10 @@ Widget _createNewChatButton(
ChatTranslations translations, ChatTranslations translations,
) => ) =>
Padding( Padding(
padding: const EdgeInsets.fromLTRB(5, 24, 5, 24), padding: const EdgeInsets.symmetric(
vertical: 24,
horizontal: 5,
),
child: ElevatedButton( child: ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor, backgroundColor: Theme.of(context).primaryColor,
@ -74,7 +77,7 @@ Widget _createNewChatButton(
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: Colors.white,
fontWeight: FontWeight.w800, fontWeight: FontWeight.w800,
fontSize: 18, fontSize: 20,
), ),
), ),
), ),
@ -84,19 +87,21 @@ Widget _createMessageInput(
TextEditingController textEditingController, TextEditingController textEditingController,
Widget suffixIcon, Widget suffixIcon,
ChatTranslations translations, ChatTranslations translations,
) => BuildContext context,
TextField( ) {
var theme = Theme.of(context);
return TextField(
textCapitalization: TextCapitalization.sentences, textCapitalization: TextCapitalization.sentences,
controller: textEditingController, controller: textEditingController,
decoration: InputDecoration( decoration: InputDecoration(
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(26.5), borderRadius: BorderRadius.circular(25),
borderSide: const BorderSide( borderSide: const BorderSide(
color: Colors.black, color: Colors.black,
), ),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(26.5), borderRadius: BorderRadius.circular(25),
borderSide: const BorderSide( borderSide: const BorderSide(
color: Colors.black, color: Colors.black,
), ),
@ -106,21 +111,19 @@ Widget _createMessageInput(
horizontal: 30, horizontal: 30,
), ),
hintText: translations.messagePlaceholder, hintText: translations.messagePlaceholder,
hintStyle: const TextStyle( hintStyle: theme.inputDecorationTheme.hintStyle,
fontWeight: FontWeight.normal,
color: Colors.black,
),
fillColor: Colors.white, fillColor: Colors.white,
filled: true, filled: true,
border: const OutlineInputBorder( border: const OutlineInputBorder(
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(26.5), Radius.circular(25),
), ),
borderSide: BorderSide.none, borderSide: BorderSide.none,
), ),
suffixIcon: suffixIcon, suffixIcon: suffixIcon,
), ),
); );
}
Widget _createChatRowContainer( Widget _createChatRowContainer(
Widget chatRow, Widget chatRow,
@ -130,7 +133,10 @@ Widget _createChatRowContainer(
vertical: 12.0, vertical: 12.0,
horizontal: 10.0, horizontal: 10.0,
), ),
child: Container(
color: Colors.transparent,
child: chatRow, child: chatRow,
),
); );
Widget _createImagePickerContainer( Widget _createImagePickerContainer(
@ -166,6 +172,7 @@ Widget _createImagePickerContainer(
Scaffold _createScaffold( Scaffold _createScaffold(
AppBar appbar, AppBar appbar,
Widget body, Widget body,
Color backgroundColor,
) => ) =>
Scaffold( Scaffold(
appBar: appbar, appBar: appbar,
@ -196,31 +203,32 @@ Widget _createGroupAvatar(
Widget _createNoChatsPlaceholder( Widget _createNoChatsPlaceholder(
ChatTranslations translations, ChatTranslations translations,
) => BuildContext context,
Center( ) {
var theme = Theme.of(context);
return Center(
child: Text( child: Text(
translations.noChatsFound, translations.noChatsFound,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle( style: theme.textTheme.bodySmall,
color: Colors.white,
fontSize: 18,
),
), ),
); );
}
Widget _createNoUsersPlaceholder( Widget _createNoUsersPlaceholder(
ChatTranslations translations, ChatTranslations translations,
) => BuildContext context,
Center( ) {
var theme = Theme.of(context);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Text( child: Text(
translations.noUsersFound, translations.noUsersFound,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle( style: theme.textTheme.bodySmall,
color: Colors.white,
fontSize: 18,
),
), ),
); );
}
typedef ButtonBuilder = Widget Function( typedef ButtonBuilder = Widget Function(
BuildContext context, BuildContext context,
@ -232,6 +240,7 @@ typedef TextInputBuilder = Widget Function(
TextEditingController textEditingController, TextEditingController textEditingController,
Widget suffixIcon, Widget suffixIcon,
ChatTranslations translations, ChatTranslations translations,
BuildContext context,
); );
typedef ContainerBuilder = Widget Function( typedef ContainerBuilder = Widget Function(
@ -247,6 +256,7 @@ typedef ImagePickerContainerBuilder = Widget Function(
typedef ScaffoldBuilder = Scaffold Function( typedef ScaffoldBuilder = Scaffold Function(
AppBar appBar, AppBar appBar,
Widget body, Widget body,
Color backgroundColor,
); );
typedef UserAvatarBuilder = Widget Function( typedef UserAvatarBuilder = Widget Function(
@ -262,8 +272,10 @@ typedef GroupAvatarBuilder = Widget Function(
typedef NoChatsPlaceholderBuilder = Widget Function( typedef NoChatsPlaceholderBuilder = Widget Function(
ChatTranslations translations, ChatTranslations translations,
BuildContext context,
); );
typedef NoUsersPlaceholderBuilder = Widget Function( typedef NoUsersPlaceholderBuilder = Widget Function(
ChatTranslations translations, ChatTranslations translations,
BuildContext context,
); );

View file

@ -36,13 +36,17 @@ class ChatTranslations {
required this.uploadFile, required this.uploadFile,
required this.takePicture, required this.takePicture,
required this.anonymousUser, required this.anonymousUser,
required this.groupNameValidatorEmpty,
required this.groupNameValidatorTooLong,
required this.groupNameHintText,
required this.newGroupChatTitle,
}); });
/// Default translations for the chat component view /// Default translations for the chat component view
const ChatTranslations.empty({ const ChatTranslations.empty({
this.chatsTitle = 'Chats', this.chatsTitle = 'Chats',
this.chatsUnread = 'unread', this.chatsUnread = 'unread',
this.newChatButton = 'Start a chat', this.newChatButton = 'Start chat',
this.newGroupChatButton = 'Create a group chat', this.newGroupChatButton = 'Create a group chat',
this.newChatTitle = 'Start a chat', this.newChatTitle = 'Start a chat',
this.image = 'Image', this.image = 'Image',
@ -68,6 +72,11 @@ class ChatTranslations {
this.imagePickerTitle = 'Do you want to upload a file or take a picture?', this.imagePickerTitle = 'Do you want to upload a file or take a picture?',
this.uploadFile = 'UPLOAD FILE', this.uploadFile = 'UPLOAD FILE',
this.takePicture = 'TAKE PICTURE', 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; final String chatsTitle;
@ -98,6 +107,10 @@ class ChatTranslations {
/// Shown when the user has no name /// Shown when the user has no name
final String anonymousUser; final String anonymousUser;
final String groupNameValidatorEmpty;
final String groupNameValidatorTooLong;
final String groupNameHintText;
final String newGroupChatTitle;
// copyWith method to override the default values // copyWith method to override the default values
ChatTranslations copyWith({ ChatTranslations copyWith({
@ -127,6 +140,10 @@ class ChatTranslations {
String? uploadFile, String? uploadFile,
String? takePicture, String? takePicture,
String? anonymousUser, String? anonymousUser,
String? groupNameValidatorEmpty,
String? groupNameValidatorTooLong,
String? groupNameHintText,
String? newGroupChatTitle,
}) => }) =>
ChatTranslations( ChatTranslations(
chatsTitle: chatsTitle ?? this.chatsTitle, chatsTitle: chatsTitle ?? this.chatsTitle,
@ -160,5 +177,9 @@ class ChatTranslations {
uploadFile: uploadFile ?? this.uploadFile, uploadFile: uploadFile ?? this.uploadFile,
takePicture: takePicture ?? this.takePicture, takePicture: takePicture ?? this.takePicture,
anonymousUser: anonymousUser ?? this.anonymousUser, anonymousUser: anonymousUser ?? this.anonymousUser,
groupNameValidatorEmpty: this.groupNameValidatorEmpty,
groupNameValidatorTooLong: this.groupNameValidatorTooLong,
groupNameHintText: this.groupNameHintText,
newGroupChatTitle: this.newGroupChatTitle,
); );
} }

View file

@ -168,8 +168,9 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
builder: (context, AsyncSnapshot<ChatModel> snapshot) { builder: (context, AsyncSnapshot<ChatModel> snapshot) {
var chatModel = snapshot.data; var chatModel = snapshot.data;
return Scaffold( return Scaffold(
backgroundColor: theme.colorScheme.surface,
appBar: AppBar( appBar: AppBar(
backgroundColor: theme.appBarTheme.backgroundColor ?? Colors.black, backgroundColor: theme.appBarTheme.backgroundColor,
iconTheme: theme.appBarTheme.iconTheme ?? iconTheme: theme.appBarTheme.iconTheme ??
const IconThemeData(color: Colors.white), const IconThemeData(color: Colors.white),
centerTitle: true, centerTitle: true,
@ -188,14 +189,6 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
children: chat == null children: chat == null
? [] ? []
: [ : [
if (chatModel is GroupChatModel) ...[
widget.options.groupAvatarBuilder(
chatModel.title,
chatModel.imageUrl,
36.0,
),
] else
...[],
Padding( Padding(
padding: (chatModel is GroupChatModel) padding: (chatModel is GroupChatModel)
? const EdgeInsets.only(left: 15.5) ? const EdgeInsets.only(left: 15.5)
@ -216,12 +209,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
? chatModel.user.firstName ?? ? chatModel.user.firstName ??
widget.translations.anonymousUser widget.translations.anonymousUser
: '', : '',
style: theme.appBarTheme.titleTextStyle ?? style: theme.appBarTheme.titleTextStyle,
TextStyle(
fontWeight: FontWeight.w800,
fontSize: 24,
color: Theme.of(context).primaryColor,
),
), ),
), ),
], ],
@ -271,11 +259,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
.writeFirstMessageInGroupChat .writeFirstMessageInGroupChat
: widget : widget
.translations.writeMessageToStartChat, .translations.writeMessageToStartChat,
style: const TextStyle( style: theme.textTheme.bodySmall,
fontSize: 14.0,
fontWeight: FontWeight.w400,
color: Color.fromRGBO(33, 33, 33, 1),
),
), ),
), ),
...detailRows, ...detailRows,

View file

@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_chat_view/flutter_chat_view.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'; import 'package:flutter_profile/flutter_profile.dart';
class ChatProfileScreen extends StatefulWidget { class ChatProfileScreen extends StatefulWidget {
@ -35,7 +34,6 @@ class ChatProfileScreen extends StatefulWidget {
class _ProfileScreenState extends State<ChatProfileScreen> { class _ProfileScreenState extends State<ChatProfileScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
var hasUser = widget.userId == null; var hasUser = widget.userId == null;
var theme = Theme.of(context); var theme = Theme.of(context);
return FutureBuilder<dynamic>( return FutureBuilder<dynamic>(
@ -67,10 +65,10 @@ class _ProfileScreenState extends State<ChatProfileScreen> {
imageUrl: data.imageUrl, imageUrl: data.imageUrl,
); );
} }
return Scaffold( return Scaffold(
backgroundColor: theme.colorScheme.surface,
appBar: AppBar( appBar: AppBar(
backgroundColor: theme.appBarTheme.backgroundColor ?? Colors.black, backgroundColor: theme.appBarTheme.backgroundColor,
iconTheme: theme.appBarTheme.iconTheme ?? iconTheme: theme.appBarTheme.iconTheme ??
const IconThemeData(color: Colors.white), const IconThemeData(color: Colors.white),
title: Text( title: Text(
@ -81,32 +79,28 @@ class _ProfileScreenState extends State<ChatProfileScreen> {
: (data is GroupChatModel) : (data is GroupChatModel)
? data.title ? data.title
: '', : '',
style: theme.appBarTheme.titleTextStyle ?? style: theme.appBarTheme.titleTextStyle,
const TextStyle(
color: Colors.white,
),
), ),
), ),
body: snapshot.hasData body: snapshot.hasData
? ListView( ? ListView(
children: [ children: [
const SizedBox( Padding(
height: 10, padding: const EdgeInsets.symmetric(vertical: 20),
child: Avatar(
user: user,
), ),
SizedBox(
height: 200,
width: size.width,
child: ProfilePage(
user: user!,
itemBuilderOptions: ItemBuilderOptions(
readOnly: true,
),
service: ChatProfileService(),
), ),
const Divider(
color: Colors.white,
thickness: 10,
), ),
if (data is GroupChatModel) ...[ if (data is GroupChatModel) ...[
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 100), padding: const EdgeInsets.symmetric(
horizontal: 100,
vertical: 20,
),
child: Text( child: Text(
widget.translations.chatProfileUsers, widget.translations.chatProfileUsers,
style: const TextStyle( style: const TextStyle(

View file

@ -77,16 +77,10 @@ class _ChatScreenState extends State<ChatScreen> {
var theme = Theme.of(context); var theme = Theme.of(context);
return widget.options.scaffoldBuilder( return widget.options.scaffoldBuilder(
AppBar( AppBar(
backgroundColor: backgroundColor: theme.appBarTheme.backgroundColor,
theme.appBarTheme.backgroundColor ?? const Color(0xff212121),
title: Text( title: Text(
translations.chatsTitle, translations.chatsTitle,
style: theme.appBarTheme.titleTextStyle ?? style: theme.appBarTheme.titleTextStyle,
TextStyle(
fontWeight: FontWeight.w800,
fontSize: 24,
color: Theme.of(context).primaryColor,
),
), ),
centerTitle: true, centerTitle: true,
actions: [ actions: [
@ -120,7 +114,7 @@ class _ChatScreenState extends State<ChatScreen> {
controller: controller, controller: controller,
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
padding: widget.options.paddingAroundChatList ?? padding: widget.options.paddingAroundChatList ??
const EdgeInsets.fromLTRB(28, 16, 28, 0), const EdgeInsets.symmetric(vertical: 16, horizontal: 28),
children: [ children: [
StreamBuilder<List<ChatModel>>( StreamBuilder<List<ChatModel>>(
stream: widget.service.chatOverviewService.getChatsStream(), stream: widget.service.chatOverviewService.getChatsStream(),
@ -138,22 +132,30 @@ class _ChatScreenState extends State<ChatScreen> {
return Center( return Center(
child: Text( child: Text(
translations.noChatsFound, translations.noChatsFound,
style: const TextStyle( style: theme.textTheme.bodySmall,
fontSize: 14,
fontWeight: FontWeight.w400,
),
), ),
); );
} else { } else {
_hasCalledOnNoChats = _hasCalledOnNoChats = false;
false; // Reset the flag if there are chats
} }
return Column( return Column(
children: [ children: [
for (ChatModel chat in (snapshot.data ?? []).where( for (ChatModel chat in (snapshot.data ?? []).where(
(chat) => !deletedChats.contains(chat.id), (chat) => !deletedChats.contains(chat.id),
)) ...[ )) ...[
Builder( Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(0.3),
width: 0.5,
),
),
),
child: Builder(
builder: (context) => !(widget builder: (context) => !(widget
.disableDismissForPermanentChats && .disableDismissForPermanentChats &&
!chat.canBeDeleted) !chat.canBeDeleted)
@ -165,7 +167,8 @@ class _ChatScreenState extends State<ChatScreen> {
context: context, context: context,
builder: (BuildContext context) => builder: (BuildContext context) =>
Container( Container(
padding: const EdgeInsets.all(16.0), padding:
const EdgeInsets.all(16.0),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: crossAxisAlignment:
@ -180,7 +183,8 @@ class _ChatScreenState extends State<ChatScreen> {
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle( style: const TextStyle(
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.bold, fontWeight:
FontWeight.bold,
), ),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
@ -205,7 +209,8 @@ class _ChatScreenState extends State<ChatScreen> {
), ),
Row( Row(
mainAxisAlignment: mainAxisAlignment:
MainAxisAlignment.center, MainAxisAlignment
.center,
children: [ children: [
ElevatedButton( ElevatedButton(
onPressed: () => onPressed: () =>
@ -215,7 +220,8 @@ class _ChatScreenState extends State<ChatScreen> {
child: Text( child: Text(
translations translations
.deleteChatModalCancel, .deleteChatModalCancel,
style: const TextStyle( style:
const TextStyle(
color: Colors.black, color: Colors.black,
fontSize: 18, fontSize: 18,
), ),
@ -230,8 +236,9 @@ class _ChatScreenState extends State<ChatScreen> {
style: ElevatedButton style: ElevatedButton
.styleFrom( .styleFrom(
backgroundColor: backgroundColor:
Theme.of(context) Theme.of(
.primaryColor, context,
).primaryColor,
), ),
onPressed: () => onPressed: () =>
Navigator.of( Navigator.of(
@ -244,7 +251,8 @@ class _ChatScreenState extends State<ChatScreen> {
.deleteChatModalConfirm, .deleteChatModalConfirm,
style: style:
const TextStyle( const TextStyle(
color: Colors.white, color:
Colors.white,
fontSize: 18, fontSize: 18,
), ),
), ),
@ -290,6 +298,7 @@ class _ChatScreenState extends State<ChatScreen> {
dateFormatter: _dateFormatter, dateFormatter: _dateFormatter,
), ),
), ),
),
], ],
], ],
); );
@ -308,6 +317,7 @@ class _ChatScreenState extends State<ChatScreen> {
), ),
], ],
), ),
theme.colorScheme.surface,
); );
} }
} }
@ -327,12 +337,10 @@ class ChatListItem extends StatelessWidget {
final DateFormatter _dateFormatter; final DateFormatter _dateFormatter;
@override @override
Widget build(BuildContext context) => Column( Widget build(BuildContext context) => GestureDetector(
children: [ onTap: () {
GestureDetector( widget.onPressChat(chat);
onTap: () => widget.onPressChat(chat), },
child: Container(
color: Colors.transparent,
child: widget.options.chatRowContainerBuilder( child: widget.options.chatRowContainerBuilder(
(chat is PersonalChatModel) (chat is PersonalChatModel)
? ChatRow( ? ChatRow(
@ -345,8 +353,7 @@ class ChatListItem extends StatelessWidget {
translations.anonymousUser, translations.anonymousUser,
subTitle: chat.lastMessage != null subTitle: chat.lastMessage != null
? chat.lastMessage is ChatTextMessageModel ? chat.lastMessage is ChatTextMessageModel
? (chat.lastMessage! as ChatTextMessageModel) ? (chat.lastMessage! as ChatTextMessageModel).text
.text
: '📷 ' : '📷 '
'${translations.image}' '${translations.image}'
: '', : '',
@ -361,8 +368,7 @@ class ChatListItem extends StatelessWidget {
unreadMessages: chat.unreadMessages ?? 0, unreadMessages: chat.unreadMessages ?? 0,
subTitle: chat.lastMessage != null subTitle: chat.lastMessage != null
? chat.lastMessage is ChatTextMessageModel ? chat.lastMessage is ChatTextMessageModel
? (chat.lastMessage! as ChatTextMessageModel) ? (chat.lastMessage! as ChatTextMessageModel).text
.text
: '📷 ' : '📷 '
'${translations.image}' '${translations.image}'
: '', : '',
@ -378,8 +384,5 @@ class ChatListItem extends StatelessWidget {
: null, : null,
), ),
), ),
),
),
],
); );
} }

View file

@ -47,10 +47,11 @@ class _NewChatScreenState extends State<NewChatScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
var theme = Theme.of(context); var theme = Theme.of(context);
return Scaffold( return Scaffold(
backgroundColor: theme.colorScheme.surface,
appBar: AppBar( appBar: AppBar(
iconTheme: theme.appBarTheme.iconTheme ?? iconTheme: theme.appBarTheme.iconTheme ??
const IconThemeData(color: Colors.white), const IconThemeData(color: Colors.white),
backgroundColor: theme.appBarTheme.backgroundColor ?? Colors.black, backgroundColor: theme.appBarTheme.backgroundColor,
title: _buildSearchField(), title: _buildSearchField(),
actions: [ actions: [
_buildSearchIcon(), _buildSearchIcon(),
@ -114,7 +115,8 @@ class _NewChatScreenState extends State<NewChatScreen> {
} else if (snapshot.hasData) { } else if (snapshot.hasData) {
return _buildUserList(snapshot.data!); return _buildUserList(snapshot.data!);
} else { } else {
return Text(widget.translations.noUsersFound); return widget.options
.noUsersPlaceholderBuilder(widget.translations, context);
} }
}, },
), ),
@ -128,9 +130,7 @@ class _NewChatScreenState extends State<NewChatScreen> {
var theme = Theme.of(context); var theme = Theme.of(context);
return _isSearching return _isSearching
? Padding( ? TextField(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextField(
focusNode: _textFieldFocusNode, focusNode: _textFieldFocusNode,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@ -139,33 +139,19 @@ class _NewChatScreenState extends State<NewChatScreen> {
}, },
decoration: InputDecoration( decoration: InputDecoration(
hintText: widget.translations.searchPlaceholder, hintText: widget.translations.searchPlaceholder,
hintStyle: theme.inputDecorationTheme.hintStyle ?? hintStyle: theme.inputDecorationTheme.hintStyle,
const TextStyle(
color: Colors.white,
),
focusedBorder: UnderlineInputBorder( focusedBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: theme.inputDecorationTheme.focusedBorder?.borderSide color: theme.colorScheme.primary,
.color ??
Colors.white,
), ),
), ),
), ),
style: theme.inputDecorationTheme.hintStyle ?? style: theme.inputDecorationTheme.hintStyle,
const TextStyle(
color: Colors.white,
),
cursorColor: theme.textSelectionTheme.cursorColor ?? Colors.white, cursorColor: theme.textSelectionTheme.cursorColor ?? Colors.white,
),
) )
: Text( : Text(
widget.translations.newChatTitle, widget.translations.newChatTitle,
style: theme.appBarTheme.titleTextStyle ?? style: theme.appBarTheme.titleTextStyle,
TextStyle(
fontWeight: FontWeight.w800,
fontSize: 24,
color: Theme.of(context).primaryColor,
),
); );
} }
@ -191,6 +177,7 @@ class _NewChatScreenState extends State<NewChatScreen> {
} }
Widget _buildUserList(List<ChatUserModel> users) { Widget _buildUserList(List<ChatUserModel> users) {
var theme = Theme.of(context);
var filteredUsers = users var filteredUsers = users
.where( .where(
(user) => (user) =>
@ -204,37 +191,44 @@ class _NewChatScreenState extends State<NewChatScreen> {
if (_textFieldFocusNode.hasFocus && query.isEmpty) { if (_textFieldFocusNode.hasFocus && query.isEmpty) {
return Padding( return Padding(
padding: const EdgeInsets.only(top: 20.0), padding: const EdgeInsets.only(top: 20.0),
child: Center(
child: Text( child: Text(
widget.translations.startTyping, widget.translations.startTyping,
style: const TextStyle( style: theme.textTheme.bodySmall,
fontSize: 16.0,
fontWeight: FontWeight.bold,
color: Colors.grey,
),
),
), ),
); );
} }
if (filteredUsers.isEmpty) { if (filteredUsers.isEmpty) {
return widget.options.noChatsPlaceholderBuilder(widget.translations); return widget.options
.noUsersPlaceholderBuilder(widget.translations, context);
} }
var isPressed = false;
return ListView.builder( return ListView.builder(
itemCount: filteredUsers.length, itemCount: filteredUsers.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
var user = filteredUsers[index]; var user = filteredUsers[index];
return GestureDetector( return Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: theme.colorScheme.secondary.withOpacity(0.3),
width: 0.5,
),
),
),
child: GestureDetector(
child: widget.options.chatRowContainerBuilder( child: widget.options.chatRowContainerBuilder(
Container( Padding(
padding: widget.options.paddingAroundChatList ??
const EdgeInsets.symmetric(vertical: 8, horizontal: 28),
child: Container(
color: Colors.transparent, color: Colors.transparent,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0), padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Row( child: Row(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.only(left: 12.0, right: 12), padding: const EdgeInsets.symmetric(horizontal: 12),
child: widget.options.userAvatarBuilder(user, 40.0), child: widget.options.userAvatarBuilder(user, 40.0),
), ),
Expanded( Expanded(
@ -242,11 +236,9 @@ class _NewChatScreenState extends State<NewChatScreen> {
height: 40.0, height: 40.0,
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
user.fullName ?? widget.translations.anonymousUser, user.fullName ??
style: const TextStyle( widget.translations.anonymousUser,
fontSize: 18.0, style: theme.textTheme.bodyLarge,
fontWeight: FontWeight.w800,
),
), ),
), ),
), ),
@ -255,9 +247,15 @@ class _NewChatScreenState extends State<NewChatScreen> {
), ),
), ),
), ),
),
onTap: () async { onTap: () async {
if (!isPressed) {
isPressed = true;
await widget.onPressCreateChat(user); await widget.onPressCreateChat(user);
isPressed = false;
}
}, },
),
); );
}, },
); );

View file

@ -33,34 +33,57 @@ class _NewGroupChatOverviewScreenState
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var theme = Theme.of(context); var theme = Theme.of(context);
var formKey = GlobalKey<FormState>();
var isPressed = false;
return Scaffold( return Scaffold(
backgroundColor: theme.colorScheme.surface,
appBar: AppBar( appBar: AppBar(
iconTheme: theme.appBarTheme.iconTheme ?? iconTheme: theme.appBarTheme.iconTheme ??
const IconThemeData(color: Colors.white), const IconThemeData(color: Colors.white),
backgroundColor: theme.appBarTheme.backgroundColor ?? Colors.black, backgroundColor: theme.appBarTheme.backgroundColor,
title: const Text( title: Text(
'New Group Chat', widget.translations.newGroupChatTitle,
style: TextStyle(color: Colors.white), style: theme.appBarTheme.titleTextStyle,
), ),
), ),
body: Padding( body: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: TextField( child: Form(
key: formKey,
child: TextFormField(
controller: _textEditingController, controller: _textEditingController,
decoration: const InputDecoration( decoration: InputDecoration(
hintText: 'Group chat name', 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( floatingActionButton: FloatingActionButton(
backgroundColor: Theme.of(context).primaryColor, backgroundColor: Theme.of(context).primaryColor,
onPressed: () async { onPressed: () async {
if (!isPressed) {
isPressed = true;
if (formKey.currentState!.validate()) {
await widget.onPressCompleteGroupChatCreation( await widget.onPressCompleteGroupChatCreation(
widget.users, widget.users,
_textEditingController.text, _textEditingController.text,
); );
}
isPressed = false;
}
}, },
child: const Icon(Icons.check_circle), child: const Icon(
Icons.check_circle,
),
), ),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
); );

View file

@ -32,10 +32,11 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
var theme = Theme.of(context); var theme = Theme.of(context);
return Scaffold( return Scaffold(
backgroundColor: theme.colorScheme.surface,
appBar: AppBar( appBar: AppBar(
iconTheme: theme.appBarTheme.iconTheme ?? iconTheme: theme.appBarTheme.iconTheme ??
const IconThemeData(color: Colors.white), const IconThemeData(color: Colors.white),
backgroundColor: theme.appBarTheme.backgroundColor ?? Colors.black, backgroundColor: theme.appBarTheme.backgroundColor,
title: _buildSearchField(), title: _buildSearchField(),
actions: [ actions: [
_buildSearchIcon(), _buildSearchIcon(),
@ -51,10 +52,8 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
return Text('Error: ${snapshot.error}'); return Text('Error: ${snapshot.error}');
} else if (snapshot.hasData) { } else if (snapshot.hasData) {
return _buildUserList(snapshot.data!); return _buildUserList(snapshot.data!);
} else {
return widget.options
.noChatsPlaceholderBuilder(widget.translations);
} }
return const SizedBox.shrink();
}, },
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
@ -72,9 +71,7 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
var theme = Theme.of(context); var theme = Theme.of(context);
return _isSearching return _isSearching
? Padding( ? TextField(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextField(
focusNode: _textFieldFocusNode, focusNode: _textFieldFocusNode,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@ -83,33 +80,19 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
}, },
decoration: InputDecoration( decoration: InputDecoration(
hintText: widget.translations.searchPlaceholder, hintText: widget.translations.searchPlaceholder,
hintStyle: theme.inputDecorationTheme.hintStyle ?? hintStyle: theme.inputDecorationTheme.hintStyle,
const TextStyle(
color: Colors.white,
),
focusedBorder: UnderlineInputBorder( focusedBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: theme.inputDecorationTheme.focusedBorder?.borderSide color: theme.colorScheme.primary,
.color ??
Colors.white,
), ),
), ),
), ),
style: theme.inputDecorationTheme.hintStyle ?? style: theme.inputDecorationTheme.hintStyle,
const TextStyle(
color: Colors.white,
),
cursorColor: theme.textSelectionTheme.cursorColor ?? Colors.white, cursorColor: theme.textSelectionTheme.cursorColor ?? Colors.white,
),
) )
: Text( : Text(
widget.translations.newGroupChatButton, widget.translations.newGroupChatButton,
style: theme.appBarTheme.titleTextStyle ?? style: theme.appBarTheme.titleTextStyle,
TextStyle(
fontWeight: FontWeight.w800,
fontSize: 24,
color: Theme.of(context).primaryColor,
),
); );
} }
@ -146,60 +129,109 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
.toList(); .toList();
if (filteredUsers.isEmpty) { if (filteredUsers.isEmpty) {
return widget.options.noChatsPlaceholderBuilder(widget.translations); return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
widget.options
.noUsersPlaceholderBuilder(widget.translations, context),
],
);
} }
return ListView.separated( return UserList(
itemCount: filteredUsers.length, filteredUsers: filteredUsers,
separatorBuilder: (context, index) => const Padding( selectedUserList: selectedUserList,
padding: EdgeInsets.symmetric(horizontal: 28.0), options: widget.options,
child: Divider(), translations: widget.translations,
), // Add Divider here );
itemBuilder: (context, index) { }
var user = filteredUsers[index]; }
var isSelected =
selectedUserList.any((selectedUser) => selectedUser == user);
return InkWell( class UserList extends StatefulWidget {
const UserList({
required this.filteredUsers,
required this.selectedUserList,
required this.options,
required this.translations,
super.key,
});
final List<ChatUserModel> filteredUsers;
final List<ChatUserModel> selectedUserList;
final ChatOptions options;
final ChatTranslations translations;
@override
State<UserList> createState() => _UserListState();
}
class _UserListState extends State<UserList> {
@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: () { onTap: () {
setState(() { setState(() {
if (selectedUserList.contains(user)) { if (widget.selectedUserList.contains(user)) {
selectedUserList.remove(user); widget.selectedUserList.remove(user);
} else { } else {
selectedUserList.add(user); widget.selectedUserList.add(user);
} }
debugPrint('The list of selected users is $selectedUserList');
}); });
}, },
child: Container(
color: isSelected ? Colors.amber.shade200 : Colors.white,
child: Padding( child: Padding(
padding: padding: widget.options.paddingAroundChatList ??
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 30), 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( child: Row(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.only(left: 12.0, right: 12), padding: const EdgeInsets.symmetric(horizontal: 12),
child: widget.options.userAvatarBuilder(user, 40.0), child: widget.options.userAvatarBuilder(user, 40.0),
), ),
Expanded( Expanded(
child: Container( child: Container(
height: 40.0, // Adjust the height as needed height: 40,
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
user.fullName ?? widget.translations.anonymousUser, user.fullName ??
style: const TextStyle( widget.translations.anonymousUser,
fontWeight: FontWeight.w800, 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),
), ),
], ],
],
),
),
), ),
), ),
), ),
@ -207,4 +239,3 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
}, },
); );
} }
}

View file

@ -4,7 +4,7 @@
name: flutter_chat_view name: flutter_chat_view
description: A standard flutter package. description: A standard flutter package.
version: 2.0.0 version: 3.0.0
publish_to: none publish_to: none
@ -20,7 +20,7 @@ dependencies:
git: git:
url: https://github.com/Iconica-Development/flutter_chat url: https://github.com/Iconica-Development/flutter_chat
path: packages/flutter_chat_interface path: packages/flutter_chat_interface
ref: 2.0.0 ref: 3.0.0
cached_network_image: ^3.2.2 cached_network_image: ^3.2.2
flutter_image_picker: flutter_image_picker:
git: git:
@ -37,6 +37,6 @@ dev_dependencies:
flutter_iconica_analysis: flutter_iconica_analysis:
git: git:
url: https://github.com/Iconica-Development/flutter_iconica_analysis url: https://github.com/Iconica-Development/flutter_iconica_analysis
ref: 6.0.0 ref: 7.0.0
flutter: flutter: