mirror of
https://github.com/Iconica-Development/flutter_chat.git
synced 2025-05-19 10:53:51 +02:00
Merge pull request #51 from Iconica-Development/update-component-documentation-workflow-correct
Add component-documentation.yml correct
This commit is contained in:
commit
5ae3295a8d
42 changed files with 674 additions and 823 deletions
14
.github/workflows/component-documentation.yml
vendored
Normal file
14
.github/workflows/component-documentation.yml
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
name: Iconica Standard Component Documentation Workflow
|
||||||
|
# Workflow Caller version: 1.0.0
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
call-iconica-component-documentation-workflow:
|
||||||
|
uses: Iconica-Development/.github/.github/workflows/component-documentation.yml@master
|
||||||
|
secrets: inherit
|
||||||
|
permissions: write-all
|
|
@ -1,3 +1,7 @@
|
||||||
|
## 1.2.0
|
||||||
|
|
||||||
|
- Added linter and workflow
|
||||||
|
|
||||||
## 1.1.0
|
## 1.1.0
|
||||||
|
|
||||||
- Added LocalChatService for example app
|
- Added LocalChatService for example app
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Possible to overwrite the rules from the package
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
|
|
@ -16,8 +16,6 @@ dependencies:
|
||||||
path: ../
|
path: ../
|
||||||
flutter_chat_firebase:
|
flutter_chat_firebase:
|
||||||
path: ../../flutter_chat_firebase
|
path: ../../flutter_chat_firebase
|
||||||
flutter_chat_local:
|
|
||||||
path: ../../flutter_chat_local
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Iconica
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
///
|
||||||
library flutter_chat;
|
library flutter_chat;
|
||||||
|
|
||||||
export 'package:flutter_chat/src/chat_entry_widget.dart';
|
export 'package:flutter_chat/src/chat_entry_widget.dart';
|
||||||
|
@ -10,4 +10,5 @@ export 'package:flutter_chat/src/flutter_chat_userstory.dart';
|
||||||
export 'package:flutter_chat/src/models/chat_configuration.dart';
|
export 'package:flutter_chat/src/models/chat_configuration.dart';
|
||||||
export 'package:flutter_chat/src/routes.dart';
|
export 'package:flutter_chat/src/routes.dart';
|
||||||
export 'package:flutter_chat_interface/flutter_chat_interface.dart';
|
export 'package:flutter_chat_interface/flutter_chat_interface.dart';
|
||||||
|
export 'package:flutter_chat_local/local_chat_service.dart';
|
||||||
export 'package:flutter_chat_view/flutter_chat_view.dart';
|
export 'package:flutter_chat_view/flutter_chat_view.dart';
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_chat/flutter_chat.dart';
|
import 'package:flutter_chat/flutter_chat.dart';
|
||||||
import 'package:flutter_chat_local/local_chat_service.dart';
|
|
||||||
|
|
||||||
class ChatEntryWidget extends StatefulWidget {
|
class ChatEntryWidget extends StatefulWidget {
|
||||||
const ChatEntryWidget({
|
const ChatEntryWidget({
|
||||||
|
@ -40,25 +39,23 @@ class _ChatEntryWidgetState extends State<ChatEntryWidget> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => GestureDetector(
|
||||||
return GestureDetector(
|
onTap: () async =>
|
||||||
onTap: () =>
|
widget.onTap?.call() ??
|
||||||
widget.onTap?.call() ??
|
Navigator.of(context).pushReplacement(
|
||||||
Navigator.of(context).pushReplacement(
|
MaterialPageRoute(
|
||||||
MaterialPageRoute(
|
builder: (context) => chatNavigatorUserStory(
|
||||||
builder: (context) => chatNavigatorUserStory(
|
context,
|
||||||
context,
|
configuration: ChatUserStoryConfiguration(
|
||||||
configuration: ChatUserStoryConfiguration(
|
chatService: chatService!,
|
||||||
chatService: chatService!,
|
chatOptionsBuilder: (ctx) => const ChatOptions(),
|
||||||
chatOptionsBuilder: (ctx) => const ChatOptions(),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
child: StreamBuilder<int>(
|
||||||
child: StreamBuilder<int>(
|
stream: chatService!.chatOverviewService.getUnreadChatsCountStream(),
|
||||||
stream: chatService!.chatOverviewService.getUnreadChatsCountStream(),
|
builder: (BuildContext context, snapshot) => Stack(
|
||||||
builder: (BuildContext context, snapshot) {
|
|
||||||
return Stack(
|
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
|
@ -96,11 +93,9 @@ class _ChatEntryWidgetState extends State<ChatEntryWidget> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AnimatedNotificationIcon extends StatefulWidget {
|
class _AnimatedNotificationIcon extends StatefulWidget {
|
||||||
|
@ -145,7 +140,7 @@ class _AnimatedNotificationIconState extends State<_AnimatedNotificationIcon>
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
if (oldWidget.notifications != widget.notifications) {
|
if (oldWidget.notifications != widget.notifications) {
|
||||||
_runAnimation();
|
unawaited(_runAnimation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,12 +150,10 @@ class _AnimatedNotificationIconState extends State<_AnimatedNotificationIcon>
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => RotationTransition(
|
||||||
return RotationTransition(
|
turns: Tween(begin: 0.0, end: -.1)
|
||||||
turns: Tween(begin: 0.0, end: -.1)
|
.chain(CurveTween(curve: Curves.elasticIn))
|
||||||
.chain(CurveTween(curve: Curves.elasticIn))
|
.animate(_animationController),
|
||||||
.animate(_animationController),
|
child: widget.icon,
|
||||||
child: widget.icon,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,167 +4,180 @@
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_chat/flutter_chat.dart';
|
import 'package:flutter_chat/flutter_chat.dart';
|
||||||
import 'package:flutter_chat_local/service/local_chat_service.dart';
|
|
||||||
|
|
||||||
Widget chatNavigatorUserStory(
|
Widget chatNavigatorUserStory(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
ChatUserStoryConfiguration? configuration,
|
ChatUserStoryConfiguration? configuration,
|
||||||
}) {
|
}) =>
|
||||||
return _chatScreenRoute(
|
_chatScreenRoute(
|
||||||
configuration ??
|
configuration ??
|
||||||
ChatUserStoryConfiguration(
|
ChatUserStoryConfiguration(
|
||||||
chatService: LocalChatService(),
|
chatService: LocalChatService(),
|
||||||
chatOptionsBuilder: (ctx) => const ChatOptions(),
|
chatOptionsBuilder: (ctx) => const ChatOptions(),
|
||||||
),
|
),
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
Widget _chatScreenRoute(
|
Widget _chatScreenRoute(
|
||||||
ChatUserStoryConfiguration configuration, BuildContext context) {
|
ChatUserStoryConfiguration configuration,
|
||||||
return ChatScreen(
|
BuildContext context,
|
||||||
service: configuration.chatService,
|
) =>
|
||||||
options: configuration.chatOptionsBuilder(context),
|
ChatScreen(
|
||||||
onNoChats: () async => await Navigator.of(context).push(
|
service: configuration.chatService,
|
||||||
MaterialPageRoute(
|
options: configuration.chatOptionsBuilder(context),
|
||||||
builder: (context) => _newChatScreenRoute(
|
onNoChats: () async => Navigator.of(context).push(
|
||||||
configuration,
|
|
||||||
context,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressStartChat: () async {
|
|
||||||
if (configuration.onPressStartChat != null) {
|
|
||||||
return await configuration.onPressStartChat?.call();
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => _newChatScreenRoute(
|
builder: (context) => _newChatScreenRoute(
|
||||||
configuration,
|
configuration,
|
||||||
context,
|
context,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
onPressStartChat: () async {
|
||||||
onPressChat: (chat) async =>
|
if (configuration.onPressStartChat != null) {
|
||||||
configuration.onPressChat?.call(context, chat) ??
|
return await configuration.onPressStartChat?.call();
|
||||||
await Navigator.of(context).push(
|
}
|
||||||
|
|
||||||
|
return Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => _chatDetailScreenRoute(
|
builder: (context) => _newChatScreenRoute(
|
||||||
configuration,
|
configuration,
|
||||||
context,
|
context,
|
||||||
chat.id!,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
onDeleteChat: (chat) =>
|
},
|
||||||
configuration.onDeleteChat?.call(context, chat) ??
|
onPressChat: (chat) async =>
|
||||||
configuration.chatService.chatOverviewService.deleteChat(chat),
|
configuration.onPressChat?.call(context, chat) ??
|
||||||
deleteChatDialog: configuration.deleteChatDialog,
|
await Navigator.of(context).push(
|
||||||
translations: configuration.translations,
|
MaterialPageRoute(
|
||||||
);
|
builder: (context) => _chatDetailScreenRoute(
|
||||||
}
|
configuration,
|
||||||
|
context,
|
||||||
|
chat.id!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onDeleteChat: (chat) async =>
|
||||||
|
configuration.onDeleteChat?.call(context, chat) ??
|
||||||
|
configuration.chatService.chatOverviewService.deleteChat(chat),
|
||||||
|
deleteChatDialog: configuration.deleteChatDialog,
|
||||||
|
translations: configuration.translations,
|
||||||
|
);
|
||||||
|
|
||||||
Widget _chatDetailScreenRoute(ChatUserStoryConfiguration configuration,
|
Widget _chatDetailScreenRoute(
|
||||||
BuildContext context, String chatId) {
|
ChatUserStoryConfiguration configuration,
|
||||||
return ChatDetailScreen(
|
BuildContext context,
|
||||||
pageSize: configuration.messagePageSize,
|
String chatId,
|
||||||
options: configuration.chatOptionsBuilder(context),
|
) =>
|
||||||
translations: configuration.translations,
|
ChatDetailScreen(
|
||||||
service: configuration.chatService,
|
pageSize: configuration.messagePageSize,
|
||||||
chatId: chatId,
|
options: configuration.chatOptionsBuilder(context),
|
||||||
onMessageSubmit: (message) async {
|
translations: configuration.translations,
|
||||||
configuration.onMessageSubmit?.call(message) ??
|
service: configuration.chatService,
|
||||||
configuration.chatService.chatDetailService
|
chatId: chatId,
|
||||||
|
onMessageSubmit: (message) async {
|
||||||
|
if (configuration.onMessageSubmit != null) {
|
||||||
|
await configuration.onMessageSubmit?.call(message);
|
||||||
|
} else {
|
||||||
|
await configuration.chatService.chatDetailService
|
||||||
.sendTextMessage(chatId: chatId, text: message);
|
.sendTextMessage(chatId: chatId, text: message);
|
||||||
configuration.afterMessageSent?.call(chatId);
|
}
|
||||||
},
|
|
||||||
onUploadImage: (image) async {
|
configuration.afterMessageSent?.call(chatId);
|
||||||
configuration.onUploadImage?.call(image) ??
|
},
|
||||||
configuration.chatService.chatDetailService
|
onUploadImage: (image) async {
|
||||||
|
if (configuration.onUploadImage != null) {
|
||||||
|
await configuration.onUploadImage?.call(image);
|
||||||
|
} else {
|
||||||
|
await configuration.chatService.chatDetailService
|
||||||
.sendImageMessage(chatId: chatId, image: image);
|
.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(
|
configuration.afterMessageSent?.call(chatId);
|
||||||
MaterialPageRoute(
|
},
|
||||||
builder: (context) => _chatProfileScreenRoute(
|
onReadChat: (chat) async =>
|
||||||
configuration,
|
configuration.onReadChat?.call(chat) ??
|
||||||
context,
|
configuration.chatService.chatOverviewService.readChat(chat),
|
||||||
chatId,
|
onPressChatTitle: (context, chat) async {
|
||||||
null,
|
if (configuration.onPressChatTitle?.call(context, chat) != null) {
|
||||||
|
return configuration.onPressChatTitle?.call(context, chat);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => _chatProfileScreenRoute(
|
||||||
|
configuration,
|
||||||
|
context,
|
||||||
|
chatId,
|
||||||
|
null,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
},
|
||||||
},
|
iconColor: configuration.iconColor,
|
||||||
iconColor: configuration.iconColor,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _chatProfileScreenRoute(ChatUserStoryConfiguration configuration,
|
Widget _chatProfileScreenRoute(
|
||||||
BuildContext context, String chatId, String? userId) {
|
ChatUserStoryConfiguration configuration,
|
||||||
return ChatProfileScreen(
|
BuildContext context,
|
||||||
translations: configuration.translations,
|
String chatId,
|
||||||
chatService: configuration.chatService,
|
String? userId,
|
||||||
chatId: chatId,
|
) =>
|
||||||
userId: userId,
|
ChatProfileScreen(
|
||||||
onTapUser: (user) async {
|
translations: configuration.translations,
|
||||||
if (configuration.onPressUserProfile != null) {
|
chatService: configuration.chatService,
|
||||||
return configuration.onPressUserProfile!.call();
|
chatId: chatId,
|
||||||
}
|
userId: userId,
|
||||||
|
onTapUser: (user) async {
|
||||||
|
if (configuration.onPressUserProfile != null) {
|
||||||
|
return configuration.onPressUserProfile!.call();
|
||||||
|
}
|
||||||
|
|
||||||
return Navigator.of(context).push(
|
return Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => _chatProfileScreenRoute(
|
builder: (context) => _chatProfileScreenRoute(
|
||||||
configuration,
|
configuration,
|
||||||
context,
|
context,
|
||||||
chatId,
|
chatId,
|
||||||
userId,
|
userId,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
},
|
||||||
},
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _newChatScreenRoute(
|
Widget _newChatScreenRoute(
|
||||||
ChatUserStoryConfiguration configuration, BuildContext context) {
|
ChatUserStoryConfiguration configuration,
|
||||||
return NewChatScreen(
|
BuildContext context,
|
||||||
options: configuration.chatOptionsBuilder(context),
|
) =>
|
||||||
translations: configuration.translations,
|
NewChatScreen(
|
||||||
service: configuration.chatService,
|
options: configuration.chatOptionsBuilder(context),
|
||||||
onPressCreateChat: (user) async {
|
translations: configuration.translations,
|
||||||
configuration.onPressCreateChat?.call(user);
|
service: configuration.chatService,
|
||||||
if (configuration.onPressCreateChat != null) return;
|
onPressCreateChat: (user) async {
|
||||||
var chat = await configuration.chatService.chatOverviewService
|
configuration.onPressCreateChat?.call(user);
|
||||||
.getChatByUser(user);
|
if (configuration.onPressCreateChat != null) return;
|
||||||
if (chat.id == null) {
|
var chat = await configuration.chatService.chatOverviewService
|
||||||
chat =
|
.getChatByUser(user);
|
||||||
await configuration.chatService.chatOverviewService.storeChatIfNot(
|
if (chat.id == null) {
|
||||||
PersonalChatModel(
|
chat = await configuration.chatService.chatOverviewService
|
||||||
user: user,
|
.storeChatIfNot(
|
||||||
),
|
PersonalChatModel(
|
||||||
);
|
user: user,
|
||||||
}
|
|
||||||
if (context.mounted) {
|
|
||||||
await Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => _chatDetailScreenRoute(
|
|
||||||
configuration,
|
|
||||||
context,
|
|
||||||
chat.id!,
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
}
|
||||||
}
|
if (context.mounted) {
|
||||||
},
|
await Navigator.of(context).push(
|
||||||
);
|
MaterialPageRoute(
|
||||||
}
|
builder: (context) => _chatDetailScreenRoute(
|
||||||
|
configuration,
|
||||||
|
context,
|
||||||
|
chat.id!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
|
@ -18,18 +18,18 @@ List<GoRoute> getChatStoryRoutes(
|
||||||
service: configuration.chatService,
|
service: configuration.chatService,
|
||||||
options: configuration.chatOptionsBuilder(context),
|
options: configuration.chatOptionsBuilder(context),
|
||||||
onNoChats: () async =>
|
onNoChats: () async =>
|
||||||
await context.push(ChatUserStoryRoutes.newChatScreen),
|
context.push(ChatUserStoryRoutes.newChatScreen),
|
||||||
onPressStartChat: () async {
|
onPressStartChat: () async {
|
||||||
if (configuration.onPressStartChat != null) {
|
if (configuration.onPressStartChat != null) {
|
||||||
return await configuration.onPressStartChat?.call();
|
return await configuration.onPressStartChat?.call();
|
||||||
}
|
}
|
||||||
|
|
||||||
return await context.push(ChatUserStoryRoutes.newChatScreen);
|
return context.push(ChatUserStoryRoutes.newChatScreen);
|
||||||
},
|
},
|
||||||
onPressChat: (chat) =>
|
onPressChat: (chat) async =>
|
||||||
configuration.onPressChat?.call(context, chat) ??
|
configuration.onPressChat?.call(context, chat) ??
|
||||||
context.push(ChatUserStoryRoutes.chatDetailViewPath(chat.id!)),
|
context.push(ChatUserStoryRoutes.chatDetailViewPath(chat.id!)),
|
||||||
onDeleteChat: (chat) =>
|
onDeleteChat: (chat) async =>
|
||||||
configuration.onDeleteChat?.call(context, chat) ??
|
configuration.onDeleteChat?.call(context, chat) ??
|
||||||
configuration.chatService.chatOverviewService.deleteChat(chat),
|
configuration.chatService.chatOverviewService.deleteChat(chat),
|
||||||
deleteChatDialog: configuration.deleteChatDialog,
|
deleteChatDialog: configuration.deleteChatDialog,
|
||||||
|
@ -59,27 +59,34 @@ List<GoRoute> getChatStoryRoutes(
|
||||||
service: configuration.chatService,
|
service: configuration.chatService,
|
||||||
chatId: chatId!,
|
chatId: chatId!,
|
||||||
onMessageSubmit: (message) async {
|
onMessageSubmit: (message) async {
|
||||||
configuration.onMessageSubmit?.call(message) ??
|
if (configuration.onMessageSubmit != null) {
|
||||||
configuration.chatService.chatDetailService
|
await configuration.onMessageSubmit?.call(message);
|
||||||
.sendTextMessage(chatId: chatId, text: message);
|
} else {
|
||||||
|
await configuration.chatService.chatDetailService
|
||||||
|
.sendTextMessage(chatId: chatId, text: message);
|
||||||
|
}
|
||||||
configuration.afterMessageSent?.call(chatId);
|
configuration.afterMessageSent?.call(chatId);
|
||||||
},
|
},
|
||||||
onUploadImage: (image) async {
|
onUploadImage: (image) async {
|
||||||
configuration.onUploadImage?.call(image) ??
|
if (configuration.onUploadImage?.call(image) != null) {
|
||||||
configuration.chatService.chatDetailService
|
await configuration.onUploadImage?.call(image);
|
||||||
.sendImageMessage(chatId: chatId, image: image);
|
} else {
|
||||||
|
await configuration.chatService.chatDetailService
|
||||||
|
.sendImageMessage(chatId: chatId, image: image);
|
||||||
|
}
|
||||||
configuration.afterMessageSent?.call(chatId);
|
configuration.afterMessageSent?.call(chatId);
|
||||||
},
|
},
|
||||||
onReadChat: (chat) =>
|
onReadChat: (chat) async =>
|
||||||
configuration.onReadChat?.call(chat) ??
|
configuration.onReadChat?.call(chat) ??
|
||||||
configuration.chatService.chatOverviewService.readChat(chat),
|
configuration.chatService.chatOverviewService.readChat(chat),
|
||||||
onPressChatTitle: (context, chat) {
|
onPressChatTitle: (context, chat) async {
|
||||||
if (configuration.onPressChatTitle?.call(context, chat) != null) {
|
if (configuration.onPressChatTitle?.call(context, chat) != null) {
|
||||||
return configuration.onPressChatTitle?.call(context, chat);
|
return configuration.onPressChatTitle?.call(context, chat);
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.push(
|
return context.push(
|
||||||
ChatUserStoryRoutes.chatProfileScreenPath(chat.id!, null));
|
ChatUserStoryRoutes.chatProfileScreenPath(chat.id!, null),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
iconColor: configuration.iconColor,
|
iconColor: configuration.iconColor,
|
||||||
);
|
);
|
||||||
|
@ -100,27 +107,29 @@ List<GoRoute> getChatStoryRoutes(
|
||||||
path: ChatUserStoryRoutes.newChatScreen,
|
path: ChatUserStoryRoutes.newChatScreen,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
var newChatScreen = NewChatScreen(
|
var newChatScreen = NewChatScreen(
|
||||||
options: configuration.chatOptionsBuilder(context),
|
options: configuration.chatOptionsBuilder(context),
|
||||||
translations: configuration.translations,
|
translations: configuration.translations,
|
||||||
service: configuration.chatService,
|
service: configuration.chatService,
|
||||||
onPressCreateChat: (user) async {
|
onPressCreateChat: (user) async {
|
||||||
configuration.onPressCreateChat?.call(user);
|
configuration.onPressCreateChat?.call(user);
|
||||||
if (configuration.onPressCreateChat != null) return;
|
if (configuration.onPressCreateChat != null) return;
|
||||||
var chat = await configuration.chatService.chatOverviewService
|
var chat = await configuration.chatService.chatOverviewService
|
||||||
.getChatByUser(user);
|
.getChatByUser(user);
|
||||||
if (chat.id == null) {
|
if (chat.id == null) {
|
||||||
chat = await configuration.chatService.chatOverviewService
|
chat = await configuration.chatService.chatOverviewService
|
||||||
.storeChatIfNot(
|
.storeChatIfNot(
|
||||||
PersonalChatModel(
|
PersonalChatModel(
|
||||||
user: user,
|
user: user,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
await context.push(
|
await context.push(
|
||||||
ChatUserStoryRoutes.chatDetailViewPath(chat.id ?? ''));
|
ChatUserStoryRoutes.chatDetailViewPath(chat.id ?? ''),
|
||||||
}
|
);
|
||||||
});
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
return buildScreenWithoutTransition(
|
return buildScreenWithoutTransition(
|
||||||
context: context,
|
context: context,
|
||||||
state: state,
|
state: state,
|
||||||
|
@ -150,7 +159,7 @@ List<GoRoute> getChatStoryRoutes(
|
||||||
return configuration.onPressUserProfile!.call();
|
return configuration.onPressUserProfile!.call();
|
||||||
}
|
}
|
||||||
|
|
||||||
return await context.push(
|
return context.push(
|
||||||
ChatUserStoryRoutes.chatProfileScreenPath(chatId, user),
|
ChatUserStoryRoutes.chatProfileScreenPath(chatId, user),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -38,13 +38,15 @@ class ChatUserStoryConfiguration {
|
||||||
final Future<void> Function(Uint8List image)? onUploadImage;
|
final Future<void> Function(Uint8List image)? onUploadImage;
|
||||||
final Future<void> Function(String text)? onMessageSubmit;
|
final Future<void> Function(String text)? onMessageSubmit;
|
||||||
|
|
||||||
/// Called after a new message is sent. This can be used to do something extra like sending a push notification.
|
/// Called after a new message is sent. This can be used to do something
|
||||||
|
/// extra like sending a push notification.
|
||||||
final Function(String chatId)? afterMessageSent;
|
final Function(String chatId)? afterMessageSent;
|
||||||
final Future<void> Function(ChatModel chat)? onReadChat;
|
final Future<void> Function(ChatModel chat)? onReadChat;
|
||||||
final Function(ChatUserModel)? onPressCreateChat;
|
final Function(ChatUserModel)? onPressCreateChat;
|
||||||
final ChatOptions Function(BuildContext context) chatOptionsBuilder;
|
final ChatOptions Function(BuildContext context) chatOptionsBuilder;
|
||||||
|
|
||||||
/// If true, the user will be routed to the new chat screen if there are no chats.
|
/// If true, the user will be routed to the new chat screen if there are
|
||||||
|
/// no chats.
|
||||||
final bool routeToNewChatIfEmpty;
|
final bool routeToNewChatIfEmpty;
|
||||||
final int messagePageSize;
|
final int messagePageSize;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
name: flutter_chat
|
name: flutter_chat
|
||||||
description: A new Flutter package project.
|
description: A new Flutter package project.
|
||||||
version: 1.1.0
|
version: 1.2.0
|
||||||
|
|
||||||
publish_to: none
|
publish_to: none
|
||||||
|
|
||||||
|
@ -20,19 +20,22 @@ 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: 1.1.0
|
ref: 1.2.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: 1.1.0
|
ref: 1.2.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: 1.1.0
|
ref: 1.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^2.0.0
|
flutter_iconica_analysis:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||||
|
ref: 6.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Possible to overwrite the rules from the package
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
|
|
@ -31,18 +31,17 @@ class FirebaseChatOptions {
|
||||||
String? usersCollectionName,
|
String? usersCollectionName,
|
||||||
String? chatsMetaDataCollectionName,
|
String? chatsMetaDataCollectionName,
|
||||||
String? userChatsCollectionName,
|
String? userChatsCollectionName,
|
||||||
}) {
|
}) =>
|
||||||
return FirebaseChatOptions(
|
FirebaseChatOptions(
|
||||||
groupChatsCollectionName:
|
groupChatsCollectionName:
|
||||||
groupChatsCollectionName ?? this.groupChatsCollectionName,
|
groupChatsCollectionName ?? this.groupChatsCollectionName,
|
||||||
chatsCollectionName: chatsCollectionName ?? this.chatsCollectionName,
|
chatsCollectionName: chatsCollectionName ?? this.chatsCollectionName,
|
||||||
messagesCollectionName:
|
messagesCollectionName:
|
||||||
messagesCollectionName ?? this.messagesCollectionName,
|
messagesCollectionName ?? this.messagesCollectionName,
|
||||||
usersCollectionName: usersCollectionName ?? this.usersCollectionName,
|
usersCollectionName: usersCollectionName ?? this.usersCollectionName,
|
||||||
chatsMetaDataCollectionName:
|
chatsMetaDataCollectionName:
|
||||||
chatsMetaDataCollectionName ?? this.chatsMetaDataCollectionName,
|
chatsMetaDataCollectionName ?? this.chatsMetaDataCollectionName,
|
||||||
userChatsCollectionName:
|
userChatsCollectionName:
|
||||||
userChatsCollectionName ?? this.userChatsCollectionName,
|
userChatsCollectionName ?? this.userChatsCollectionName,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,15 +19,6 @@ class FirebaseChatDocument {
|
||||||
this.lastMessage,
|
this.lastMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String? id;
|
|
||||||
final String? title;
|
|
||||||
final String? imageUrl;
|
|
||||||
final bool personal;
|
|
||||||
final bool canBeDeleted;
|
|
||||||
final Timestamp? lastUsed;
|
|
||||||
final List<String> users;
|
|
||||||
final FirebaseMessageDocument? lastMessage;
|
|
||||||
|
|
||||||
FirebaseChatDocument.fromJson(Map<String, dynamic> json, this.id)
|
FirebaseChatDocument.fromJson(Map<String, dynamic> json, this.id)
|
||||||
: title = json['title'],
|
: title = json['title'],
|
||||||
imageUrl = json['image_url'],
|
imageUrl = json['image_url'],
|
||||||
|
@ -42,6 +33,15 @@ class FirebaseChatDocument {
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final String? id;
|
||||||
|
final String? title;
|
||||||
|
final String? imageUrl;
|
||||||
|
final bool personal;
|
||||||
|
final bool canBeDeleted;
|
||||||
|
final Timestamp? lastUsed;
|
||||||
|
final List<String> users;
|
||||||
|
final FirebaseMessageDocument? lastMessage;
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
'title': title,
|
'title': title,
|
||||||
'image_url': imageUrl,
|
'image_url': imageUrl,
|
||||||
|
|
|
@ -15,18 +15,18 @@ class FirebaseMessageDocument {
|
||||||
this.imageUrl,
|
this.imageUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String? id;
|
|
||||||
final String sender;
|
|
||||||
final String? text;
|
|
||||||
final String? imageUrl;
|
|
||||||
final Timestamp timestamp;
|
|
||||||
|
|
||||||
FirebaseMessageDocument.fromJson(Map<String, dynamic> json, this.id)
|
FirebaseMessageDocument.fromJson(Map<String, dynamic> json, this.id)
|
||||||
: sender = json['sender'],
|
: sender = json['sender'],
|
||||||
text = json['text'],
|
text = json['text'],
|
||||||
imageUrl = json['image_url'],
|
imageUrl = json['image_url'],
|
||||||
timestamp = json['timestamp'];
|
timestamp = json['timestamp'];
|
||||||
|
|
||||||
|
final String? id;
|
||||||
|
final String sender;
|
||||||
|
final String? text;
|
||||||
|
final String? imageUrl;
|
||||||
|
final Timestamp timestamp;
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
'sender': sender,
|
'sender': sender,
|
||||||
'text': text,
|
'text': text,
|
||||||
|
|
|
@ -13,28 +13,27 @@ class FirebaseUserDocument {
|
||||||
this.id,
|
this.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
FirebaseUserDocument.fromJson(
|
||||||
|
Map<String, Object?> json,
|
||||||
|
String id,
|
||||||
|
) : this(
|
||||||
|
id: id,
|
||||||
|
firstName:
|
||||||
|
json['first_name'] == null ? '' : json['first_name']! as String,
|
||||||
|
lastName:
|
||||||
|
json['last_name'] == null ? '' : json['last_name']! as String,
|
||||||
|
imageUrl:
|
||||||
|
json['image_url'] == null ? null : json['image_url']! as String,
|
||||||
|
);
|
||||||
|
|
||||||
final String? firstName;
|
final String? firstName;
|
||||||
final String? lastName;
|
final String? lastName;
|
||||||
final String? imageUrl;
|
final String? imageUrl;
|
||||||
final String? id;
|
final String? id;
|
||||||
|
|
||||||
FirebaseUserDocument.fromJson(
|
Map<String, Object?> toJson() => {
|
||||||
Map<String, Object?> json,
|
'first_name': firstName,
|
||||||
String id,
|
'last_name': lastName,
|
||||||
) : this(
|
'image_url': imageUrl,
|
||||||
id: id,
|
};
|
||||||
firstName:
|
|
||||||
json['first_name'] == null ? '' : json['first_name'] as String,
|
|
||||||
lastName:
|
|
||||||
json['last_name'] == null ? '' : json['last_name'] as String,
|
|
||||||
imageUrl:
|
|
||||||
json['image_url'] == null ? null : json['image_url'] as String);
|
|
||||||
|
|
||||||
Map<String, Object?> toJson() {
|
|
||||||
return {
|
|
||||||
'first_name': firstName,
|
|
||||||
'last_name': lastName,
|
|
||||||
'image_url': imageUrl,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Iconica
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
///
|
||||||
library flutter_chat_firebase;
|
library flutter_chat_firebase;
|
||||||
|
|
||||||
export 'package:flutter_chat_firebase/service/service.dart';
|
export 'package:flutter_chat_firebase/service/service.dart';
|
||||||
|
|
|
@ -16,6 +16,18 @@ import 'package:uuid/uuid.dart';
|
||||||
class FirebaseChatDetailService
|
class FirebaseChatDetailService
|
||||||
with ChangeNotifier
|
with ChangeNotifier
|
||||||
implements ChatDetailService {
|
implements ChatDetailService {
|
||||||
|
FirebaseChatDetailService({
|
||||||
|
required ChatUserService userService,
|
||||||
|
FirebaseApp? app,
|
||||||
|
FirebaseChatOptions? options,
|
||||||
|
}) {
|
||||||
|
var appInstance = app ?? Firebase.app();
|
||||||
|
|
||||||
|
_db = FirebaseFirestore.instanceFor(app: appInstance);
|
||||||
|
_storage = FirebaseStorage.instanceFor(app: appInstance);
|
||||||
|
_userService = userService;
|
||||||
|
_options = options ?? const FirebaseChatOptions();
|
||||||
|
}
|
||||||
late final FirebaseFirestore _db;
|
late final FirebaseFirestore _db;
|
||||||
late final FirebaseStorage _storage;
|
late final FirebaseStorage _storage;
|
||||||
late final ChatUserService _userService;
|
late final ChatUserService _userService;
|
||||||
|
@ -29,19 +41,6 @@ class FirebaseChatDetailService
|
||||||
int? chatPageSize;
|
int? chatPageSize;
|
||||||
DateTime timestampToFilter = DateTime.now();
|
DateTime timestampToFilter = DateTime.now();
|
||||||
|
|
||||||
FirebaseChatDetailService({
|
|
||||||
required ChatUserService userService,
|
|
||||||
FirebaseApp? app,
|
|
||||||
FirebaseChatOptions? options,
|
|
||||||
}) {
|
|
||||||
var appInstance = app ?? Firebase.app();
|
|
||||||
|
|
||||||
_db = FirebaseFirestore.instanceFor(app: appInstance);
|
|
||||||
_storage = FirebaseStorage.instanceFor(app: appInstance);
|
|
||||||
_userService = userService;
|
|
||||||
_options = options ?? const FirebaseChatOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _sendMessage(String chatId, Map<String, dynamic> data) async {
|
Future<void> _sendMessage(String chatId, Map<String, dynamic> data) async {
|
||||||
var currentUser = await _userService.getCurrentUser();
|
var currentUser = await _userService.getCurrentUser();
|
||||||
|
|
||||||
|
@ -52,7 +51,7 @@ class FirebaseChatDetailService
|
||||||
var message = {
|
var message = {
|
||||||
'sender': currentUser.id,
|
'sender': currentUser.id,
|
||||||
'timestamp': DateTime.now(),
|
'timestamp': DateTime.now(),
|
||||||
...data
|
...data,
|
||||||
};
|
};
|
||||||
|
|
||||||
var chatReference = _db
|
var chatReference = _db
|
||||||
|
@ -89,7 +88,8 @@ class FirebaseChatDetailService
|
||||||
|
|
||||||
// update the chat counter for the other users
|
// update the chat counter for the other users
|
||||||
// get all users from the chat
|
// get all users from the chat
|
||||||
// there is a field in the chat document called users that has a list of user ids
|
// there is a field in the chat document called users that has a
|
||||||
|
// list of user ids
|
||||||
var fetchedChat = await metadataReference.get();
|
var fetchedChat = await metadataReference.get();
|
||||||
var chatUsers = fetchedChat.data()?['users'] as List<dynamic>;
|
var chatUsers = fetchedChat.data()?['users'] as List<dynamic>;
|
||||||
// for all users except the message sender update the unread counter
|
// for all users except the message sender update the unread counter
|
||||||
|
@ -112,9 +112,12 @@ class FirebaseChatDetailService
|
||||||
'amount_unread_messages': FieldValue.increment(1),
|
'amount_unread_messages': FieldValue.increment(1),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await userReference.set({
|
await userReference.set(
|
||||||
'amount_unread_messages': 1,
|
{
|
||||||
}, SetOptions(merge: true));
|
'amount_unread_messages': 1,
|
||||||
|
},
|
||||||
|
SetOptions(merge: true),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,14 +127,13 @@ class FirebaseChatDetailService
|
||||||
Future<void> sendTextMessage({
|
Future<void> sendTextMessage({
|
||||||
required String text,
|
required String text,
|
||||||
required String chatId,
|
required String chatId,
|
||||||
}) {
|
}) =>
|
||||||
return _sendMessage(
|
_sendMessage(
|
||||||
chatId,
|
chatId,
|
||||||
{
|
{
|
||||||
'text': text,
|
'text': text,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> sendImageMessage({
|
Future<void> sendImageMessage({
|
||||||
|
@ -171,7 +173,9 @@ class FirebaseChatDetailService
|
||||||
)
|
)
|
||||||
.withConverter<FirebaseMessageDocument>(
|
.withConverter<FirebaseMessageDocument>(
|
||||||
fromFirestore: (snapshot, _) => FirebaseMessageDocument.fromJson(
|
fromFirestore: (snapshot, _) => FirebaseMessageDocument.fromJson(
|
||||||
snapshot.data()!, snapshot.id),
|
snapshot.data()!,
|
||||||
|
snapshot.id,
|
||||||
|
),
|
||||||
toFirestore: (user, _) => user.toJson(),
|
toFirestore: (user, _) => user.toJson(),
|
||||||
)
|
)
|
||||||
.snapshots();
|
.snapshots();
|
||||||
|
@ -181,7 +185,7 @@ class FirebaseChatDetailService
|
||||||
var data = message.doc.data();
|
var data = message.doc.data();
|
||||||
var sender = await _userService.getUser(data!.sender);
|
var sender = await _userService.getUser(data!.sender);
|
||||||
var timestamp = DateTime.fromMillisecondsSinceEpoch(
|
var timestamp = DateTime.fromMillisecondsSinceEpoch(
|
||||||
(data.timestamp).millisecondsSinceEpoch,
|
data.timestamp.millisecondsSinceEpoch,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (timestamp.isBefore(timestampToFilter)) {
|
if (timestamp.isBefore(timestampToFilter)) {
|
||||||
|
@ -206,16 +210,15 @@ class FirebaseChatDetailService
|
||||||
..._cumulativeMessages,
|
..._cumulativeMessages,
|
||||||
...messages,
|
...messages,
|
||||||
];
|
];
|
||||||
List<ChatMessageModel> uniqueObjects =
|
var uniqueObjects = _cumulativeMessages.toSet().toList();
|
||||||
_cumulativeMessages.toSet().toList();
|
|
||||||
_cumulativeMessages = uniqueObjects;
|
_cumulativeMessages = uniqueObjects;
|
||||||
_cumulativeMessages
|
_cumulativeMessages
|
||||||
.sort((a, b) => a.timestamp.compareTo(b.timestamp));
|
.sort((a, b) => a.timestamp.compareTo(b.timestamp));
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onCancel: () {
|
onCancel: () async {
|
||||||
_subscription?.cancel();
|
await _subscription?.cancel();
|
||||||
_subscription = null;
|
_subscription = null;
|
||||||
debugPrint('Canceling messages stream');
|
debugPrint('Canceling messages stream');
|
||||||
},
|
},
|
||||||
|
@ -225,10 +228,10 @@ class FirebaseChatDetailService
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void stopListeningForMessages() {
|
Future<void> stopListeningForMessages() async {
|
||||||
_subscription?.cancel();
|
await _subscription?.cancel();
|
||||||
_subscription = null;
|
_subscription = null;
|
||||||
_controller?.close();
|
await _controller?.close();
|
||||||
_controller = null;
|
_controller = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,8 +244,9 @@ class FirebaseChatDetailService
|
||||||
lastChat = chatId;
|
lastChat = chatId;
|
||||||
lastMessage = null;
|
lastMessage = null;
|
||||||
}
|
}
|
||||||
// get the x amount of last messages from the oldest message that is in cumulative messages and add that to the list
|
// get the x amount of last messages from the oldest message that is in
|
||||||
List<ChatMessageModel> messages = [];
|
// cumulative messages and add that to the list
|
||||||
|
var messages = <ChatMessageModel>[];
|
||||||
QuerySnapshot<FirebaseMessageDocument>? messagesQuerySnapshot;
|
QuerySnapshot<FirebaseMessageDocument>? messagesQuerySnapshot;
|
||||||
var query = _db
|
var query = _db
|
||||||
.collection(_options.chatsCollectionName)
|
.collection(_options.chatsCollectionName)
|
||||||
|
@ -275,14 +279,14 @@ class FirebaseChatDetailService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<FirebaseMessageDocument> messageDocuments = messagesQuerySnapshot.docs
|
var messageDocuments = messagesQuerySnapshot.docs
|
||||||
.map((QueryDocumentSnapshot<FirebaseMessageDocument> doc) => doc.data())
|
.map((QueryDocumentSnapshot<FirebaseMessageDocument> doc) => doc.data())
|
||||||
.toList();
|
.toList();
|
||||||
for (var message in messageDocuments) {
|
for (var message in messageDocuments) {
|
||||||
var sender = await _userService.getUser(message.sender);
|
var sender = await _userService.getUser(message.sender);
|
||||||
if (sender != null) {
|
if (sender != null) {
|
||||||
var timestamp = DateTime.fromMillisecondsSinceEpoch(
|
var timestamp = DateTime.fromMillisecondsSinceEpoch(
|
||||||
(message.timestamp).millisecondsSinceEpoch,
|
message.timestamp.millisecondsSinceEpoch,
|
||||||
);
|
);
|
||||||
|
|
||||||
messages.add(
|
messages.add(
|
||||||
|
@ -310,7 +314,5 @@ class FirebaseChatDetailService
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<ChatMessageModel> getMessages() {
|
List<ChatMessageModel> getMessages() => _cumulativeMessages;
|
||||||
return _cumulativeMessages;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ class FirebaseChatOverviewService implements ChatOverviewService {
|
||||||
@override
|
@override
|
||||||
Stream<List<ChatModel>> getChatsStream() {
|
Stream<List<ChatModel>> getChatsStream() {
|
||||||
StreamSubscription? chatSubscription;
|
StreamSubscription? chatSubscription;
|
||||||
|
// ignore: close_sinks
|
||||||
late StreamController<List<ChatModel>> controller;
|
late StreamController<List<ChatModel>> controller;
|
||||||
controller = StreamController(
|
controller = StreamController(
|
||||||
onListen: () async {
|
onListen: () async {
|
||||||
|
@ -68,11 +69,13 @@ class FirebaseChatOverviewService implements ChatOverviewService {
|
||||||
)
|
)
|
||||||
.withConverter(
|
.withConverter(
|
||||||
fromFirestore: (snapshot, _) => FirebaseChatDocument.fromJson(
|
fromFirestore: (snapshot, _) => FirebaseChatDocument.fromJson(
|
||||||
snapshot.data()!, snapshot.id),
|
snapshot.data()!,
|
||||||
|
snapshot.id,
|
||||||
|
),
|
||||||
toFirestore: (chat, _) => chat.toJson(),
|
toFirestore: (chat, _) => chat.toJson(),
|
||||||
)
|
)
|
||||||
.snapshots();
|
.snapshots();
|
||||||
List<ChatModel> chats = [];
|
var chats = <ChatModel>[];
|
||||||
ChatModel? chatModel;
|
ChatModel? chatModel;
|
||||||
|
|
||||||
chatSubscription = chatSnapshot.listen((event) async {
|
chatSubscription = chatSnapshot.listen((event) async {
|
||||||
|
@ -84,7 +87,7 @@ class FirebaseChatOverviewService implements ChatOverviewService {
|
||||||
(element) => element != currentUser?.id,
|
(element) => element != currentUser?.id,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
int? unread =
|
var unread =
|
||||||
await _addUnreadChatSubscription(chat.id!, currentUser!.id!);
|
await _addUnreadChatSubscription(chat.id!, currentUser!.id!);
|
||||||
|
|
||||||
if (chat.personal) {
|
if (chat.personal) {
|
||||||
|
@ -157,10 +160,10 @@ class FirebaseChatOverviewService implements ChatOverviewService {
|
||||||
}
|
}
|
||||||
chats.add(chatModel!);
|
chats.add(chatModel!);
|
||||||
}
|
}
|
||||||
Set<String> uniqueIds = <String>{};
|
var uniqueIds = <String>{};
|
||||||
List<ChatModel> uniqueChatModels = [];
|
var uniqueChatModels = <ChatModel>[];
|
||||||
|
|
||||||
for (ChatModel chatModel in chats) {
|
for (var chatModel in chats) {
|
||||||
if (uniqueIds.add(chatModel.id!)) {
|
if (uniqueIds.add(chatModel.id!)) {
|
||||||
uniqueChatModels.add(chatModel);
|
uniqueChatModels.add(chatModel);
|
||||||
} else {
|
} else {
|
||||||
|
@ -189,8 +192,8 @@ class FirebaseChatOverviewService implements ChatOverviewService {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onCancel: () {
|
onCancel: () async {
|
||||||
chatSubscription?.cancel();
|
await chatSubscription?.cancel();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return controller.stream;
|
return controller.stream;
|
||||||
|
@ -225,6 +228,7 @@ class FirebaseChatOverviewService implements ChatOverviewService {
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
if (chatCollection.exists && chatCollection.data()?['users'] != null) {
|
if (chatCollection.exists && chatCollection.data()?['users'] != null) {
|
||||||
|
// ignore: avoid_dynamic_calls
|
||||||
var otherUser = chatCollection.data()?['users'].firstWhere(
|
var otherUser = chatCollection.data()?['users'].firstWhere(
|
||||||
(element) => element != currentUser?.id,
|
(element) => element != currentUser?.id,
|
||||||
);
|
);
|
||||||
|
@ -278,7 +282,7 @@ class FirebaseChatOverviewService implements ChatOverviewService {
|
||||||
|
|
||||||
if (chatData != null) {
|
if (chatData != null) {
|
||||||
for (var userId in chatData.users) {
|
for (var userId in chatData.users) {
|
||||||
_db
|
await _db
|
||||||
.collection(_options.usersCollectionName)
|
.collection(_options.usersCollectionName)
|
||||||
.doc(userId)
|
.doc(userId)
|
||||||
.collection(_options.userChatsCollectionName)
|
.collection(_options.userChatsCollectionName)
|
||||||
|
@ -349,7 +353,7 @@ class FirebaseChatOverviewService implements ChatOverviewService {
|
||||||
return chat;
|
return chat;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> userIds = [
|
var userIds = <String>[
|
||||||
currentUser!.id!,
|
currentUser!.id!,
|
||||||
...chat.users.map((e) => e.id!),
|
...chat.users.map((e) => e.id!),
|
||||||
];
|
];
|
||||||
|
@ -390,8 +394,10 @@ class FirebaseChatOverviewService implements ChatOverviewService {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<int> getUnreadChatsCountStream() {
|
Stream<int> getUnreadChatsCountStream() {
|
||||||
// open a stream to the user's chats collection and listen to changes in this collection we will also add the amount of read chats
|
// open a stream to the user's chats collection and listen to changes in
|
||||||
|
// this collection we will also add the amount of read chats
|
||||||
StreamSubscription? unreadChatSubscription;
|
StreamSubscription? unreadChatSubscription;
|
||||||
|
// ignore: close_sinks
|
||||||
late StreamController<int> controller;
|
late StreamController<int> controller;
|
||||||
controller = StreamController(
|
controller = StreamController(
|
||||||
onListen: () async {
|
onListen: () async {
|
||||||
|
@ -403,17 +409,20 @@ class FirebaseChatOverviewService implements ChatOverviewService {
|
||||||
.snapshots();
|
.snapshots();
|
||||||
|
|
||||||
unreadChatSubscription = userSnapshot.listen((event) {
|
unreadChatSubscription = userSnapshot.listen((event) {
|
||||||
// every chat has a field called amount_unread_messages, combine all of these fields to get the total amount of unread messages
|
// every chat has a field called amount_unread_messages, combine all
|
||||||
|
// of these fields to get the total amount of unread messages
|
||||||
var unreadChats = event.docs
|
var unreadChats = event.docs
|
||||||
.map((chat) => chat.data()['amount_unread_messages'] ?? 0)
|
.map((chat) => chat.data()['amount_unread_messages'] ?? 0)
|
||||||
.toList();
|
.toList();
|
||||||
var totalUnreadChats = unreadChats.fold<int>(
|
var totalUnreadChats = unreadChats.fold<int>(
|
||||||
0, (previousValue, element) => previousValue + (element as int));
|
0,
|
||||||
|
(previousValue, element) => previousValue + (element as int),
|
||||||
|
);
|
||||||
controller.add(totalUnreadChats);
|
controller.add(totalUnreadChats);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onCancel: () {
|
onCancel: () async {
|
||||||
unreadChatSubscription?.cancel();
|
await unreadChatSubscription?.cancel();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return controller.stream;
|
return controller.stream;
|
||||||
|
@ -429,7 +438,7 @@ class FirebaseChatOverviewService implements ChatOverviewService {
|
||||||
// set the amount of unread messages to 0
|
// set the amount of unread messages to 0
|
||||||
await _db
|
await _db
|
||||||
.collection(_options.usersCollectionName)
|
.collection(_options.usersCollectionName)
|
||||||
.doc(currentUser!.id!)
|
.doc(currentUser!.id)
|
||||||
.collection(_options.userChatsCollectionName)
|
.collection(_options.userChatsCollectionName)
|
||||||
.doc(chat.id)
|
.doc(chat.id)
|
||||||
.set({'amount_unread_messages': 0}, SetOptions(merge: true));
|
.set({'amount_unread_messages': 0}, SetOptions(merge: true));
|
||||||
|
|
|
@ -1,4 +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_detail_service.dart';
|
||||||
export 'package:flutter_chat_firebase/service/firebase_chat_overview_service.dart';
|
export 'package:flutter_chat_firebase/service/firebase_chat_overview_service.dart';
|
||||||
export 'package:flutter_chat_firebase/service/firebase_chat_service.dart';
|
export 'package:flutter_chat_firebase/service/firebase_chat_service.dart';
|
||||||
|
export 'package:flutter_chat_firebase/service/firebase_chat_user_service.dart';
|
||||||
|
|
|
@ -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: 1.1.0
|
version: 1.2.0
|
||||||
publish_to: none
|
publish_to: none
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
@ -23,9 +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: 1.1.0
|
ref: 1.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^2.0.0
|
flutter_iconica_analysis:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||||
|
ref: 6.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Possible to overwrite the rules from the package
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Iconica
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
///
|
||||||
library flutter_chat_interface;
|
library flutter_chat_interface;
|
||||||
|
|
||||||
|
export 'package:flutter_chat_interface/src/chat_data_provider.dart';
|
||||||
export 'package:flutter_chat_interface/src/model/model.dart';
|
export 'package:flutter_chat_interface/src/model/model.dart';
|
||||||
export 'package:flutter_chat_interface/src/service/service.dart';
|
export 'package:flutter_chat_interface/src/service/service.dart';
|
||||||
export 'package:flutter_chat_interface/src/chat_data_provider.dart';
|
|
||||||
|
|
|
@ -34,15 +34,15 @@ abstract class GroupChatModelInterface extends ChatModel {
|
||||||
|
|
||||||
class GroupChatModel implements GroupChatModelInterface {
|
class GroupChatModel implements GroupChatModelInterface {
|
||||||
GroupChatModel({
|
GroupChatModel({
|
||||||
|
required this.canBeDeleted,
|
||||||
|
required this.title,
|
||||||
|
required this.imageUrl,
|
||||||
|
required this.users,
|
||||||
this.id,
|
this.id,
|
||||||
this.messages,
|
this.messages,
|
||||||
this.unreadMessages,
|
this.unreadMessages,
|
||||||
this.lastUsed,
|
this.lastUsed,
|
||||||
this.lastMessage,
|
this.lastMessage,
|
||||||
required this.canBeDeleted,
|
|
||||||
required this.title,
|
|
||||||
required this.imageUrl,
|
|
||||||
required this.users,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -75,17 +75,16 @@ class GroupChatModel implements GroupChatModelInterface {
|
||||||
String? title,
|
String? title,
|
||||||
String? imageUrl,
|
String? imageUrl,
|
||||||
List<ChatUserModel>? users,
|
List<ChatUserModel>? users,
|
||||||
}) {
|
}) =>
|
||||||
return GroupChatModel(
|
GroupChatModel(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
messages: messages ?? this.messages,
|
messages: messages ?? this.messages,
|
||||||
unreadMessages: unreadMessages ?? this.unreadMessages,
|
unreadMessages: unreadMessages ?? this.unreadMessages,
|
||||||
lastUsed: lastUsed ?? this.lastUsed,
|
lastUsed: lastUsed ?? this.lastUsed,
|
||||||
lastMessage: lastMessage ?? this.lastMessage,
|
lastMessage: lastMessage ?? this.lastMessage,
|
||||||
canBeDeleted: canBeDeleted ?? this.canBeDeleted,
|
canBeDeleted: canBeDeleted ?? this.canBeDeleted,
|
||||||
title: title ?? this.title,
|
title: title ?? this.title,
|
||||||
imageUrl: imageUrl ?? this.imageUrl,
|
imageUrl: imageUrl ?? this.imageUrl,
|
||||||
users: users ?? this.users,
|
users: users ?? this.users,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_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';
|
||||||
export 'chat_message.dart';
|
|
||||||
|
|
|
@ -30,13 +30,13 @@ abstract class PersonalChatModelInterface extends ChatModel {
|
||||||
|
|
||||||
class PersonalChatModel implements PersonalChatModelInterface {
|
class PersonalChatModel implements PersonalChatModelInterface {
|
||||||
PersonalChatModel({
|
PersonalChatModel({
|
||||||
|
required this.user,
|
||||||
this.id,
|
this.id,
|
||||||
this.messages = const [],
|
this.messages = const [],
|
||||||
this.unreadMessages,
|
this.unreadMessages,
|
||||||
this.lastUsed,
|
this.lastUsed,
|
||||||
this.lastMessage,
|
this.lastMessage,
|
||||||
this.canBeDeleted = true,
|
this.canBeDeleted = true,
|
||||||
required this.user,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -64,15 +64,14 @@ class PersonalChatModel implements PersonalChatModelInterface {
|
||||||
ChatMessageModel? lastMessage,
|
ChatMessageModel? lastMessage,
|
||||||
bool? canBeDeleted,
|
bool? canBeDeleted,
|
||||||
ChatUserModel? user,
|
ChatUserModel? user,
|
||||||
}) {
|
}) =>
|
||||||
return PersonalChatModel(
|
PersonalChatModel(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
messages: messages ?? this.messages,
|
messages: messages ?? this.messages,
|
||||||
unreadMessages: unreadMessages ?? this.unreadMessages,
|
unreadMessages: unreadMessages ?? this.unreadMessages,
|
||||||
lastUsed: lastUsed ?? this.lastUsed,
|
lastUsed: lastUsed ?? this.lastUsed,
|
||||||
lastMessage: lastMessage ?? this.lastMessage,
|
lastMessage: lastMessage ?? this.lastMessage,
|
||||||
user: user ?? this.user,
|
user: user ?? this.user,
|
||||||
canBeDeleted: canBeDeleted ?? this.canBeDeleted,
|
canBeDeleted: canBeDeleted ?? this.canBeDeleted,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export 'chat_overview_service.dart';
|
|
||||||
export 'user_service.dart';
|
|
||||||
export 'chat_detail_service.dart';
|
export 'chat_detail_service.dart';
|
||||||
|
export 'chat_overview_service.dart';
|
||||||
export 'chat_service.dart';
|
export 'chat_service.dart';
|
||||||
|
export 'user_service.dart';
|
||||||
|
|
|
@ -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: 1.1.0
|
version: 1.2.0
|
||||||
publish_to: none
|
publish_to: none
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
@ -20,6 +20,9 @@ dependencies:
|
||||||
ref: 1.0.0
|
ref: 1.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^2.0.0
|
flutter_iconica_analysis:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||||
|
ref: 6.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Possible to overwrite the rules from the package
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
///
|
||||||
library local_chat_service;
|
library local_chat_service;
|
||||||
|
|
||||||
export 'service/local_chat_service.dart';
|
|
||||||
export 'service/local_chat_detail_service.dart';
|
export 'service/local_chat_detail_service.dart';
|
||||||
export 'service/local_chat_overview_service.dart';
|
export 'service/local_chat_overview_service.dart';
|
||||||
|
export 'service/local_chat_service.dart';
|
||||||
export 'service/local_chat_user_service.dart';
|
export 'service/local_chat_user_service.dart';
|
||||||
|
|
|
@ -5,9 +5,8 @@ import 'package:flutter_chat_interface/flutter_chat_interface.dart';
|
||||||
import 'package:flutter_chat_local/local_chat_service.dart';
|
import 'package:flutter_chat_local/local_chat_service.dart';
|
||||||
|
|
||||||
class LocalChatDetailService with ChangeNotifier implements ChatDetailService {
|
class LocalChatDetailService with ChangeNotifier implements ChatDetailService {
|
||||||
final ChatOverviewService chatOverviewService;
|
|
||||||
|
|
||||||
LocalChatDetailService({required this.chatOverviewService});
|
LocalChatDetailService({required this.chatOverviewService});
|
||||||
|
final ChatOverviewService chatOverviewService;
|
||||||
final List<ChatMessageModel> _cumulativeMessages = [];
|
final List<ChatMessageModel> _cumulativeMessages = [];
|
||||||
final StreamController<List<ChatMessageModel>> _controller =
|
final StreamController<List<ChatMessageModel>> _controller =
|
||||||
StreamController<List<ChatMessageModel>>.broadcast();
|
StreamController<List<ChatMessageModel>>.broadcast();
|
||||||
|
@ -25,13 +24,11 @@ class LocalChatDetailService with ChangeNotifier implements ChatDetailService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<ChatMessageModel> getMessages() {
|
List<ChatMessageModel> getMessages() => _cumulativeMessages;
|
||||||
return _cumulativeMessages;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<List<ChatMessageModel>> getMessagesStream(String chatId) {
|
Stream<List<ChatMessageModel>> getMessagesStream(String chatId) {
|
||||||
_controller.onListen = () {
|
_controller.onListen = () async {
|
||||||
_subscription =
|
_subscription =
|
||||||
chatOverviewService.getChatById(chatId).asStream().listen((event) {
|
chatOverviewService.getChatById(chatId).asStream().listen((event) {
|
||||||
_cumulativeMessages.clear();
|
_cumulativeMessages.clear();
|
||||||
|
@ -44,20 +41,22 @@ class LocalChatDetailService with ChangeNotifier implements ChatDetailService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> sendImageMessage(
|
Future<void> sendImageMessage({
|
||||||
{required String chatId, required Uint8List image}) async {
|
required String chatId,
|
||||||
|
required Uint8List image,
|
||||||
|
}) async {
|
||||||
var chat = (chatOverviewService as LocalChatOverviewService)
|
var chat = (chatOverviewService as LocalChatOverviewService)
|
||||||
.chats
|
.chats
|
||||||
.firstWhere((element) => element.id == chatId);
|
.firstWhere((element) => element.id == chatId);
|
||||||
var message = ChatImageMessageModel(
|
var message = ChatImageMessageModel(
|
||||||
sender: ChatUserModel(
|
sender: ChatUserModel(
|
||||||
id: "3",
|
id: '3',
|
||||||
firstName: "ico",
|
firstName: 'ico',
|
||||||
lastName: "nica",
|
lastName: 'nica',
|
||||||
imageUrl: "https://picsum.photos/100/200",
|
imageUrl: 'https://picsum.photos/100/200',
|
||||||
),
|
),
|
||||||
timestamp: DateTime.now(),
|
timestamp: DateTime.now(),
|
||||||
imageUrl: "https://picsum.photos/200/300",
|
imageUrl: 'https://picsum.photos/200/300',
|
||||||
);
|
);
|
||||||
|
|
||||||
await (chatOverviewService as LocalChatOverviewService).updateChat(
|
await (chatOverviewService as LocalChatOverviewService).updateChat(
|
||||||
|
@ -75,17 +74,19 @@ class LocalChatDetailService with ChangeNotifier implements ChatDetailService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> sendTextMessage(
|
Future<void> sendTextMessage({
|
||||||
{required String chatId, required String text}) async {
|
required String chatId,
|
||||||
|
required String text,
|
||||||
|
}) async {
|
||||||
var chat = (chatOverviewService as LocalChatOverviewService)
|
var chat = (chatOverviewService as LocalChatOverviewService)
|
||||||
.chats
|
.chats
|
||||||
.firstWhere((element) => element.id == chatId);
|
.firstWhere((element) => element.id == chatId);
|
||||||
var message = ChatTextMessageModel(
|
var message = ChatTextMessageModel(
|
||||||
sender: ChatUserModel(
|
sender: ChatUserModel(
|
||||||
id: "3",
|
id: '3',
|
||||||
firstName: "ico",
|
firstName: 'ico',
|
||||||
lastName: "nica",
|
lastName: 'nica',
|
||||||
imageUrl: "https://picsum.photos/100/200",
|
imageUrl: 'https://picsum.photos/100/200',
|
||||||
),
|
),
|
||||||
timestamp: DateTime.now(),
|
timestamp: DateTime.now(),
|
||||||
text: text,
|
text: text,
|
||||||
|
@ -106,8 +107,8 @@ class LocalChatDetailService with ChangeNotifier implements ChatDetailService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void stopListeningForMessages() {
|
Future<void> stopListeningForMessages() async {
|
||||||
_subscription?.cancel();
|
await _subscription?.cancel();
|
||||||
_subscription = null;
|
_subscription = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,16 +29,15 @@ class LocalChatOverviewService
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<ChatModel> getChatById(String id) {
|
Future<ChatModel> getChatById(String id) =>
|
||||||
return Future.value(_chats.firstWhere((element) => element.id == id));
|
Future.value(_chats.firstWhere((element) => element.id == id));
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<ChatModel> getChatByUser(ChatUserModel user) {
|
Future<ChatModel> getChatByUser(ChatUserModel user) {
|
||||||
PersonalChatModel? chat;
|
PersonalChatModel? chat;
|
||||||
try {
|
try {
|
||||||
chat = _chats.firstWhere((element) => element.user.id == user.id);
|
chat = _chats.firstWhere((element) => element.user.id == user.id);
|
||||||
} catch (e) {
|
} on Exception catch (_) {
|
||||||
chat = PersonalChatModel(
|
chat = PersonalChatModel(
|
||||||
user: user,
|
user: user,
|
||||||
messages: [],
|
messages: [],
|
||||||
|
@ -54,22 +53,14 @@ class LocalChatOverviewService
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<List<ChatModel>> getChatsStream() {
|
Stream<List<ChatModel>> getChatsStream() => _chatsController.stream;
|
||||||
return _chatsController.stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<int> getUnreadChatsCountStream() {
|
Stream<int> getUnreadChatsCountStream() => Stream.value(0);
|
||||||
return Stream.value(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> readChat(ChatModel chat) async {
|
Future<void> readChat(ChatModel chat) async => Future.value();
|
||||||
return Future.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<ChatModel> storeChatIfNot(ChatModel chat) {
|
Future<ChatModel> storeChatIfNot(ChatModel chat) => Future.value(chat);
|
||||||
return Future.value(chat);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,25 +5,21 @@ class LocalChatUserService implements ChatUserService {
|
||||||
ChatUserModel(
|
ChatUserModel(
|
||||||
id: '1',
|
id: '1',
|
||||||
firstName: 'John',
|
firstName: 'John',
|
||||||
lastName: "Doe",
|
lastName: 'Doe',
|
||||||
imageUrl: 'https://picsum.photos/200/300',
|
imageUrl: 'https://picsum.photos/200/300',
|
||||||
),
|
),
|
||||||
ChatUserModel(
|
ChatUserModel(
|
||||||
id: '2',
|
id: '2',
|
||||||
firstName: 'Jane',
|
firstName: 'Jane',
|
||||||
lastName: "Doe",
|
lastName: 'Doe',
|
||||||
imageUrl: 'https://picsum.photos/200/300',
|
imageUrl: 'https://picsum.photos/200/300',
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
@override
|
@override
|
||||||
Future<List<ChatUserModel>> getAllUsers() {
|
Future<List<ChatUserModel>> getAllUsers() => Future.value(users);
|
||||||
return Future.value(users);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<ChatUserModel?> getCurrentUser() {
|
Future<ChatUserModel?> getCurrentUser() => Future.value(ChatUserModel());
|
||||||
return Future.value(ChatUserModel());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<ChatUserModel?> getUser(String id) {
|
Future<ChatUserModel?> getUser(String id) {
|
||||||
|
|
|
@ -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: 1.1.0
|
version: 1.2.0
|
||||||
publish_to: none
|
publish_to: none
|
||||||
homepage:
|
homepage:
|
||||||
|
|
||||||
|
@ -15,10 +15,13 @@ 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: 1.1.0
|
ref: 1.2.0
|
||||||
|
|
||||||
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: 6.0.0
|
||||||
flutter:
|
flutter:
|
||||||
|
|
|
@ -1,214 +1,9 @@
|
||||||
# SPDX-FileCopyrightText: 2022 Iconica
|
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# Possible to overwrite the rules from the package
|
||||||
|
|
||||||
include: package:flutter_lints/flutter.yaml
|
|
||||||
analyzer:
|
analyzer:
|
||||||
errors:
|
exclude:
|
||||||
todo: ignore
|
|
||||||
exclude: [lib/generated_plugin_registrant.dart]
|
|
||||||
linter:
|
|
||||||
# https://dart.dev/tools/linter-rules#lints
|
|
||||||
rules:
|
|
||||||
# error rules
|
|
||||||
always_use_package_imports: false
|
|
||||||
avoid_dynamic_calls: true
|
|
||||||
avoid_empty_else: true
|
|
||||||
avoid_print: true
|
|
||||||
avoid_relative_lib_imports: true
|
|
||||||
avoid_returning_null_for_future: true
|
|
||||||
avoid_slow_async_io: true
|
|
||||||
avoid_type_to_string: true
|
|
||||||
avoid_types_as_parameter_names: true
|
|
||||||
avoid_web_libraries_in_flutter: true
|
|
||||||
cancel_subscriptions: true
|
|
||||||
close_sinks: true
|
|
||||||
comment_references: false
|
|
||||||
control_flow_in_finally: true
|
|
||||||
diagnostic_describe_all_properties: false
|
|
||||||
empty_statements: true
|
|
||||||
hash_and_equals: true
|
|
||||||
literal_only_boolean_expressions: true
|
|
||||||
no_adjacent_strings_in_list: true
|
|
||||||
no_duplicate_case_values: true
|
|
||||||
no_logic_in_create_state: true
|
|
||||||
prefer_relative_imports: false
|
|
||||||
prefer_void_to_null: true
|
|
||||||
test_types_in_equals: true
|
|
||||||
throw_in_finally: true
|
|
||||||
unnecessary_statements: true
|
|
||||||
unrelated_type_equality_checks: true
|
|
||||||
unsafe_html: true
|
|
||||||
use_build_context_synchronously: true
|
|
||||||
use_key_in_widget_constructors: true
|
|
||||||
valid_regexps: true
|
|
||||||
# style rules
|
|
||||||
always_declare_return_types: true
|
|
||||||
always_put_control_body_on_new_line: true
|
|
||||||
always_put_required_named_parameters_first: true
|
|
||||||
always_require_non_null_named_parameters: true
|
|
||||||
always_specify_types: false
|
|
||||||
annotate_overrides: true
|
|
||||||
avoid_annotating_with_dynamic: false
|
|
||||||
avoid_bool_literals_in_conditional_expressions: true
|
|
||||||
avoid_catches_without_on_clauses: false
|
|
||||||
avoid_catching_errors: false
|
|
||||||
avoid_classes_with_only_static_members: true
|
|
||||||
avoid_double_and_int_checks: true
|
|
||||||
avoid_equals_and_hash_code_on_mutable_classes: false
|
|
||||||
avoid_escaping_inner_quotes: false
|
|
||||||
avoid_field_initializers_in_const_classes: true
|
|
||||||
avoid_final_parameters: true
|
|
||||||
avoid_function_literals_in_foreach_calls: true
|
|
||||||
avoid_implementing_value_types: true
|
|
||||||
avoid_init_to_null: true
|
|
||||||
avoid_js_rounded_ints: true
|
|
||||||
avoid_multiple_declarations_per_line: true
|
|
||||||
avoid_null_checks_in_equality_operators: true
|
|
||||||
avoid_positional_boolean_parameters: true
|
|
||||||
avoid_private_typedef_functions: true
|
|
||||||
avoid_redundant_argument_values: false
|
|
||||||
avoid_renaming_method_parameters: true
|
|
||||||
avoid_return_types_on_setters: true
|
|
||||||
avoid_returning_null: true
|
|
||||||
avoid_returning_null_for_void: true
|
|
||||||
avoid_returning_this: true
|
|
||||||
avoid_setters_without_getters: true
|
|
||||||
avoid_shadowing_type_parameters: true
|
|
||||||
avoid_single_cascade_in_expression_statements: true
|
|
||||||
avoid_types_on_closure_parameters: false
|
|
||||||
avoid_unnecessary_containers: false
|
|
||||||
avoid_unused_constructor_parameters: true
|
|
||||||
avoid_void_async: true
|
|
||||||
await_only_futures: true
|
|
||||||
camel_case_extensions: true
|
|
||||||
camel_case_types: true
|
|
||||||
cascade_invocations: true
|
|
||||||
cast_nullable_to_non_nullable: true
|
|
||||||
conditional_uri_does_not_exist: true
|
|
||||||
constant_identifier_names: true
|
|
||||||
curly_braces_in_flow_control_structures: true
|
|
||||||
deprecated_consistency: true
|
|
||||||
directives_ordering: true
|
|
||||||
do_not_use_environment: true
|
|
||||||
empty_catches: true
|
|
||||||
empty_constructor_bodies: true
|
|
||||||
eol_at_end_of_file: true
|
|
||||||
exhaustive_cases: true
|
|
||||||
file_names: true
|
|
||||||
flutter_style_todos: true
|
|
||||||
implementation_imports: true
|
|
||||||
join_return_with_assignment: true
|
|
||||||
leading_newlines_in_multiline_strings: true
|
|
||||||
library_names: true
|
|
||||||
library_prefixes: true
|
|
||||||
library_private_types_in_public_api: true
|
|
||||||
lines_longer_than_80_chars: true
|
|
||||||
missing_whitespace_between_adjacent_strings: true
|
|
||||||
no_default_cases: true
|
|
||||||
no_leading_underscores_for_library_prefixes: true
|
|
||||||
no_leading_underscores_for_local_identifiers: true
|
|
||||||
no_runtimeType_toString: true
|
|
||||||
non_constant_identifier_names: true
|
|
||||||
noop_primitive_operations: true
|
|
||||||
null_check_on_nullable_type_parameter: true
|
|
||||||
null_closures: true
|
|
||||||
omit_local_variable_types: true
|
|
||||||
one_member_abstracts: true
|
|
||||||
only_throw_errors: true
|
|
||||||
overridden_fields: true
|
|
||||||
package_api_docs: true
|
|
||||||
package_prefixed_library_names: true
|
|
||||||
parameter_assignments: true
|
|
||||||
prefer_adjacent_string_concatenation: true
|
|
||||||
prefer_asserts_in_initializer_lists: true
|
|
||||||
prefer_asserts_with_message: true
|
|
||||||
prefer_collection_literals: true
|
|
||||||
prefer_conditional_assignment: true
|
|
||||||
prefer_const_constructors: true
|
|
||||||
prefer_const_constructors_in_immutables: true
|
|
||||||
prefer_const_declarations: false
|
|
||||||
prefer_const_literals_to_create_immutables: false
|
|
||||||
prefer_constructors_over_static_methods: true
|
|
||||||
prefer_contains: true
|
|
||||||
prefer_double_quotes: false
|
|
||||||
prefer_equal_for_default_values: true
|
|
||||||
prefer_expression_function_bodies: false
|
|
||||||
prefer_final_fields: true
|
|
||||||
prefer_final_in_for_each: false
|
|
||||||
prefer_final_locals: false
|
|
||||||
prefer_final_parameters: false
|
|
||||||
prefer_for_elements_to_map_fromIterable: true
|
|
||||||
prefer_foreach: true
|
|
||||||
prefer_function_declarations_over_variables: true
|
|
||||||
prefer_generic_function_type_aliases: true
|
|
||||||
prefer_if_elements_to_conditional_expressions: true
|
|
||||||
prefer_if_null_operators: true
|
|
||||||
prefer_initializing_formals: true
|
|
||||||
prefer_inlined_adds: true
|
|
||||||
prefer_int_literals: false
|
|
||||||
prefer_interpolation_to_compose_strings: true
|
|
||||||
prefer_is_empty: true
|
|
||||||
prefer_is_not_empty: true
|
|
||||||
prefer_is_not_operator: true
|
|
||||||
prefer_iterable_whereType: true
|
|
||||||
prefer_mixin: true
|
|
||||||
prefer_null_aware_method_calls: true
|
|
||||||
prefer_null_aware_operators: true
|
|
||||||
prefer_single_quotes: true
|
|
||||||
prefer_spread_collections: true
|
|
||||||
prefer_typing_uninitialized_variables: true
|
|
||||||
provide_deprecation_message: true
|
|
||||||
public_member_api_docs: false
|
|
||||||
recursive_getters: true
|
|
||||||
require_trailing_commas: true
|
|
||||||
sized_box_for_whitespace: true
|
|
||||||
sized_box_shrink_expand: true
|
|
||||||
slash_for_doc_comments: true
|
|
||||||
sort_child_properties_last: true
|
|
||||||
sort_constructors_first: true
|
|
||||||
sort_unnamed_constructors_first: true
|
|
||||||
tighten_type_of_initializing_formals: true
|
|
||||||
type_annotate_public_apis: true
|
|
||||||
type_init_formals: true
|
|
||||||
unawaited_futures: true
|
|
||||||
unnecessary_await_in_return: true
|
|
||||||
unnecessary_brace_in_string_interps: true
|
|
||||||
unnecessary_const: false
|
|
||||||
unnecessary_constructor_name: true
|
|
||||||
unnecessary_final: true
|
|
||||||
unnecessary_getters_setters: true
|
|
||||||
unnecessary_lambdas: true
|
|
||||||
unnecessary_late: true
|
|
||||||
unnecessary_new: true
|
|
||||||
unnecessary_null_aware_assignments: true
|
|
||||||
unnecessary_null_checks: true
|
|
||||||
unnecessary_null_in_if_null_operators: true
|
|
||||||
unnecessary_nullable_for_final_variable_declarations: true
|
|
||||||
unnecessary_overrides: true
|
|
||||||
unnecessary_parenthesis: true
|
|
||||||
unnecessary_raw_strings: true
|
|
||||||
unnecessary_string_escapes: true
|
|
||||||
unnecessary_string_interpolations: true
|
|
||||||
unnecessary_this: true
|
|
||||||
use_decorated_box: true
|
|
||||||
use_full_hex_values_for_flutter_colors: true
|
|
||||||
use_function_type_syntax_for_parameters: true
|
|
||||||
use_if_null_to_convert_nulls_to_bools: true
|
|
||||||
use_is_even_rather_than_modulo: true
|
|
||||||
use_late_for_private_fields_and_variables: true
|
|
||||||
use_named_constants: true
|
|
||||||
use_raw_strings: false
|
|
||||||
use_rethrow_when_possible: true
|
|
||||||
use_setters_to_change_properties: true
|
|
||||||
use_string_buffers: true
|
|
||||||
use_test_throws_matchers: true
|
|
||||||
use_to_and_as_if_applicable: true
|
|
||||||
void_checks: true
|
|
||||||
# pub rules
|
|
||||||
depend_on_referenced_packages: true
|
|
||||||
secure_pubspec_urls: false
|
|
||||||
sort_pub_dependencies: false
|
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
linter:
|
||||||
# https://dart.dev/guides/language/analysis-options
|
rules:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Iconica
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
///
|
||||||
library flutter_chat_view;
|
library flutter_chat_view;
|
||||||
|
|
||||||
export 'package:flutter_chat_interface/flutter_chat_interface.dart';
|
export 'package:flutter_chat_interface/flutter_chat_interface.dart';
|
||||||
|
|
|
@ -53,11 +53,11 @@ class _ChatBottomState extends State<ChatBottom> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
var value = _textEditingController.text;
|
var value = _textEditingController.text;
|
||||||
|
|
||||||
if (value.isNotEmpty) {
|
if (value.isNotEmpty) {
|
||||||
widget.onMessageSubmit(value);
|
await widget.onMessageSubmit(value);
|
||||||
_textEditingController.clear();
|
_textEditingController.clear();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -64,85 +64,80 @@ class _ChatDetailRowState extends State<ChatDetailRow> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Padding(
|
||||||
child: Padding(
|
padding: const EdgeInsets.symmetric(horizontal: 22.0),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 22.0),
|
child: Column(
|
||||||
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
children: [
|
||||||
children: [
|
if (isNewDate || isSameSender)
|
||||||
if (isNewDate || isSameSender)
|
Row(
|
||||||
Row(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
children: [
|
||||||
children: [
|
Text(
|
||||||
Text(
|
widget.message.sender.fullName?.toUpperCase() ??
|
||||||
widget.message.sender.fullName?.toUpperCase() ??
|
widget.translations.anonymousUser,
|
||||||
widget.translations.anonymousUser,
|
style: TextStyle(
|
||||||
style: TextStyle(
|
fontSize: 14,
|
||||||
fontSize: 14,
|
fontWeight: FontWeight.w500,
|
||||||
fontWeight: FontWeight.w500,
|
color:
|
||||||
color: Theme.of(context)
|
Theme.of(context).textTheme.labelMedium?.color,
|
||||||
.textTheme
|
),
|
||||||
.labelMedium
|
),
|
||||||
?.color,
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 5.0),
|
||||||
|
child: Text(
|
||||||
|
_dateFormatter.format(
|
||||||
|
date: widget.message.timestamp,
|
||||||
|
showFullDate: true,
|
||||||
|
),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Color(0xFFBBBBBB),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
),
|
||||||
padding: const EdgeInsets.only(top: 5.0),
|
],
|
||||||
child: Text(
|
|
||||||
_dateFormatter.format(
|
|
||||||
date: widget.message.timestamp,
|
|
||||||
showFullDate: true,
|
|
||||||
),
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFFBBBBBB),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 3.0),
|
|
||||||
child: widget.message is ChatTextMessageModel
|
|
||||||
? RichText(
|
|
||||||
text: TextSpan(
|
|
||||||
text: (widget.message as ChatTextMessageModel)
|
|
||||||
.text,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelMedium
|
|
||||||
?.color,
|
|
||||||
),
|
|
||||||
children: <TextSpan>[
|
|
||||||
if (widget.showTime)
|
|
||||||
TextSpan(
|
|
||||||
text: " ${_dateFormatter.format(
|
|
||||||
date: widget.message.timestamp,
|
|
||||||
showFullDate: true,
|
|
||||||
).split(' ').last}",
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFFBBBBBB),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else
|
|
||||||
const TextSpan(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 999,
|
|
||||||
)
|
|
||||||
: CachedNetworkImage(
|
|
||||||
imageUrl:
|
|
||||||
(widget.message as ChatImageMessageModel)
|
|
||||||
.imageUrl,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
Padding(
|
||||||
),
|
padding: const EdgeInsets.only(top: 3.0),
|
||||||
|
child: widget.message is ChatTextMessageModel
|
||||||
|
? RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
text:
|
||||||
|
(widget.message as ChatTextMessageModel).text,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.labelMedium
|
||||||
|
?.color,
|
||||||
|
),
|
||||||
|
children: <TextSpan>[
|
||||||
|
if (widget.showTime)
|
||||||
|
TextSpan(
|
||||||
|
text: " ${_dateFormatter.format(
|
||||||
|
date: widget.message.timestamp,
|
||||||
|
showFullDate: true,
|
||||||
|
).split(' ').last}",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Color(0xFFBBBBBB),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
const TextSpan(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 999,
|
||||||
|
)
|
||||||
|
: CachedNetworkImage(
|
||||||
|
imageUrl: (widget.message as ChatImageMessageModel)
|
||||||
|
.imageUrl,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -123,7 +123,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Future<void> onPressSelectImage() => showModalBottomSheet<Uint8List?>(
|
Future<void> onPressSelectImage() async => showModalBottomSheet<Uint8List?>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) =>
|
builder: (BuildContext context) =>
|
||||||
widget.options.imagePickerContainerBuilder(
|
widget.options.imagePickerContainerBuilder(
|
||||||
|
@ -147,6 +147,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
|
||||||
);
|
);
|
||||||
|
|
||||||
return FutureBuilder<ChatModel>(
|
return FutureBuilder<ChatModel>(
|
||||||
|
// ignore: discarded_futures
|
||||||
future: widget.service.chatOverviewService.getChatById(widget.chatId),
|
future: widget.service.chatOverviewService.getChatById(widget.chatId),
|
||||||
builder: (context, AsyncSnapshot<ChatModel> snapshot) {
|
builder: (context, AsyncSnapshot<ChatModel> snapshot) {
|
||||||
var chatModel = snapshot.data;
|
var chatModel = snapshot.data;
|
||||||
|
@ -199,7 +200,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
|
||||||
var isTop = controller.position.pixels ==
|
var isTop = controller.position.pixels ==
|
||||||
controller.position.maxScrollExtent;
|
controller.position.maxScrollExtent;
|
||||||
|
|
||||||
if (showIndicator == false &&
|
if (!showIndicator &&
|
||||||
!isTop &&
|
!isTop &&
|
||||||
controller.position.userScrollDirection ==
|
controller.position.userScrollDirection ==
|
||||||
ScrollDirection.reverse) {
|
ScrollDirection.reverse) {
|
||||||
|
|
|
@ -30,7 +30,9 @@ class _ProfileScreenState extends State<ChatProfileScreen> {
|
||||||
var hasUser = widget.userId == null;
|
var hasUser = widget.userId == null;
|
||||||
return FutureBuilder<dynamic>(
|
return FutureBuilder<dynamic>(
|
||||||
future: hasUser
|
future: hasUser
|
||||||
|
// ignore: discarded_futures
|
||||||
? widget.chatService.chatOverviewService.getChatById(widget.chatId)
|
? widget.chatService.chatOverviewService.getChatById(widget.chatId)
|
||||||
|
// ignore: discarded_futures
|
||||||
: widget.chatService.chatUserService.getUser(widget.userId!),
|
: widget.chatService.chatUserService.getUser(widget.userId!),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
var data = snapshot.data;
|
var data = snapshot.data;
|
||||||
|
|
|
@ -258,55 +258,53 @@ class ChatListItem extends StatelessWidget {
|
||||||
final DateFormatter _dateFormatter;
|
final DateFormatter _dateFormatter;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => GestureDetector(
|
||||||
return GestureDetector(
|
onTap: () => widget.onPressChat(chat),
|
||||||
onTap: () => widget.onPressChat(chat),
|
child: Container(
|
||||||
child: Container(
|
color: Colors.transparent,
|
||||||
color: Colors.transparent,
|
child: widget.options.chatRowContainerBuilder(
|
||||||
child: widget.options.chatRowContainerBuilder(
|
(chat is PersonalChatModel)
|
||||||
(chat is PersonalChatModel)
|
? ChatRow(
|
||||||
? ChatRow(
|
unreadMessages: chat.unreadMessages ?? 0,
|
||||||
unreadMessages: chat.unreadMessages ?? 0,
|
avatar: widget.options.userAvatarBuilder(
|
||||||
avatar: widget.options.userAvatarBuilder(
|
(chat as PersonalChatModel).user,
|
||||||
(chat as PersonalChatModel).user,
|
40.0,
|
||||||
40.0,
|
),
|
||||||
|
title: (chat as PersonalChatModel).user.fullName ??
|
||||||
|
translations.anonymousUser,
|
||||||
|
subTitle: chat.lastMessage != null
|
||||||
|
? chat.lastMessage is ChatTextMessageModel
|
||||||
|
? (chat.lastMessage! as ChatTextMessageModel).text
|
||||||
|
: '📷 '
|
||||||
|
'${translations.image}'
|
||||||
|
: '',
|
||||||
|
lastUsed: chat.lastUsed != null
|
||||||
|
? _dateFormatter.format(
|
||||||
|
date: chat.lastUsed!,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
)
|
||||||
|
: ChatRow(
|
||||||
|
title: (chat as GroupChatModel).title,
|
||||||
|
unreadMessages: chat.unreadMessages ?? 0,
|
||||||
|
subTitle: chat.lastMessage != null
|
||||||
|
? chat.lastMessage is ChatTextMessageModel
|
||||||
|
? (chat.lastMessage! as ChatTextMessageModel).text
|
||||||
|
: '📷 '
|
||||||
|
'${translations.image}'
|
||||||
|
: '',
|
||||||
|
avatar: widget.options.groupAvatarBuilder(
|
||||||
|
(chat as GroupChatModel).title,
|
||||||
|
(chat as GroupChatModel).imageUrl,
|
||||||
|
40.0,
|
||||||
|
),
|
||||||
|
lastUsed: chat.lastUsed != null
|
||||||
|
? _dateFormatter.format(
|
||||||
|
date: chat.lastUsed!,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
title: (chat as PersonalChatModel).user.fullName ??
|
),
|
||||||
translations.anonymousUser,
|
|
||||||
subTitle: chat.lastMessage != null
|
|
||||||
? chat.lastMessage is ChatTextMessageModel
|
|
||||||
? (chat.lastMessage! as ChatTextMessageModel).text
|
|
||||||
: '📷 '
|
|
||||||
'${translations.image}'
|
|
||||||
: '',
|
|
||||||
lastUsed: chat.lastUsed != null
|
|
||||||
? _dateFormatter.format(
|
|
||||||
date: chat.lastUsed!,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
)
|
|
||||||
: ChatRow(
|
|
||||||
title: (chat as GroupChatModel).title,
|
|
||||||
unreadMessages: chat.unreadMessages ?? 0,
|
|
||||||
subTitle: chat.lastMessage != null
|
|
||||||
? chat.lastMessage is ChatTextMessageModel
|
|
||||||
? (chat.lastMessage! as ChatTextMessageModel).text
|
|
||||||
: '📷 '
|
|
||||||
'${translations.image}'
|
|
||||||
: '',
|
|
||||||
avatar: widget.options.groupAvatarBuilder(
|
|
||||||
(chat as GroupChatModel).title,
|
|
||||||
(chat as GroupChatModel).imageUrl,
|
|
||||||
40.0,
|
|
||||||
),
|
|
||||||
lastUsed: chat.lastUsed != null
|
|
||||||
? _dateFormatter.format(
|
|
||||||
date: chat.lastUsed!,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,67 +29,62 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||||
String query = '';
|
String query = '';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => Scaffold(
|
||||||
return Scaffold(
|
appBar: AppBar(
|
||||||
appBar: AppBar(
|
title: _buildSearchField(),
|
||||||
title: _buildSearchField(),
|
actions: [
|
||||||
actions: [
|
_buildSearchIcon(),
|
||||||
_buildSearchIcon(),
|
],
|
||||||
],
|
),
|
||||||
),
|
body: FutureBuilder<List<ChatUserModel>>(
|
||||||
body: FutureBuilder<List<ChatUserModel>>(
|
// ignore: discarded_futures
|
||||||
future: widget.service.chatUserService.getAllUsers(),
|
future: widget.service.chatUserService.getAllUsers(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
} else if (snapshot.hasError) {
|
} else if (snapshot.hasError) {
|
||||||
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 {
|
} else {
|
||||||
return widget.options
|
return widget.options
|
||||||
.noChatsPlaceholderBuilder(widget.translations);
|
.noChatsPlaceholderBuilder(widget.translations);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSearchField() => _isSearching
|
||||||
|
? Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
child: TextField(
|
||||||
|
focusNode: _textFieldFocusNode,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
query = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: widget.translations.searchPlaceholder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Text(widget.translations.newChatButton);
|
||||||
|
|
||||||
|
Widget _buildSearchIcon() => IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_isSearching = !_isSearching;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (_isSearching) {
|
||||||
|
_textFieldFocusNode.requestFocus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
icon: Icon(
|
||||||
);
|
_isSearching ? Icons.close : Icons.search,
|
||||||
}
|
),
|
||||||
|
);
|
||||||
Widget _buildSearchField() {
|
|
||||||
return _isSearching
|
|
||||||
? Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
|
||||||
child: TextField(
|
|
||||||
focusNode: _textFieldFocusNode,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
query = value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: widget.translations.searchPlaceholder,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Text(widget.translations.newChatButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildSearchIcon() {
|
|
||||||
return IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
_isSearching = !_isSearching;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (_isSearching) {
|
|
||||||
_textFieldFocusNode.requestFocus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon: Icon(
|
|
||||||
_isSearching ? Icons.close : Icons.search,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildUserList(List<ChatUserModel> users) {
|
Widget _buildUserList(List<ChatUserModel> users) {
|
||||||
var filteredUsers = users
|
var filteredUsers = users
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
name: flutter_chat_view
|
name: flutter_chat_view
|
||||||
description: A standard flutter package.
|
description: A standard flutter package.
|
||||||
version: 1.1.0
|
version: 1.2.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: 1.1.0
|
ref: 1.2.0
|
||||||
cached_network_image: ^3.2.2
|
cached_network_image: ^3.2.2
|
||||||
flutter_image_picker:
|
flutter_image_picker:
|
||||||
git:
|
git:
|
||||||
|
@ -34,6 +34,9 @@ dependencies:
|
||||||
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: 6.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
Loading…
Reference in a new issue