feat: add firebase repo implementation, remove scaffold builders and add basescreenbuilder

This commit is contained in:
Niels Gorter 2024-08-21 12:01:56 +02:00 committed by Freek van de Ven
parent 1f3dc09f44
commit 61de7ae44a
24 changed files with 699 additions and 347 deletions

View file

@ -80,6 +80,36 @@ class ChatModel {
lastMessage: lastMessage ?? this.lastMessage, lastMessage: lastMessage ?? this.lastMessage,
unreadMessageCount: unreadMessageCount ?? this.unreadMessageCount, unreadMessageCount: unreadMessageCount ?? this.unreadMessageCount,
); );
/// The factory chat model that creates a chat model from a map
factory ChatModel.fromMap(String id, Map<String, dynamic> data) {
return ChatModel(
id: id,
users: List<String>.from(data['users']),
isGroupChat: data['isGroupChat'],
chatName: data['chatName'],
description: data['description'],
imageUrl: data['imageUrl'],
canBeDeleted: data['canBeDeleted'] ?? true,
lastUsed: data['lastUsed'] != null
? DateTime.fromMillisecondsSinceEpoch(data['lastUsed'])
: null,
lastMessage: data['lastMessage'],
unreadMessageCount: data['unreadMessageCount'] ?? 0,
);
}
Map<String, dynamic> toMap() => {
'users': users,
'isGroupChat': isGroupChat,
'chatName': chatName,
'description': description,
'imageUrl': imageUrl,
'canBeDeleted': canBeDeleted,
'lastUsed': lastUsed?.millisecondsSinceEpoch,
'lastMessage': lastMessage,
'unreadMessageCount': unreadMessageCount,
};
} }
/// The chat model extension /// The chat model extension

View file

@ -15,7 +15,7 @@ class MessageModel {
required this.timestamp, required this.timestamp,
required this.senderId, required this.senderId,
}); });
/// The chat id
final String chatId; final String chatId;
/// The message id /// The message id
@ -50,6 +50,27 @@ class MessageModel {
timestamp: timestamp ?? this.timestamp, timestamp: timestamp ?? this.timestamp,
senderId: senderId ?? this.senderId, senderId: senderId ?? this.senderId,
); );
factory MessageModel.fromMap(String id, Map<String, dynamic> map) {
return MessageModel(
chatId: map['chatId'],
id: id,
text: map['text'],
imageUrl: map['imageUrl'],
timestamp: DateTime.fromMillisecondsSinceEpoch(map['timestamp']),
senderId: map['senderId'],
);
}
Map<String, dynamic> toMap() {
return {
'chatId': chatId,
'text': text,
'imageUrl': imageUrl,
'timestamp': timestamp.millisecondsSinceEpoch,
'senderId': senderId,
};
}
} }
/// Extension on [MessageModel] to check the message type /// Extension on [MessageModel] to check the message type

View file

@ -25,6 +25,15 @@ class UserModel {
/// The user image url /// The user image url
final String? imageUrl; final String? imageUrl;
factory UserModel.fromMap(String id, Map<String, dynamic> data) {
return UserModel(
id: id,
firstName: data['firstName'],
lastName: data['lastName'],
imageUrl: data['imageUrl'],
);
}
} }
/// Extension on [UserModel] to get the user full name /// Extension on [UserModel] to get the user full name

View file

@ -188,16 +188,8 @@ class ChatService {
/// Returns a [Stream] of [int]. /// Returns a [Stream] of [int].
Stream<int> getUnreadMessagesCount({ Stream<int> getUnreadMessagesCount({
required String userId, required String userId,
String? chatId,
}) { }) {
if (chatId == null) { return chatRepository.getUnreadMessagesCount(userId: userId);
return chatRepository.getUnreadMessagesCount(userId: userId);
}
return chatRepository.getUnreadMessagesCount(
userId: userId,
chatId: chatId,
);
} }
/// Upload an image with the given parameters. /// Upload an image with the given parameters.
@ -218,9 +210,18 @@ class ChatService {
/// Returns a [Future] of [void]. /// Returns a [Future] of [void].
Future<void> markAsRead({ Future<void> markAsRead({
required String chatId, required String chatId,
required String userId,
}) async { }) async {
var chat = await chatRepository.getChat(chatId: chatId).first; var chat = await chatRepository.getChat(chatId: chatId).first;
if (chat.lastMessage == null) return;
var lastMessage = await chatRepository
.getMessage(chatId: chatId, messageId: chat.lastMessage!)
.first;
if (lastMessage != null && lastMessage.senderId == userId) return;
var newChat = chat.copyWith( var newChat = chat.copyWith(
lastUsed: DateTime.now(), lastUsed: DateTime.now(),
unreadMessageCount: 0, unreadMessageCount: 0,

View file

@ -1,39 +1,16 @@
<!-- # Firebase chat repository
This README describes the package. If you publish this package to pub.dev, The firebase implementation of the chat_repository_interface
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/developing-packages).
-->
TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.
## Features
TODO: List what your package can do. Maybe include images, gifs, or videos.
## Getting started
TODO: List prerequisites and provide or point to information on how to
start using the package.
## Usage ## Usage
TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.
```dart ```dart
const like = 'sample'; chatService: ChatService(
``` chatRepository: FirebaseChatRepository(
chatCollection: 'chats',
## Additional information messageCollection: 'messages',
mediaPath: 'chat',
TODO: Tell users more about the package: where to find more information, how to ),
contribute to the package, how to file issues, what response they can expect userRepository: FirebaseUserRepository(
from the package authors, and more. userCollection: 'users',
),
),
```

View file

@ -1,5 +1,2 @@
/// A Calculator. export 'src/firebase_chat_repository.dart';
class Calculator { export 'src/firebase_user_repository.dart';
/// Returns [value] plus 1.
int addOne(int value) => value + 1;
}

View file

@ -0,0 +1,191 @@
import 'dart:typed_data';
import 'package:chat_repository_interface/chat_repository_interface.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
class FirebaseChatRepository implements ChatRepositoryInterface {
final FirebaseFirestore _firestore;
final FirebaseStorage _storage;
final String chatCollection;
final String messageCollection;
final String mediaPath;
FirebaseChatRepository({
FirebaseFirestore? firestore,
FirebaseStorage? storage,
this.chatCollection = 'chats',
this.messageCollection = 'messages',
this.mediaPath = 'chat',
}) : _firestore = firestore ?? FirebaseFirestore.instance,
_storage = storage ?? FirebaseStorage.instance;
@override
Future<void> createChat({
required List<String> users,
required bool isGroupChat,
String? chatName,
String? description,
String? imageUrl,
List<MessageModel>? messages,
}) async {
final chatData = {
'users': users,
'isGroupChat': isGroupChat,
'chatName': chatName,
'description': description,
'imageUrl': imageUrl,
'createdAt': DateTime.now().millisecondsSinceEpoch,
};
await _firestore.collection(chatCollection).add(chatData);
}
@override
Future<void> deleteChat({required String chatId}) async {
await _firestore.collection(chatCollection).doc(chatId).delete();
}
@override
Stream<ChatModel> getChat({required String chatId}) {
return _firestore
.collection(chatCollection)
.doc(chatId)
.snapshots()
.map((snapshot) {
var data = snapshot.data() as Map<String, dynamic>;
return ChatModel.fromMap(snapshot.id, data);
});
}
@override
Stream<List<ChatModel>?> getChats({required String userId}) {
return _firestore
.collection(chatCollection)
.where('users', arrayContains: userId)
.snapshots()
.map((querySnapshot) {
return querySnapshot.docs.map((doc) {
var data = doc.data();
return ChatModel.fromMap(doc.id, data);
}).toList();
});
}
@override
Stream<MessageModel?> getMessage(
{required String chatId, required String messageId}) {
return _firestore
.collection(chatCollection)
.doc(chatId)
.collection(messageCollection)
.doc(messageId)
.snapshots()
.map((snapshot) {
var data = snapshot.data() as Map<String, dynamic>;
return MessageModel.fromMap(
snapshot.id,
data,
);
});
}
@override
Stream<List<MessageModel>?> getMessages({
required String chatId,
required String userId,
required int pageSize,
required int page,
}) {
return _firestore
.collection(chatCollection)
.doc(chatId)
.collection(messageCollection)
.orderBy('timestamp')
.limit(pageSize)
.snapshots()
.map((query) => query.docs
.map((snapshot) => MessageModel.fromMap(
snapshot.id,
snapshot.data(),
))
.toList());
}
@override
Stream<int> getUnreadMessagesCount(
{required String userId, String? chatId}) async* {
var query = _firestore
.collection(chatCollection)
.where('users', arrayContains: userId)
.where('unreadMessageCount', isGreaterThan: 0)
.snapshots();
await for (var snapshot in query) {
var count = 0;
for (var doc in snapshot.docs) {
var data = doc.data();
var lastMessageKey = data['lastMessage'];
var message =
await getMessage(chatId: doc.id, messageId: lastMessageKey).first;
if (message?.senderId != userId) {
count += data['unreadMessageCount'] as int;
}
}
yield count;
}
}
@override
Future<void> sendMessage({
required String chatId,
required String senderId,
required String messageId,
String? text,
String? imageUrl,
DateTime? timestamp,
}) async {
var message = MessageModel(
chatId: chatId,
id: messageId,
text: text,
imageUrl: imageUrl,
timestamp: timestamp ?? DateTime.now(),
senderId: senderId);
await _firestore
.collection(chatCollection)
.doc(chatId)
.collection(messageCollection)
.doc(messageId)
.set(
message.toMap(),
);
await _firestore.collection(chatCollection).doc(chatId).update(
{
'lastMessage': messageId,
'unreadMessageCount': FieldValue.increment(1),
'lastUsed': DateTime.now().millisecondsSinceEpoch,
},
);
}
@override
Future<void> updateChat({required ChatModel chat}) async {
await _firestore
.collection(chatCollection)
.doc(chat.id)
.update(chat.toMap());
}
@override
Future<String> uploadImage(
{required String path, required Uint8List image}) async {
final ref = _storage.ref().child(mediaPath).child(path);
final uploadTask = ref.putData(image);
final snapshot = await uploadTask.whenComplete(() => {});
return await snapshot.ref.getDownloadURL();
}
}

View file

@ -0,0 +1,41 @@
import 'package:chat_repository_interface/chat_repository_interface.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class FirebaseUserRepository implements UserRepositoryInterface {
final FirebaseFirestore _firestore;
final String userCollection;
FirebaseUserRepository({
FirebaseFirestore? firestore,
this.userCollection = 'users',
}) : _firestore = firestore ?? FirebaseFirestore.instance;
@override
Stream<List<UserModel>> getAllUsers() {
return _firestore
.collection(userCollection)
.snapshots()
.map((querySnapshot) {
return querySnapshot.docs
.map((doc) => UserModel.fromMap(
doc.id,
doc.data(),
))
.toList();
});
}
@override
Stream<UserModel> getUser({required String userId}) {
return _firestore
.collection(userCollection)
.doc(userId)
.snapshots()
.map((snapshot) {
return UserModel.fromMap(
snapshot.id,
snapshot.data() as Map<String, dynamic>,
);
});
}
}

View file

@ -2,6 +2,7 @@ name: firebase_chat_repository
description: "A new Flutter package project." description: "A new Flutter package project."
version: 0.0.1 version: 0.0.1
homepage: homepage:
publish_to: 'none'
environment: environment:
sdk: '>=3.4.3 <4.0.0' sdk: '>=3.4.3 <4.0.0'
@ -11,6 +12,12 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
chat_repository_interface:
path: ../chat_repository_interface
firebase_storage: any
cloud_firestore: any
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter

View file

@ -41,3 +41,5 @@ app.*.map.json
/android/app/debug /android/app/debug
/android/app/profile /android/app/profile
/android/app/release /android/app/release
firebase_options.dart

View file

@ -0,0 +1 @@
{"flutter":{"platforms":{"dart":{"lib/firebase_options.dart":{"projectId":"appshell-demo","configurations":{"android":"1:431820621472:android:6a0f2bc3559d17781babc5","web":"1:431820621472:web:f4b27eea24be24fd1babc5"}}},"android":{"default":{"projectId":"appshell-demo","appId":"1:431820621472:android:6a0f2bc3559d17781babc5","fileOutput":"android/app/google-services.json"}}}}}

View file

@ -1,3 +1,7 @@
// import 'package:example/firebase_options.dart';
// import 'package:firebase_auth/firebase_auth.dart';
// import 'package:firebase_chat_repository/firebase_chat_repository.dart';
// import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_chat/flutter_chat.dart'; import 'package:flutter_chat/flutter_chat.dart';
@ -7,7 +11,6 @@ void main() {
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({super.key}); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
@ -16,6 +19,18 @@ class MyApp extends StatelessWidget {
useMaterial3: true, useMaterial3: true,
), ),
home: const MyHomePage(), home: const MyHomePage(),
// FutureBuilder(
// future: Firebase.initializeApp(
// options: DefaultFirebaseOptions.currentPlatform,
// ),
// builder: (context, snapshot) {
// if (snapshot.connectionState != ConnectionState.done) {
// return const Center(
// child: CircularProgressIndicator(),
// );
// }
// return const MyHomePage();
// }),
); );
} }
} }
@ -28,6 +43,12 @@ class MyHomePage extends StatefulWidget {
} }
class _MyHomePageState extends State<MyHomePage> { class _MyHomePageState extends State<MyHomePage> {
// @override
// void initState() {
// FirebaseAuth.instance.signInAnonymously();
// super.initState();
// }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(

View file

@ -13,6 +13,10 @@ dependencies:
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
flutter_chat: flutter_chat:
path: ../ path: ../
# firebase_chat_repository:
# path: ../../firebase_chat_repository
# firebase_core: any
# firebase_auth: any
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View file

@ -11,6 +11,7 @@ export "package:flutter_chat/src/flutter_chat_navigator_userstory.dart";
export "src/config/chat_builders.dart"; export "src/config/chat_builders.dart";
export "src/config/chat_options.dart"; export "src/config/chat_options.dart";
export "src/config/chat_translations.dart"; export "src/config/chat_translations.dart";
export 'src/config/screen_types.dart';
// Screens // Screens
export "src/screens/chat_detail_screen.dart"; export "src/screens/chat_detail_screen.dart";

View file

@ -1,18 +1,14 @@
import "package:chat_repository_interface/chat_repository_interface.dart"; import "package:chat_repository_interface/chat_repository_interface.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_chat/src/config/chat_translations.dart"; import "package:flutter_chat/src/config/chat_translations.dart";
import "package:flutter_chat/src/config/screen_types.dart";
/// The chat builders /// The chat builders
class ChatBuilders { class ChatBuilders {
/// The chat builders constructor /// The chat builders constructor
const ChatBuilders({ const ChatBuilders({
this.chatScreenScaffoldBuilder, this.baseScreenBuilder,
this.newChatScreenScaffoldBuilder,
this.newGroupChatScreenScaffoldBuilder,
this.newGroupChatOverviewScaffoldBuilder,
this.chatProfileScaffoldBuilder,
this.messageInputBuilder, this.messageInputBuilder,
this.chatDetailScaffoldBuilder,
this.chatRowContainerBuilder, this.chatRowContainerBuilder,
this.groupAvatarBuilder, this.groupAvatarBuilder,
this.imagePickerContainerBuilder, this.imagePickerContainerBuilder,
@ -25,23 +21,21 @@ class ChatBuilders {
this.loadingWidgetBuilder, this.loadingWidgetBuilder,
}); });
/// The chat screen scaffold builder /// The base screen builder
final ScaffoldBuilder? chatScreenScaffoldBuilder; /// This builder is used to build the base screen for the chat
/// You can switch on the [screenType] to build different screens
/// The new chat screen scaffold builder /// ```dart
final ScaffoldBuilder? newChatScreenScaffoldBuilder; /// baseScreenBuilder: (context, screenType, appBar, body) {
/// switch (screenType) {
/// The new group chat overview scaffold builder /// case ScreenType.chatScreen:
final ScaffoldBuilder? newGroupChatOverviewScaffoldBuilder; /// return Scaffold(
/// appBar: appBar,
/// The new group chat screen scaffold builder /// body: body,
final ScaffoldBuilder? newGroupChatScreenScaffoldBuilder; /// );
/// case ScreenType.chatDetailScreen:
/// The chat detail scaffold builder /// // And so on....
final ScaffoldBuilder? chatDetailScaffoldBuilder; /// ```
final BaseScreenBuilder? baseScreenBuilder;
/// The chat profile scaffold builder
final ScaffoldBuilder? chatProfileScaffoldBuilder;
/// The message input builder /// The message input builder
final TextInputBuilder? messageInputBuilder; final TextInputBuilder? messageInputBuilder;
@ -100,12 +94,12 @@ typedef TextInputBuilder = Widget Function(
ChatTranslations translations, ChatTranslations translations,
); );
/// The scaffold builder /// The base screen builder
typedef ScaffoldBuilder = Scaffold Function( typedef BaseScreenBuilder = Widget Function(
BuildContext context, BuildContext context,
ScreenType screenType,
PreferredSizeWidget appBar, PreferredSizeWidget appBar,
Widget body, Widget body,
Color backgroundColor,
); );
/// The container builder /// The container builder

View file

@ -0,0 +1,28 @@
import 'package:flutter/material.dart';
import 'package:flutter_chat/src/screens/chat_detail_screen.dart';
import 'package:flutter_chat/src/screens/chat_profile_screen.dart';
import 'package:flutter_chat/src/screens/chat_screen.dart';
import 'package:flutter_chat/src/screens/creation/new_chat_screen.dart';
import 'package:flutter_chat/src/screens/creation/new_group_chat_overview.dart';
import 'package:flutter_chat/src/screens/creation/new_group_chat_screen.dart';
enum ScreenType {
chatScreen(screen: ChatScreen),
chatDetailScreen(screen: ChatDetailScreen),
chatProfileScreen(screen: ChatProfileScreen),
newChatScreen(screen: NewChatScreen),
newGroupChatScreen(screen: NewGroupChatScreen),
newGroupChatOverview(screen: NewGroupChatOverview);
const ScreenType({
required this.screen,
});
final Type screen;
}
extension MapFromWidget on Widget {
ScreenType get mapScreenType {
return ScreenType.values.firstWhere((e) => e.screen == this.runtimeType);
}
}

View file

@ -9,6 +9,7 @@ class FlutterChatEntryWidget extends StatefulWidget {
const FlutterChatEntryWidget({ const FlutterChatEntryWidget({
required this.userId, required this.userId,
this.chatService, this.chatService,
this.options,
this.onTap, this.onTap,
this.widgetSize = 75, this.widgetSize = 75,
this.backgroundColor = Colors.grey, this.backgroundColor = Colors.grey,
@ -46,6 +47,9 @@ class FlutterChatEntryWidget extends StatefulWidget {
/// Text style for the counter. /// Text style for the counter.
final TextStyle? textStyle; final TextStyle? textStyle;
/// The chat options
final ChatOptions? options;
@override @override
State<FlutterChatEntryWidget> createState() => _FlutterChatEntryWidgetState(); State<FlutterChatEntryWidget> createState() => _FlutterChatEntryWidgetState();
} }
@ -69,6 +73,7 @@ class _FlutterChatEntryWidgetState extends State<FlutterChatEntryWidget> {
builder: (context) => FlutterChatNavigatorUserstory( builder: (context) => FlutterChatNavigatorUserstory(
userId: widget.userId, userId: widget.userId,
chatService: chatService, chatService: chatService,
chatOptions: widget.options,
), ),
), ),
), ),

View file

@ -100,9 +100,8 @@ class _NavigatorWrapper extends StatelessWidget {
chatService: chatService, chatService: chatService,
chatOptions: chatOptions, chatOptions: chatOptions,
chat: chat, chat: chat,
onReadChat: (chat) async => chatService.markAsRead( onReadChat: (chat) async =>
chatId: chat.id, chatService.markAsRead(chatId: chat.id, userId: userId),
),
onPressChatTitle: (chat) async { onPressChatTitle: (chat) async {
if (chat.isGroupChat) { if (chat.isGroupChat) {
return route(context, chatProfileScreen(context, null, chat)); return route(context, chatProfileScreen(context, null, chat));
@ -117,7 +116,8 @@ class _NavigatorWrapper extends StatelessWidget {
onPressUserProfile: (user) => onPressUserProfile: (user) =>
route(context, chatProfileScreen(context, user, null)), route(context, chatProfileScreen(context, user, null)),
onUploadImage: (data) async { onUploadImage: (data) async {
var path = await chatService.uploadImage(path: "chats", image: data); var path = await chatService.uploadImage(
path: "chats/${chat.id}-$userId-${DateTime.now()}", image: data);
await chatService.sendMessage( await chatService.sendMessage(
messageId: "${chat.id}-$userId-${DateTime.now()}", messageId: "${chat.id}-$userId-${DateTime.now()}",
@ -190,7 +190,8 @@ class _NavigatorWrapper extends StatelessWidget {
onComplete: (users, title, description, image) async { onComplete: (users, title, description, image) async {
String? path; String? path;
if (image != null) { if (image != null) {
path = await chatService.uploadImage(path: "groups", image: image); path = await chatService.uploadImage(
path: "groups/$title", image: image);
} }
var chat = await createGroupChat( var chat = await createGroupChat(
users, users,

View file

@ -5,6 +5,7 @@ import "package:cached_network_image/cached_network_image.dart";
import "package:chat_repository_interface/chat_repository_interface.dart"; import "package:chat_repository_interface/chat_repository_interface.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_chat/src/config/chat_options.dart"; import "package:flutter_chat/src/config/chat_options.dart";
import "package:flutter_chat/src/config/screen_types.dart";
import "package:flutter_chat/src/screens/creation/widgets/image_picker.dart"; import "package:flutter_chat/src/screens/creation/widgets/image_picker.dart";
import "package:flutter_chat/src/services/date_formatter.dart"; import "package:flutter_chat/src/services/date_formatter.dart";
import "package:flutter_profile/flutter_profile.dart"; import "package:flutter_profile/flutter_profile.dart";
@ -85,46 +86,47 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var theme = Theme.of(context); if (widget.chatOptions.builders.baseScreenBuilder == null) {
return Scaffold(
appBar: _AppBar(
chatTitle: chatTitle,
chatOptions: widget.chatOptions,
onPressChatTitle: widget.onPressChatTitle,
chatModel: widget.chat,
),
body: _Body(
chatService: widget.chatService,
options: widget.chatOptions,
chat: widget.chat,
currentUserId: widget.userId,
onPressUserProfile: widget.onPressUserProfile,
onUploadImage: widget.onUploadImage,
onMessageSubmit: widget.onMessageSubmit,
onReadChat: widget.onReadChat,
),
);
}
return widget.chatOptions.builders.chatDetailScaffoldBuilder?.call( return widget.chatOptions.builders.baseScreenBuilder!.call(
context, context,
_AppBar( widget.mapScreenType,
chatTitle: chatTitle, _AppBar(
chatOptions: widget.chatOptions, chatTitle: chatTitle,
onPressChatTitle: widget.onPressChatTitle, chatOptions: widget.chatOptions,
chatModel: widget.chat, onPressChatTitle: widget.onPressChatTitle,
), chatModel: widget.chat,
_Body( ),
chatService: widget.chatService, _Body(
options: widget.chatOptions, chatService: widget.chatService,
chat: widget.chat, options: widget.chatOptions,
currentUserId: widget.userId, chat: widget.chat,
onPressUserProfile: widget.onPressUserProfile, currentUserId: widget.userId,
onUploadImage: widget.onUploadImage, onPressUserProfile: widget.onPressUserProfile,
onMessageSubmit: widget.onMessageSubmit, onUploadImage: widget.onUploadImage,
onReadChat: widget.onReadChat, onMessageSubmit: widget.onMessageSubmit,
), onReadChat: widget.onReadChat,
theme.scaffoldBackgroundColor, ),
) ?? );
Scaffold(
appBar: _AppBar(
chatTitle: chatTitle,
chatOptions: widget.chatOptions,
onPressChatTitle: widget.onPressChatTitle,
chatModel: widget.chat,
),
body: _Body(
chatService: widget.chatService,
options: widget.chatOptions,
chat: widget.chat,
currentUserId: widget.userId,
onPressUserProfile: widget.onPressUserProfile,
onUploadImage: widget.onUploadImage,
onMessageSubmit: widget.onMessageSubmit,
onReadChat: widget.onReadChat,
),
);
} }
} }

View file

@ -1,6 +1,7 @@
import "package:chat_repository_interface/chat_repository_interface.dart"; import "package:chat_repository_interface/chat_repository_interface.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_chat/src/config/chat_options.dart"; import "package:flutter_chat/src/config/chat_options.dart";
import "package:flutter_chat/src/config/screen_types.dart";
import "package:flutter_profile/flutter_profile.dart"; import "package:flutter_profile/flutter_profile.dart";
/// The chat profile screen /// The chat profile screen
@ -41,42 +42,43 @@ class ChatProfileScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var theme = Theme.of(context); if (options.builders.baseScreenBuilder == null) {
return Scaffold(
appBar: _AppBar(
user: userModel,
chat: chatModel,
options: options,
),
body: _Body(
currentUser: userId,
options: options,
service: service,
user: userModel,
chat: chatModel,
onTapUser: onTapUser,
onPressStartChat: onPressStartChat,
),
);
}
return options.builders.chatProfileScaffoldBuilder?.call( return options.builders.baseScreenBuilder!.call(
context, context,
_AppBar( this.mapScreenType,
user: userModel, _AppBar(
chat: chatModel, user: userModel,
options: options, chat: chatModel,
), options: options,
_Body( ),
service: service, _Body(
currentUser: userId, currentUser: userId,
options: options, options: options,
user: userModel, service: service,
chat: chatModel, user: userModel,
onTapUser: onTapUser, chat: chatModel,
onPressStartChat: onPressStartChat, onTapUser: onTapUser,
), onPressStartChat: onPressStartChat,
theme.scaffoldBackgroundColor, ),
) ?? );
Scaffold(
appBar: _AppBar(
user: userModel,
chat: chatModel,
options: options,
),
body: _Body(
currentUser: userId,
options: options,
service: service,
user: userModel,
chat: chatModel,
onTapUser: onTapUser,
onPressStartChat: onPressStartChat,
),
);
} }
} }

View file

@ -2,6 +2,7 @@ import "package:chat_repository_interface/chat_repository_interface.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_chat/src/config/chat_options.dart"; import "package:flutter_chat/src/config/chat_options.dart";
import "package:flutter_chat/src/config/chat_translations.dart"; import "package:flutter_chat/src/config/chat_translations.dart";
import "package:flutter_chat/src/config/screen_types.dart";
import "package:flutter_chat/src/services/date_formatter.dart"; import "package:flutter_chat/src/services/date_formatter.dart";
import "package:flutter_profile/flutter_profile.dart"; import "package:flutter_profile/flutter_profile.dart";
@ -39,39 +40,41 @@ class ChatScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var theme = Theme.of(context); if (chatOptions.builders.baseScreenBuilder == null) {
return chatOptions.builders.chatScreenScaffoldBuilder?.call( return Scaffold(
context, appBar: _AppBar(
_AppBar( userId: userId,
userId: userId, chatOptions: chatOptions,
chatOptions: chatOptions, chatService: chatService,
chatService: chatService, ),
), body: _Body(
_Body( userId: userId,
userId: userId, chatOptions: chatOptions,
chatOptions: chatOptions, chatService: chatService,
chatService: chatService, onPressChat: onPressChat,
onPressChat: onPressChat, onPressStartChat: onPressStartChat,
onPressStartChat: onPressStartChat, onDeleteChat: onDeleteChat,
onDeleteChat: onDeleteChat, ),
), );
theme.scaffoldBackgroundColor, }
) ??
Scaffold( return chatOptions.builders.baseScreenBuilder!.call(
appBar: _AppBar( context,
userId: userId, this.mapScreenType,
chatOptions: chatOptions, _AppBar(
chatService: chatService, userId: userId,
), chatOptions: chatOptions,
body: _Body( chatService: chatService,
userId: userId, ),
chatOptions: chatOptions, _Body(
chatService: chatService, userId: userId,
onPressChat: onPressChat, chatOptions: chatOptions,
onPressStartChat: onPressStartChat, chatService: chatService,
onDeleteChat: onDeleteChat, onPressChat: onPressChat,
), onPressStartChat: onPressStartChat,
); onDeleteChat: onDeleteChat,
),
);
} }
} }
@ -395,9 +398,13 @@ class _ChatListItem extends StatelessWidget {
var data = snapshot.data; var data = snapshot.data;
var showUnreadMessageCount =
data != null && data.senderId != currentUserId;
return _ChatRow( return _ChatRow(
title: chat.chatName ?? translations.groupNameEmpty, title: chat.chatName ?? translations.groupNameEmpty,
unreadMessages: chat.unreadMessageCount, unreadMessages:
showUnreadMessageCount ? chat.unreadMessageCount : 0,
subTitle: data != null subTitle: data != null
? data.isTextMessage ? data.isTextMessage
? data.text ? data.text
@ -465,8 +472,12 @@ class _ChatListItem extends StatelessWidget {
var data = snapshot.data; var data = snapshot.data;
var showUnreadMessageCount =
data != null && data.senderId != currentUserId;
return _ChatRow( return _ChatRow(
unreadMessages: chat.unreadMessageCount, unreadMessages:
showUnreadMessageCount ? chat.unreadMessageCount : 0,
avatar: options.builders.userAvatarBuilder?.call( avatar: options.builders.userAvatarBuilder?.call(
context, context,
otherUser, otherUser,

View file

@ -1,6 +1,7 @@
import "package:chat_repository_interface/chat_repository_interface.dart"; import "package:chat_repository_interface/chat_repository_interface.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_chat/src/config/chat_options.dart"; import "package:flutter_chat/src/config/chat_options.dart";
import "package:flutter_chat/src/config/screen_types.dart";
import "package:flutter_chat/src/screens/creation/widgets/search_field.dart"; import "package:flutter_chat/src/screens/creation/widgets/search_field.dart";
import "package:flutter_chat/src/screens/creation/widgets/search_icon.dart"; import "package:flutter_chat/src/screens/creation/widgets/search_icon.dart";
import "package:flutter_chat/src/screens/creation/widgets/user_list.dart"; import "package:flutter_chat/src/screens/creation/widgets/user_list.dart";
@ -44,74 +45,75 @@ class _NewChatScreenState extends State<NewChatScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var theme = Theme.of(context); if (widget.chatOptions.builders.baseScreenBuilder == null) {
return Scaffold(
appBar: _AppBar(
chatOptions: widget.chatOptions,
isSearching: _isSearching,
onSearch: (query) {
setState(() {
_isSearching = query.isNotEmpty;
this.query = query;
});
},
onPressedSearchIcon: () {
setState(() {
_isSearching = !_isSearching;
query = "";
});
return widget.chatOptions.builders.newChatScreenScaffoldBuilder?.call( if (_isSearching) {
context, _textFieldFocusNode.requestFocus();
_AppBar( }
chatOptions: widget.chatOptions, },
isSearching: _isSearching, focusNode: _textFieldFocusNode,
onSearch: (query) { ),
setState(() { body: _Body(
_isSearching = query.isNotEmpty; chatOptions: widget.chatOptions,
this.query = query; chatService: widget.chatService,
}); isSearching: _isSearching,
}, onPressCreateGroupChat: widget.onPressCreateGroupChat,
onPressedSearchIcon: () { onPressCreateChat: widget.onPressCreateChat,
setState(() { userId: widget.userId,
_isSearching = !_isSearching; query: query,
query = ""; ),
}); );
}
if (_isSearching) { return widget.chatOptions.builders.baseScreenBuilder!.call(
_textFieldFocusNode.requestFocus(); context,
} widget.mapScreenType,
}, _AppBar(
focusNode: _textFieldFocusNode, chatOptions: widget.chatOptions,
), isSearching: _isSearching,
_Body( onSearch: (query) {
chatOptions: widget.chatOptions, setState(() {
chatService: widget.chatService, _isSearching = query.isNotEmpty;
isSearching: _isSearching, this.query = query;
onPressCreateGroupChat: widget.onPressCreateGroupChat, });
onPressCreateChat: widget.onPressCreateChat, },
userId: widget.userId, onPressedSearchIcon: () {
query: query, setState(() {
), _isSearching = !_isSearching;
theme.scaffoldBackgroundColor, query = "";
) ?? });
Scaffold(
appBar: _AppBar(
chatOptions: widget.chatOptions,
isSearching: _isSearching,
onSearch: (query) {
setState(() {
_isSearching = query.isNotEmpty;
this.query = query;
});
},
onPressedSearchIcon: () {
setState(() {
_isSearching = !_isSearching;
query = "";
});
if (_isSearching) { if (_isSearching) {
_textFieldFocusNode.requestFocus(); _textFieldFocusNode.requestFocus();
} }
}, },
focusNode: _textFieldFocusNode, focusNode: _textFieldFocusNode,
), ),
body: _Body( _Body(
chatOptions: widget.chatOptions, chatOptions: widget.chatOptions,
chatService: widget.chatService, chatService: widget.chatService,
isSearching: _isSearching, isSearching: _isSearching,
onPressCreateGroupChat: widget.onPressCreateGroupChat, onPressCreateGroupChat: widget.onPressCreateGroupChat,
onPressCreateChat: widget.onPressCreateChat, onPressCreateChat: widget.onPressCreateChat,
userId: widget.userId, userId: widget.userId,
query: query, query: query,
), ),
); );
} }
} }

View file

@ -3,6 +3,7 @@ import "dart:typed_data";
import "package:chat_repository_interface/chat_repository_interface.dart"; import "package:chat_repository_interface/chat_repository_interface.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_chat/src/config/chat_options.dart"; import "package:flutter_chat/src/config/chat_options.dart";
import "package:flutter_chat/src/config/screen_types.dart";
import "package:flutter_chat/src/screens/creation/widgets/image_picker.dart"; import "package:flutter_chat/src/screens/creation/widgets/image_picker.dart";
import "package:flutter_profile/flutter_profile.dart"; import "package:flutter_profile/flutter_profile.dart";
@ -34,30 +35,31 @@ class NewGroupChatOverview extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var theme = Theme.of(context); if (options.builders.baseScreenBuilder == null) {
return Scaffold(
appBar: _AppBar(
options: options,
),
body: _Body(
options: options,
users: users,
onComplete: onComplete,
),
);
}
return options.builders.newGroupChatOverviewScaffoldBuilder?.call( return options.builders.baseScreenBuilder!.call(
context, context,
_AppBar( this.mapScreenType,
options: options, _AppBar(
), options: options,
_Body( ),
options: options, _Body(
users: users, options: options,
onComplete: onComplete, users: users,
), onComplete: onComplete,
theme.scaffoldBackgroundColor, ),
) ?? );
Scaffold(
appBar: _AppBar(
options: options,
),
body: _Body(
options: options,
users: users,
onComplete: onComplete,
),
);
} }
} }

View file

@ -1,6 +1,7 @@
import "package:chat_repository_interface/chat_repository_interface.dart"; import "package:chat_repository_interface/chat_repository_interface.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_chat/src/config/chat_options.dart"; import "package:flutter_chat/src/config/chat_options.dart";
import "package:flutter_chat/src/config/screen_types.dart";
import "package:flutter_chat/src/screens/creation/widgets/search_field.dart"; import "package:flutter_chat/src/screens/creation/widgets/search_field.dart";
import "package:flutter_chat/src/screens/creation/widgets/search_icon.dart"; import "package:flutter_chat/src/screens/creation/widgets/search_icon.dart";
import "package:flutter_chat/src/screens/creation/widgets/user_list.dart"; import "package:flutter_chat/src/screens/creation/widgets/user_list.dart";
@ -42,76 +43,77 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var theme = Theme.of(context); if (widget.chatOptions.builders.baseScreenBuilder == null) {
return Scaffold(
appBar: _AppBar(
chatOptions: widget.chatOptions,
isSearching: _isSearching,
onSearch: (query) {
setState(() {
_isSearching = query.isNotEmpty;
this.query = query;
});
},
onPressedSearchIcon: () {
setState(() {
_isSearching = !_isSearching;
query = "";
});
return widget.chatOptions.builders.newGroupChatScreenScaffoldBuilder?.call( if (_isSearching) {
context, _textFieldFocusNode.requestFocus();
_AppBar( }
chatOptions: widget.chatOptions, },
isSearching: _isSearching, focusNode: _textFieldFocusNode,
onSearch: (query) { ),
setState(() { body: _Body(
_isSearching = query.isNotEmpty; onSelectedUser: handleUserTap,
this.query = query; selectedUsers: selectedUsers,
}); onPressGroupChatOverview: widget.onContinue,
}, chatOptions: widget.chatOptions,
onPressedSearchIcon: () { chatService: widget.chatService,
setState(() { isSearching: _isSearching,
_isSearching = !_isSearching; userId: widget.userId,
query = ""; query: query,
}); ),
);
}
if (_isSearching) { return widget.chatOptions.builders.baseScreenBuilder!.call(
_textFieldFocusNode.requestFocus(); context,
} widget.mapScreenType,
}, _AppBar(
focusNode: _textFieldFocusNode, chatOptions: widget.chatOptions,
), isSearching: _isSearching,
_Body( onSearch: (query) {
onSelectedUser: handleUserTap, setState(() {
selectedUsers: selectedUsers, _isSearching = query.isNotEmpty;
onPressGroupChatOverview: widget.onContinue, this.query = query;
chatOptions: widget.chatOptions, });
chatService: widget.chatService, },
isSearching: _isSearching, onPressedSearchIcon: () {
userId: widget.userId, setState(() {
query: query, _isSearching = !_isSearching;
), query = "";
theme.scaffoldBackgroundColor, });
) ??
Scaffold(
appBar: _AppBar(
chatOptions: widget.chatOptions,
isSearching: _isSearching,
onSearch: (query) {
setState(() {
_isSearching = query.isNotEmpty;
this.query = query;
});
},
onPressedSearchIcon: () {
setState(() {
_isSearching = !_isSearching;
query = "";
});
if (_isSearching) { if (_isSearching) {
_textFieldFocusNode.requestFocus(); _textFieldFocusNode.requestFocus();
} }
}, },
focusNode: _textFieldFocusNode, focusNode: _textFieldFocusNode,
), ),
body: _Body( _Body(
onSelectedUser: handleUserTap, onSelectedUser: handleUserTap,
selectedUsers: selectedUsers, selectedUsers: selectedUsers,
onPressGroupChatOverview: widget.onContinue, onPressGroupChatOverview: widget.onContinue,
chatOptions: widget.chatOptions, chatOptions: widget.chatOptions,
chatService: widget.chatService, chatService: widget.chatService,
isSearching: _isSearching, isSearching: _isSearching,
userId: widget.userId, userId: widget.userId,
query: query, query: query,
), ),
); );
} }
void handleUserTap(UserModel user) { void handleUserTap(UserModel user) {