mirror of
https://github.com/Iconica-Development/flutter_chat.git
synced 2025-05-18 18:33:49 +02:00
fix: feedback
This commit is contained in:
parent
28e307cf90
commit
2b5ab5a933
10 changed files with 351 additions and 10 deletions
|
@ -1,3 +1,9 @@
|
|||
## 1.0.0
|
||||
|
||||
- Added pagination for the ChatDetailScreen
|
||||
- Added routes with Go_router and Navigator
|
||||
- Added ChatEntryWidget
|
||||
|
||||
## 0.6.0 - December 1 2023
|
||||
|
||||
- Made the message controller nullable
|
||||
|
|
99
README.md
99
README.md
|
@ -18,7 +18,7 @@ To use this package, add flutter_chat as a dependency in your pubspec.yaml file:
|
|||
path: packages/flutter_chat
|
||||
```
|
||||
|
||||
If you are going to use Firebase as the back-end of the Community Chat, you should also add the following package as a dependency to your pubspec.yaml file:
|
||||
If you are going to use Firebase as the back-end of the Chat, you should also add the following package as a dependency to your pubspec.yaml file:
|
||||
|
||||
```
|
||||
flutter_chat_firebase:
|
||||
|
@ -29,6 +29,35 @@ If you are going to use Firebase as the back-end of the Community Chat, you shou
|
|||
|
||||
Create a Firebase project for your application and add firebase firestore and storage.
|
||||
|
||||
make sure you are authenticated using the `Firebase_auth` package or adjust your firebase rules, otherwise you won't be able to retreive data.
|
||||
|
||||
Also make sure you have the corresponding collections in your firebase project as defined in `FirebaseChatOptions`, you can override the
|
||||
default paths as you wish, also the structure of your data should be equal to our predefined models, you can implement any model by making your own model and implementing one of the predefined interfaces like so:
|
||||
|
||||
```
|
||||
class ChatMessageModel implements ChatMessageModelInterface {
|
||||
ChatMessageModel({
|
||||
required this.sender,
|
||||
required this.timestamp,
|
||||
});
|
||||
|
||||
@override
|
||||
final ChatUserModel sender;
|
||||
@override
|
||||
final DateTime timestamp;
|
||||
}
|
||||
```
|
||||
|
||||
below a list of interfaces you can implement;
|
||||
|
||||
`ChatUserModelInterface`,
|
||||
`ChatImageMessageModelInterface`,
|
||||
`ChatTextMessageModelInterface`
|
||||
`ChatMessageModelInterface`,
|
||||
`ChatModelInterface`,
|
||||
`GroupChatModelInterface`,
|
||||
`PersonalChatModelInterface`,
|
||||
|
||||
To use the camera or photo library to send photos add the following to your project:
|
||||
|
||||
For ios add the following lines to your info.plist:
|
||||
|
@ -63,6 +92,8 @@ List<GoRoute> getChatRoutes() => getChatStoryRoutes(
|
|||
);
|
||||
```
|
||||
|
||||
You can override any method in the `ChatUserStoryConfiguration`.
|
||||
|
||||
Add the `getChatRoutes()` to your go_router routes like so:
|
||||
|
||||
```
|
||||
|
@ -81,12 +112,55 @@ final GoRouter _router = GoRouter(
|
|||
);
|
||||
```
|
||||
|
||||
To use the module within your Flutter-application without predefined `Go_router` routes add the following code to the build-method of a chosen widget:
|
||||
The routes that can be used to navigate are:
|
||||
|
||||
The `ChatScreen` shows all chats that you currently have with their latest messages.
|
||||
For routing to the `ChatScreen`:
|
||||
|
||||
```
|
||||
static const String chatScreen = '/chat';
|
||||
```
|
||||
|
||||
For routing to the `ChatDetailScreen`:
|
||||
|
||||
```
|
||||
static String chatDetailViewPath(String chatId) => '/chat-detail/$chatId';
|
||||
static const String chatDetailScreen = '/chat-detail/:id';
|
||||
```
|
||||
|
||||
For routing to the `NewChatScreen`:
|
||||
|
||||
```
|
||||
static const String newChatScreen = '/new-chat';
|
||||
```
|
||||
|
||||
For routing to the `ChatProfileScreen`:
|
||||
you can see the information about a person or group you started a chat with.
|
||||
If the userId is null a group profile screen will be shown otherwise the profile of a single person will be shown.
|
||||
|
||||
```
|
||||
static String chatProfileScreenPath(String chatId, String? userId) =>
|
||||
'/chat-profile/$chatId/$userId';
|
||||
static const String chatProfileScreen = '/chat-profile/:id/:userId';
|
||||
```
|
||||
|
||||
To use the module within your Flutter-application without predefined `Go_router` routes but with Navigator routes add the following code to the build-method of a chosen widget:
|
||||
|
||||
```
|
||||
chatNavigatorUserStory(
|
||||
ChatUserStoryConfiguration(
|
||||
chatService: ChatService,
|
||||
chatOptionsBuilder: (ctx) => const ChatOptions(),
|
||||
),
|
||||
context,
|
||||
);
|
||||
```
|
||||
|
||||
Just like with the `Go_router` routes you can override any methods in the `ChatUserStoryConfiguration`.
|
||||
|
||||
Or create your own routing using the Screens:
|
||||
To add the `ChatScreen` add the following code:
|
||||
|
||||
````
|
||||
```
|
||||
ChatScreen(
|
||||
options: options,
|
||||
onPressStartChat: onPressStartChat,
|
||||
|
@ -121,10 +195,22 @@ NewChatScreen(
|
|||
);
|
||||
```
|
||||
|
||||
On the `ChatProfileScreen` you can see the information about a person or group you started a chat with.
|
||||
If the userId is null a group profile screen will be shown otherwise the profile of a single person will be shown.
|
||||
|
||||
```
|
||||
ChatProfileScreen(
|
||||
chatService: chatservice,
|
||||
chatId: chatId,
|
||||
translations: translations,
|
||||
onTapUser: onTapUser,
|
||||
userId: userId,
|
||||
);
|
||||
```
|
||||
|
||||
The `ChatEntryWidget` is a widget you can put anywhere in your app.
|
||||
It displays the amount of unread messages you currently have.
|
||||
You can choose to add a onTap to the `ChatEntryWidget` so it routes to the `ChatScreen`,
|
||||
where all your chats are shown.
|
||||
You can choose to add a onTap to the `ChatEntryWidget` so it routes to the `ChatScreen`.
|
||||
|
||||
To add the `ChatEntryWidget` add the follwoing code:
|
||||
|
||||
|
@ -158,4 +244,3 @@ If you would like to contribute to the plugin (e.g. by improving the documentati
|
|||
## Author
|
||||
|
||||
This `flutter_chat` for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at <support@iconica.nl>
|
||||
````
|
||||
|
|
|
@ -9,3 +9,4 @@ export 'package:flutter_chat_interface/flutter_chat_interface.dart';
|
|||
export 'package:flutter_chat/src/routes.dart';
|
||||
export 'package:flutter_chat/src/models/chat_configuration.dart';
|
||||
export 'package:flutter_chat/src/flutter_chat_userstory.dart';
|
||||
export 'package:flutter_chat/src/flutter_chat_navigator_userstory.dart';
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
// SPDX-FileCopyrightText: 2023 Iconica
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_chat/flutter_chat.dart';
|
||||
|
||||
Widget chatNavigatorUserStory(
|
||||
ChatUserStoryConfiguration configuration, BuildContext context) {
|
||||
return _chatScreenRoute(configuration, context);
|
||||
}
|
||||
|
||||
Widget _chatScreenRoute(
|
||||
ChatUserStoryConfiguration configuration, BuildContext context) {
|
||||
return ChatScreen(
|
||||
service: configuration.chatService,
|
||||
options: configuration.chatOptionsBuilder(context),
|
||||
onNoChats: () async => await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => _newChatScreenRoute(
|
||||
configuration,
|
||||
context,
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressStartChat: () async {
|
||||
if (configuration.onPressStartChat != null) {
|
||||
return await configuration.onPressStartChat?.call();
|
||||
}
|
||||
|
||||
return await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => _newChatScreenRoute(
|
||||
configuration,
|
||||
context,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
onPressChat: (chat) async =>
|
||||
configuration.onPressChat?.call(context, chat) ??
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => _chatDetailScreenRoute(
|
||||
configuration,
|
||||
context,
|
||||
chat.id!,
|
||||
),
|
||||
),
|
||||
),
|
||||
onDeleteChat: (chat) =>
|
||||
configuration.onDeleteChat?.call(context, chat) ??
|
||||
configuration.chatService.chatOverviewService.deleteChat(chat),
|
||||
deleteChatDialog: configuration.deleteChatDialog,
|
||||
translations: configuration.translations,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _chatDetailScreenRoute(ChatUserStoryConfiguration configuration,
|
||||
BuildContext context, String chatId) {
|
||||
return ChatDetailScreen(
|
||||
pageSize: configuration.messagePageSize,
|
||||
options: configuration.chatOptionsBuilder(context),
|
||||
translations: configuration.translations,
|
||||
service: configuration.chatService,
|
||||
chatId: chatId,
|
||||
onMessageSubmit: (message) async {
|
||||
configuration.onMessageSubmit?.call(message) ??
|
||||
configuration.chatService.chatDetailService
|
||||
.sendTextMessage(chatId: chatId, text: message);
|
||||
configuration.afterMessageSent?.call(chatId);
|
||||
},
|
||||
onUploadImage: (image) async {
|
||||
configuration.onUploadImage?.call(image) ??
|
||||
configuration.chatService.chatDetailService
|
||||
.sendImageMessage(chatId: chatId, image: image);
|
||||
configuration.afterMessageSent?.call(chatId);
|
||||
},
|
||||
onReadChat: (chat) =>
|
||||
configuration.onReadChat?.call(chat) ??
|
||||
configuration.chatService.chatOverviewService.readChat(chat),
|
||||
onPressChatTitle: (context, chat) async {
|
||||
if (configuration.onPressChatTitle?.call(context, chat) != null) {
|
||||
return configuration.onPressChatTitle?.call(context, chat);
|
||||
}
|
||||
|
||||
return await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => _chatProfileScreenRoute(
|
||||
configuration,
|
||||
context,
|
||||
chatId,
|
||||
null,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
iconColor: configuration.iconColor,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _chatProfileScreenRoute(ChatUserStoryConfiguration configuration,
|
||||
BuildContext context, String chatId, String? userId) {
|
||||
return ChatProfileScreen(
|
||||
translations: configuration.translations,
|
||||
chatService: configuration.chatService,
|
||||
chatId: chatId,
|
||||
userId: userId,
|
||||
onTapUser: (user) async {
|
||||
if (configuration.onPressUserProfile != null) {
|
||||
return configuration.onPressUserProfile!.call();
|
||||
}
|
||||
|
||||
return Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => _chatProfileScreenRoute(
|
||||
configuration,
|
||||
context,
|
||||
chatId,
|
||||
userId,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _newChatScreenRoute(
|
||||
ChatUserStoryConfiguration configuration, BuildContext context) {
|
||||
return NewChatScreen(
|
||||
options: configuration.chatOptionsBuilder(context),
|
||||
translations: configuration.translations,
|
||||
service: configuration.chatService,
|
||||
onPressCreateChat: (user) async {
|
||||
configuration.onPressCreateChat?.call(user);
|
||||
if (configuration.onPressCreateChat != null) return;
|
||||
var chat = await configuration.chatService.chatOverviewService
|
||||
.getChatByUser(user);
|
||||
if (chat.id == null) {
|
||||
chat =
|
||||
await configuration.chatService.chatOverviewService.storeChatIfNot(
|
||||
PersonalChatModel(
|
||||
user: user,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (context.mounted) {
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => _chatDetailScreenRoute(
|
||||
configuration,
|
||||
context,
|
||||
chat.id!,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
|
@ -105,7 +105,7 @@ List<GoRoute> getChatStoryRoutes(
|
|||
service: configuration.chatService,
|
||||
onPressCreateChat: (user) async {
|
||||
configuration.onPressCreateChat?.call(user);
|
||||
if (configuration.onPressChat != null) return;
|
||||
if (configuration.onPressCreateChat != null) return;
|
||||
var chat = await configuration.chatService.chatOverviewService
|
||||
.getChatByUser(user);
|
||||
if (chat.id == null) {
|
||||
|
|
|
@ -12,7 +12,6 @@ class ChatUserStoryConfiguration {
|
|||
const ChatUserStoryConfiguration({
|
||||
required this.chatService,
|
||||
required this.chatOptionsBuilder,
|
||||
this.pageSize = 10,
|
||||
this.onPressStartChat,
|
||||
this.onPressChat,
|
||||
this.onDeleteChat,
|
||||
|
@ -47,7 +46,6 @@ class ChatUserStoryConfiguration {
|
|||
|
||||
/// If true, the user will be routed to the new chat screen if there are no chats.
|
||||
final bool routeToNewChatIfEmpty;
|
||||
final int pageSize;
|
||||
final int messagePageSize;
|
||||
|
||||
final Future<bool?> Function(BuildContext, ChatModel)? deleteChatDialog;
|
||||
|
|
|
@ -163,8 +163,22 @@ class FirebaseChatOverviewService implements ChatOverviewService {
|
|||
for (ChatModel chatModel in chats) {
|
||||
if (uniqueIds.add(chatModel.id!)) {
|
||||
uniqueChatModels.add(chatModel);
|
||||
} else {
|
||||
var index = uniqueChatModels.indexWhere(
|
||||
(element) => element.id == chatModel.id,
|
||||
);
|
||||
if (index != -1) {
|
||||
if (chatModel.lastUsed != null &&
|
||||
uniqueChatModels[index].lastUsed != null) {
|
||||
if (chatModel.lastUsed!
|
||||
.isAfter(uniqueChatModels[index].lastUsed!)) {
|
||||
uniqueChatModels[index] = chatModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uniqueChatModels.sort(
|
||||
(a, b) => (b.lastUsed ?? DateTime.now()).compareTo(
|
||||
a.lastUsed ?? DateTime.now(),
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter_chat_firebase/config/firebase_chat_options.dart';
|
||||
import 'package:flutter_chat_firebase/flutter_chat_firebase.dart';
|
||||
import 'package:flutter_chat_interface/flutter_chat_interface.dart';
|
||||
|
||||
class FirebaseChatService implements ChatService {
|
||||
FirebaseChatService({
|
||||
this.options,
|
||||
this.app,
|
||||
this.firebaseChatDetailService,
|
||||
this.firebaseChatOverviewService,
|
||||
this.firebaseChatUserService,
|
||||
}) {
|
||||
firebaseChatDetailService ??= FirebaseChatDetailService(
|
||||
userService: chatUserService,
|
||||
options: options,
|
||||
app: app,
|
||||
);
|
||||
|
||||
firebaseChatOverviewService ??= FirebaseChatOverviewService(
|
||||
userService: chatUserService,
|
||||
options: options,
|
||||
app: app,
|
||||
);
|
||||
|
||||
firebaseChatUserService ??= FirebaseChatUserService(
|
||||
options: options,
|
||||
app: app,
|
||||
);
|
||||
}
|
||||
|
||||
final FirebaseChatOptions? options;
|
||||
final FirebaseApp? app;
|
||||
ChatDetailService? firebaseChatDetailService;
|
||||
ChatOverviewService? firebaseChatOverviewService;
|
||||
ChatUserService? firebaseChatUserService;
|
||||
|
||||
@override
|
||||
ChatDetailService get chatDetailService {
|
||||
if (firebaseChatDetailService != null) {
|
||||
return firebaseChatDetailService!;
|
||||
} else {
|
||||
return FirebaseChatDetailService(
|
||||
userService: chatUserService,
|
||||
options: options,
|
||||
app: app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ChatOverviewService get chatOverviewService {
|
||||
if (firebaseChatOverviewService != null) {
|
||||
return firebaseChatOverviewService!;
|
||||
} else {
|
||||
return FirebaseChatOverviewService(
|
||||
userService: chatUserService,
|
||||
options: options,
|
||||
app: app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ChatUserService get chatUserService {
|
||||
if (firebaseChatUserService != null) {
|
||||
return firebaseChatUserService!;
|
||||
} else {
|
||||
return FirebaseChatUserService(
|
||||
options: options,
|
||||
app: app,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
export 'package:flutter_chat_firebase/service/firebase_chat_user_service.dart';
|
||||
export 'package:flutter_chat_firebase/service/firebase_chat_detail_service.dart';
|
||||
export 'package:flutter_chat_firebase/service/firebase_chat_overview_service.dart';
|
||||
export 'package:flutter_chat_firebase/service/firebase_chat_service.dart';
|
||||
|
|
|
@ -17,6 +17,7 @@ class ChatProfileService extends ProfileService {
|
|||
@override
|
||||
FutureOr<void> uploadImage(
|
||||
BuildContext context, {
|
||||
// ignore: avoid_positional_boolean_parameters
|
||||
required Function(bool isUploading) onUploadStateChanged,
|
||||
}) {
|
||||
throw UnimplementedError();
|
||||
|
|
Loading…
Reference in a new issue