feat: change ChatService and ChatScope to work the same as in flutter_availability

The userid should be managed inside of the service and it should come from the ChatOptions
This commit is contained in:
Freek van de Ven 2025-02-12 08:53:58 +01:00
parent 9af9bc8078
commit 8429add89c
6 changed files with 62 additions and 60 deletions

View file

@ -16,11 +16,15 @@ import "package:collection/collection.dart";
class ChatService { class ChatService {
/// Create a chat service with the given parameters. /// Create a chat service with the given parameters.
ChatService({ ChatService({
required this.userId,
ChatRepositoryInterface? chatRepository, ChatRepositoryInterface? chatRepository,
UserRepositoryInterface? userRepository, UserRepositoryInterface? userRepository,
}) : chatRepository = chatRepository ?? LocalChatRepository(), }) : chatRepository = chatRepository ?? LocalChatRepository(),
userRepository = userRepository ?? LocalUserRepository(); userRepository = userRepository ?? LocalUserRepository();
/// The user ID of the person currently looking at the chat
final String userId;
/// The chat repository /// The chat repository
final ChatRepositoryInterface chatRepository; final ChatRepositoryInterface chatRepository;
@ -54,11 +58,9 @@ class ChatService {
); );
} }
/// Get the chats for the given [userId]. /// Get the chats for the user with the given [userId].
/// Returns a list of [ChatModel] stream. /// Returns a list of [ChatModel] stream.
Stream<List<ChatModel>?> getChats({ Stream<List<ChatModel>?> getChats() =>
required String userId,
}) =>
chatRepository.getChats(userId: userId); chatRepository.getChats(userId: userId);
/// Get the chat with the given [chatId]. /// Get the chat with the given [chatId].
@ -129,11 +131,9 @@ class ChatService {
/// Returns a list of [MessageModel] stream. /// Returns a list of [MessageModel] stream.
/// [pageSize] is the number of messages to be fetched. /// [pageSize] is the number of messages to be fetched.
/// [page] is the page number. /// [page] is the page number.
/// [userId] is the user id.
/// [chatId] is the chat id. /// [chatId] is the chat id.
/// Returns a list of [MessageModel] stream. /// Returns a list of [MessageModel] stream.
Stream<List<MessageModel>?> getMessages({ Stream<List<MessageModel>?> getMessages({
required String userId,
required String chatId, required String chatId,
required int pageSize, required int pageSize,
required int page, required int page,
@ -183,15 +183,14 @@ class ChatService {
/// Returns a list of [UserModel] stream. /// Returns a list of [UserModel] stream.
Stream<List<UserModel>> getAllUsers() => userRepository.getAllUsers(); Stream<List<UserModel>> getAllUsers() => userRepository.getAllUsers();
/// Get the unread messages count for the given [userId] and or [chatId]. /// Get the unread messages count for a user [chatId].
/// [userId] is the user id.
/// [chatId] is the chat id. If not provided, it will return the /// [chatId] is the chat id. If not provided, it will return the
/// total unread messages count. /// total unread messages count.
/// Returns a [Stream] of [int]. /// Returns a [Stream] of [int].
Stream<int> getUnreadMessagesCount({ Stream<int> getUnreadMessagesCount({
required String userId, String? chatId,
}) => }) =>
chatRepository.getUnreadMessagesCount(userId: userId); chatRepository.getUnreadMessagesCount(userId: userId, chatId: chatId);
/// Upload an image with the given parameters. /// Upload an image with the given parameters.
/// [path] is the image path. /// [path] is the image path.
@ -211,7 +210,6 @@ class ChatService {
/// Returns a [Future] of [void]. /// Returns a [Future] of [void].
Future<void> markAsRead({ Future<void> markAsRead({
required String chatId, required String chatId,
required String userId,
}) async { }) async {
var chat = await chatRepository.getChat(chatId: chatId).first; var chat = await chatRepository.getChat(chatId: chatId).first;

View file

@ -7,7 +7,7 @@ import "package:flutter_chat/src/config/chat_translations.dart";
/// Use this class to configure the chat options. /// Use this class to configure the chat options.
class ChatOptions { class ChatOptions {
/// The chat options constructor /// The chat options constructor
const ChatOptions({ ChatOptions({
this.dateformat, this.dateformat,
this.groupChatEnabled = true, this.groupChatEnabled = true,
this.enableLoadingIndicator = false, this.enableLoadingIndicator = false,
@ -21,7 +21,16 @@ class ChatOptions {
this.chatAlignment, this.chatAlignment,
this.onNoChats, this.onNoChats,
this.pageSize = 20, this.pageSize = 20,
}); ChatRepositoryInterface? chatRepository,
UserRepositoryInterface? userRepository,
}) : chatRepository = chatRepository ?? LocalChatRepository(),
userRepository = userRepository ?? LocalUserRepository();
/// The implementation for communication with persistance layer for chats
final ChatRepositoryInterface chatRepository;
/// The implementation for communication with persistance layer for users
final UserRepositoryInterface userRepository;
/// [dateformat] is a function that formats the date. /// [dateformat] is a function that formats the date.
// ignore: avoid_positional_boolean_parameters // ignore: avoid_positional_boolean_parameters

View file

@ -8,7 +8,6 @@ class FlutterChatEntryWidget extends StatefulWidget {
/// Constructs a [FlutterChatEntryWidget]. /// Constructs a [FlutterChatEntryWidget].
const FlutterChatEntryWidget({ const FlutterChatEntryWidget({
required this.userId, required this.userId,
this.chatService,
this.options, this.options,
this.onTap, this.onTap,
this.widgetSize = 75, this.widgetSize = 75,
@ -20,9 +19,6 @@ class FlutterChatEntryWidget extends StatefulWidget {
super.key, super.key,
}); });
/// The chat service associated with the widget.
final ChatService? chatService;
/// The user ID of the person currently looking at the chat /// The user ID of the person currently looking at the chat
final String userId; final String userId;
@ -56,12 +52,29 @@ class FlutterChatEntryWidget extends StatefulWidget {
/// State class for [FlutterChatEntryWidget]. /// State class for [FlutterChatEntryWidget].
class _FlutterChatEntryWidgetState extends State<FlutterChatEntryWidget> { class _FlutterChatEntryWidgetState extends State<FlutterChatEntryWidget> {
ChatService? chatService; late ChatService chatService;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
chatService ??= widget.chatService ?? ChatService(); _initChatService();
}
@override
void didUpdateWidget(covariant FlutterChatEntryWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.userId != widget.userId ||
oldWidget.options != widget.options) {
_initChatService();
}
}
void _initChatService() {
chatService = ChatService(
userId: widget.userId,
chatRepository: widget.options?.chatRepository,
userRepository: widget.options?.userRepository,
);
} }
@override @override
@ -72,13 +85,12 @@ class _FlutterChatEntryWidgetState extends State<FlutterChatEntryWidget> {
MaterialPageRoute( MaterialPageRoute(
builder: (context) => FlutterChatNavigatorUserstory( builder: (context) => FlutterChatNavigatorUserstory(
userId: widget.userId, userId: widget.userId,
chatService: chatService, options: widget.options ?? ChatOptions(),
chatOptions: widget.options,
), ),
), ),
), ),
child: StreamBuilder<int>( child: StreamBuilder<int>(
stream: chatService!.getUnreadMessagesCount(userId: widget.userId), stream: chatService.getUnreadMessagesCount(),
builder: (BuildContext context, snapshot) => Stack( builder: (BuildContext context, snapshot) => Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [

View file

@ -25,19 +25,15 @@ class FlutterChatNavigatorUserstory extends StatefulWidget {
/// Constructs a [FlutterChatNavigatorUserstory]. /// Constructs a [FlutterChatNavigatorUserstory].
const FlutterChatNavigatorUserstory({ const FlutterChatNavigatorUserstory({
required this.userId, required this.userId,
this.chatService, required this.options,
this.chatOptions,
super.key, super.key,
}); });
/// The user ID of the person currently looking at the chat /// The user ID of the person currently looking at the chat
final String userId; final String userId;
/// The chat service associated with the widget.
final ChatService? chatService;
/// The chat options /// The chat options
final ChatOptions? chatOptions; final ChatOptions options;
@override @override
State<FlutterChatNavigatorUserstory> createState() => State<FlutterChatNavigatorUserstory> createState() =>
@ -46,14 +42,18 @@ class FlutterChatNavigatorUserstory extends StatefulWidget {
class _FlutterChatNavigatorUserstoryState class _FlutterChatNavigatorUserstoryState
extends State<FlutterChatNavigatorUserstory> { extends State<FlutterChatNavigatorUserstory> {
late ChatService _service = widget.chatService ?? ChatService(); late ChatService _service = ChatService(
userId: widget.userId,
chatRepository: widget.options.chatRepository,
userRepository: widget.options.userRepository,
);
late final PopHandler _popHandler = PopHandler(); late final PopHandler _popHandler = PopHandler();
@override @override
Widget build(BuildContext context) => ChatScope( Widget build(BuildContext context) => ChatScope(
userId: widget.userId, userId: widget.userId,
options: widget.chatOptions ?? const ChatOptions(), options: widget.options,
service: _service, service: _service,
popHandler: _popHandler, popHandler: _popHandler,
child: NavigatorPopHandler( child: NavigatorPopHandler(
@ -67,7 +67,7 @@ class _FlutterChatNavigatorUserstoryState
builder: (context) => _NavigatorWrapper( builder: (context) => _NavigatorWrapper(
userId: widget.userId, userId: widget.userId,
chatService: _service, chatService: _service,
chatOptions: widget.chatOptions ?? const ChatOptions(), chatOptions: widget.options,
), ),
), ),
), ),
@ -79,9 +79,13 @@ class _FlutterChatNavigatorUserstoryState
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (oldWidget.userId != widget.userId || if (oldWidget.userId != widget.userId ||
oldWidget.chatOptions != widget.chatOptions) { oldWidget.options != widget.options) {
setState(() { setState(() {
_service = widget.chatService ?? ChatService(); _service = ChatService(
userId: widget.userId,
chatRepository: widget.options.chatRepository,
userRepository: widget.options.userRepository,
);
}); });
} }
} }
@ -102,7 +106,6 @@ class _NavigatorWrapper extends StatelessWidget {
Widget build(BuildContext context) => chatScreen(context); Widget build(BuildContext context) => chatScreen(context);
Widget chatScreen(BuildContext context) => ChatScreen( Widget chatScreen(BuildContext context) => ChatScreen(
userId: userId,
chatService: chatService, chatService: chatService,
chatOptions: chatOptions, chatOptions: chatOptions,
onPressChat: (chat) => route(context, chatDetailScreen(context, chat)), onPressChat: (chat) => route(context, chatDetailScreen(context, chat)),
@ -115,8 +118,7 @@ class _NavigatorWrapper extends StatelessWidget {
Widget chatDetailScreen(BuildContext context, ChatModel chat) => Widget chatDetailScreen(BuildContext context, ChatModel chat) =>
ChatDetailScreen( ChatDetailScreen(
chat: chat, chat: chat,
onReadChat: (chat) async => onReadChat: (chat) async => chatService.markAsRead(chatId: chat.id),
chatService.markAsRead(chatId: chat.id, userId: userId),
onPressChatTitle: (chat) async { onPressChatTitle: (chat) async {
if (chat.isGroupChat) { if (chat.isGroupChat) {
return route(context, chatProfileScreen(context, null, chat)); return route(context, chatProfileScreen(context, null, chat));

View file

@ -204,7 +204,6 @@ class _BodyState extends State<_Body> {
var chatScope = ChatScope.of(context); var chatScope = ChatScope.of(context);
var options = chatScope.options; var options = chatScope.options;
var service = chatScope.service; var service = chatScope.service;
var userId = chatScope.userId;
void handleScroll(PointerMoveEvent event) { void handleScroll(PointerMoveEvent event) {
if (!showIndicator && if (!showIndicator &&
@ -233,7 +232,6 @@ class _BodyState extends State<_Body> {
alignment: options.chatAlignment ?? Alignment.bottomCenter, alignment: options.chatAlignment ?? Alignment.bottomCenter,
child: StreamBuilder<List<MessageModel>?>( child: StreamBuilder<List<MessageModel>?>(
stream: service.getMessages( stream: service.getMessages(
userId: userId,
chatId: widget.chat.id, chatId: widget.chat.id,
pageSize: pageSize, pageSize: pageSize,
page: page, page: page,

View file

@ -4,6 +4,7 @@ import "package:flutter_chat/src/config/chat_options.dart";
import "package:flutter_chat/src/config/chat_translations.dart"; import "package:flutter_chat/src/config/chat_translations.dart";
import "package:flutter_chat/src/config/screen_types.dart"; import "package:flutter_chat/src/config/screen_types.dart";
import "package:flutter_chat/src/services/date_formatter.dart"; import "package:flutter_chat/src/services/date_formatter.dart";
import "package:flutter_chat/src/util/scope.dart";
import "package:flutter_profile/flutter_profile.dart"; import "package:flutter_profile/flutter_profile.dart";
/// The chat screen /// The chat screen
@ -11,7 +12,6 @@ import "package:flutter_profile/flutter_profile.dart";
class ChatScreen extends StatelessWidget { class ChatScreen extends StatelessWidget {
/// Constructs a [ChatScreen] /// Constructs a [ChatScreen]
const ChatScreen({ const ChatScreen({
required this.userId,
required this.chatService, required this.chatService,
required this.chatOptions, required this.chatOptions,
required this.onPressChat, required this.onPressChat,
@ -20,9 +20,6 @@ class ChatScreen extends StatelessWidget {
super.key, super.key,
}); });
/// The user ID of the person currently looking at the chat
final String userId;
/// The chat service /// The chat service
final ChatService chatService; final ChatService chatService;
@ -43,12 +40,10 @@ class ChatScreen extends StatelessWidget {
if (chatOptions.builders.baseScreenBuilder == null) { if (chatOptions.builders.baseScreenBuilder == null) {
return Scaffold( return Scaffold(
appBar: _AppBar( appBar: _AppBar(
userId: userId,
chatOptions: chatOptions, chatOptions: chatOptions,
chatService: chatService, chatService: chatService,
), ),
body: _Body( body: _Body(
userId: userId,
chatOptions: chatOptions, chatOptions: chatOptions,
chatService: chatService, chatService: chatService,
onPressChat: onPressChat, onPressChat: onPressChat,
@ -62,12 +57,10 @@ class ChatScreen extends StatelessWidget {
context, context,
mapScreenType, mapScreenType,
_AppBar( _AppBar(
userId: userId,
chatOptions: chatOptions, chatOptions: chatOptions,
chatService: chatService, chatService: chatService,
), ),
_Body( _Body(
userId: userId,
chatOptions: chatOptions, chatOptions: chatOptions,
chatService: chatService, chatService: chatService,
onPressChat: onPressChat, onPressChat: onPressChat,
@ -80,12 +73,10 @@ class ChatScreen extends StatelessWidget {
class _AppBar extends StatelessWidget implements PreferredSizeWidget { class _AppBar extends StatelessWidget implements PreferredSizeWidget {
const _AppBar({ const _AppBar({
required this.userId,
required this.chatOptions, required this.chatOptions,
required this.chatService, required this.chatService,
}); });
final String userId;
final ChatOptions chatOptions; final ChatOptions chatOptions;
final ChatService chatService; final ChatService chatService;
@ -100,7 +91,7 @@ class _AppBar extends StatelessWidget implements PreferredSizeWidget {
), ),
actions: [ actions: [
StreamBuilder<int>( StreamBuilder<int>(
stream: chatService.getUnreadMessagesCount(userId: userId), stream: chatService.getUnreadMessagesCount(),
builder: (BuildContext context, snapshot) => Align( builder: (BuildContext context, snapshot) => Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: Visibility( child: Visibility(
@ -127,7 +118,6 @@ class _AppBar extends StatelessWidget implements PreferredSizeWidget {
class _Body extends StatefulWidget { class _Body extends StatefulWidget {
const _Body({ const _Body({
required this.userId,
required this.chatOptions, required this.chatOptions,
required this.chatService, required this.chatService,
required this.onPressChat, required this.onPressChat,
@ -135,7 +125,6 @@ class _Body extends StatefulWidget {
this.onPressStartChat, this.onPressStartChat,
}); });
final String userId;
final ChatOptions chatOptions; final ChatOptions chatOptions;
final ChatService chatService; final ChatService chatService;
final Function(ChatModel chat) onPressChat; final Function(ChatModel chat) onPressChat;
@ -163,7 +152,7 @@ class _BodyState extends State<_Body> {
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 28), padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 28),
children: [ children: [
StreamBuilder<List<ChatModel>?>( StreamBuilder<List<ChatModel>?>(
stream: widget.chatService.getChats(userId: widget.userId), stream: widget.chatService.getChats(),
builder: (BuildContext context, snapshot) { builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done && if (snapshot.connectionState == ConnectionState.done &&
(snapshot.data?.isEmpty ?? true) || (snapshot.data?.isEmpty ?? true) ||
@ -251,7 +240,6 @@ class _BodyState extends State<_Body> {
service: widget.chatService, service: widget.chatService,
chat: chat, chat: chat,
chatOptions: widget.chatOptions, chatOptions: widget.chatOptions,
userId: widget.userId,
onPressChat: widget.onPressChat, onPressChat: widget.onPressChat,
), ),
) )
@ -259,7 +247,6 @@ class _BodyState extends State<_Body> {
service: widget.chatService, service: widget.chatService,
chat: chat, chat: chat,
chatOptions: widget.chatOptions, chatOptions: widget.chatOptions,
userId: widget.userId,
onPressChat: widget.onPressChat, onPressChat: widget.onPressChat,
), ),
), ),
@ -308,14 +295,12 @@ class _ChatItem extends StatelessWidget {
required this.chat, required this.chat,
required this.chatOptions, required this.chatOptions,
required this.service, required this.service,
required this.userId,
required this.onPressChat, required this.onPressChat,
}); });
final ChatModel chat; final ChatModel chat;
final ChatOptions chatOptions; final ChatOptions chatOptions;
final ChatService service; final ChatService service;
final String userId;
final Function(ChatModel chat) onPressChat; final Function(ChatModel chat) onPressChat;
@override @override
@ -335,7 +320,6 @@ class _ChatItem extends StatelessWidget {
options: chatOptions, options: chatOptions,
dateFormatter: dateFormatter, dateFormatter: dateFormatter,
chatService: service, chatService: service,
currentUserId: userId,
), ),
) ?? ) ??
DecoratedBox( DecoratedBox(
@ -355,7 +339,6 @@ class _ChatItem extends StatelessWidget {
options: chatOptions, options: chatOptions,
dateFormatter: dateFormatter, dateFormatter: dateFormatter,
chatService: service, chatService: service,
currentUserId: userId,
), ),
), ),
), ),
@ -368,18 +351,18 @@ class _ChatListItem extends StatelessWidget {
required this.chat, required this.chat,
required this.options, required this.options,
required this.dateFormatter, required this.dateFormatter,
required this.currentUserId,
required this.chatService, required this.chatService,
}); });
final ChatModel chat; final ChatModel chat;
final ChatOptions options; final ChatOptions options;
final DateFormatter dateFormatter; final DateFormatter dateFormatter;
final String currentUserId;
final ChatService chatService; final ChatService chatService;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var scope = ChatScope.of(context);
var currentUserId = scope.userId;
var translations = options.translations; var translations = options.translations;
if (chat.isGroupChat) { if (chat.isGroupChat) {
return StreamBuilder<MessageModel?>( return StreamBuilder<MessageModel?>(