feat: rework the existing navigator userstory to look more like flutter_availability userstory

This commit is contained in:
Freek van de Ven 2025-02-12 10:30:54 +01:00 committed by Bart Ribbers
parent 5414ab237f
commit a26e5d92f0
4 changed files with 137 additions and 110 deletions

View file

@ -3,7 +3,7 @@ export "package:chat_repository_interface/chat_repository_interface.dart";
// User story
export "package:flutter_chat/src/flutter_chat_entry_widget.dart";
export "package:flutter_chat/src/flutter_chat_navigator_userstory.dart";
export "package:flutter_chat/src/flutter_chat_navigator_userstories.dart";
// Options
export "src/config/chat_builders.dart";

View file

@ -81,7 +81,7 @@ class _FlutterChatEntryWidgetState extends State<FlutterChatEntryWidget> {
Widget build(BuildContext context) => InkWell(
onTap: () async =>
widget.onTap?.call() ??
Navigator.of(context).pushReplacement(
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => FlutterChatNavigatorUserstory(
userId: widget.userId,
@ -122,7 +122,7 @@ class _FlutterChatEntryWidgetState extends State<FlutterChatEntryWidget> {
),
child: Center(
child: Text(
"${snapshot.data ?? 0}",
snapshot.data?.toString() ?? "0",
style: widget.textStyle,
),
),

View file

@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: 2023 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import "package:chat_repository_interface/chat_repository_interface.dart";
import "package:flutter/material.dart";
import "package:flutter_chat/src/config/chat_options.dart";
import "package:flutter_chat/src/routes.dart";
import "package:flutter_chat/src/services/pop_handler.dart";
import "package:flutter_chat/src/util/scope.dart";
/// The flutter chat navigator userstory
/// [userId] is the id of the user
/// [chatService] is the chat service
/// [chatOptions] are the chat options
/// This widget is the entry point for the chat UI
class FlutterChatNavigatorUserstory extends StatefulWidget {
/// Constructs a [FlutterChatNavigatorUserstory].
const FlutterChatNavigatorUserstory({
required this.userId,
required this.options,
super.key,
});
/// The user ID of the person currently looking at the chat
final String userId;
/// The chat options
final ChatOptions options;
@override
State<FlutterChatNavigatorUserstory> createState() =>
_FlutterChatNavigatorUserstoryState();
}
class _FlutterChatNavigatorUserstoryState
extends State<FlutterChatNavigatorUserstory> {
late ChatService _service = ChatService(
userId: widget.userId,
chatRepository: widget.options.chatRepository,
userRepository: widget.options.userRepository,
);
late final PopHandler _popHandler = PopHandler();
final GlobalKey<NavigatorState> _nestedNavigatorKey =
GlobalKey<NavigatorState>();
@override
Widget build(BuildContext context) => ChatScope(
userId: widget.userId,
options: widget.options,
service: _service,
popHandler: _popHandler,
child: NavigatorPopHandler(
// ignore: deprecated_member_use
onPop: () => _popHandler.handlePop(),
child: Navigator(
key: _nestedNavigatorKey,
onGenerateRoute: (settings) => MaterialPageRoute(
builder: (context) => NavigatorWrapper(
userId: widget.userId,
chatService: _service,
chatOptions: widget.options,
),
),
),
),
);
@override
void didUpdateWidget(covariant FlutterChatNavigatorUserstory oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.userId != widget.userId ||
oldWidget.options != widget.options) {
setState(() {
_service = ChatService(
userId: widget.userId,
chatRepository: widget.options.chatRepository,
userRepository: widget.options.userRepository,
);
});
}
}
}

View file

@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2023 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import "dart:async";
import "package:chat_repository_interface/chat_repository_interface.dart";
@ -13,125 +9,66 @@ import "package:flutter_chat/src/screens/chat_screen.dart";
import "package:flutter_chat/src/screens/creation/new_chat_screen.dart";
import "package:flutter_chat/src/screens/creation/new_group_chat_overview.dart";
import "package:flutter_chat/src/screens/creation/new_group_chat_screen.dart";
import "package:flutter_chat/src/services/pop_handler.dart";
import "package:flutter_chat/src/util/scope.dart";
/// The flutter chat navigator userstory
/// [userId] is the id of the user
/// [chatService] is the chat service
/// [chatOptions] are the chat options
/// This widget is the entry point for the chat UI
class FlutterChatNavigatorUserstory extends StatefulWidget {
/// Constructs a [FlutterChatNavigatorUserstory].
const FlutterChatNavigatorUserstory({
required this.userId,
required this.options,
super.key,
});
/// The user ID of the person currently looking at the chat
final String userId;
/// The chat options
final ChatOptions options;
@override
State<FlutterChatNavigatorUserstory> createState() =>
_FlutterChatNavigatorUserstoryState();
}
class _FlutterChatNavigatorUserstoryState
extends State<FlutterChatNavigatorUserstory> {
late ChatService _service = ChatService(
userId: widget.userId,
chatRepository: widget.options.chatRepository,
userRepository: widget.options.userRepository,
);
late final PopHandler _popHandler = PopHandler();
@override
Widget build(BuildContext context) => ChatScope(
userId: widget.userId,
options: widget.options,
service: _service,
popHandler: _popHandler,
child: NavigatorPopHandler(
// ignore: deprecated_member_use
onPop: _popHandler.handlePop,
child: Navigator(
key: const ValueKey(
"chat_navigator",
),
onGenerateRoute: (settings) => MaterialPageRoute(
builder: (context) => _NavigatorWrapper(
userId: widget.userId,
chatService: _service,
chatOptions: widget.options,
),
),
),
),
);
@override
void didUpdateWidget(covariant FlutterChatNavigatorUserstory oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.userId != widget.userId ||
oldWidget.options != widget.options) {
setState(() {
_service = ChatService(
userId: widget.userId,
chatRepository: widget.options.chatRepository,
userRepository: widget.options.userRepository,
);
});
}
}
}
class _NavigatorWrapper extends StatelessWidget {
const _NavigatorWrapper({
/// The navigator wrapper
class NavigatorWrapper extends StatelessWidget {
/// Constructs a [NavigatorWrapper].
const NavigatorWrapper({
required this.userId,
required this.chatService,
required this.chatOptions,
super.key,
});
/// The user ID of the person starting the chat userstory
final String userId;
/// The chat service containing the chat repository and user repository
final ChatService chatService;
/// The chat userstory configuration
final ChatOptions chatOptions;
@override
Widget build(BuildContext context) => chatScreen(context);
/// The chat overview screen
Widget chatScreen(BuildContext context) => ChatScreen(
chatService: chatService,
chatOptions: chatOptions,
onPressChat: (chat) => route(context, chatDetailScreen(context, chat)),
onPressChat: (chat) async =>
_routeToScreen(context, chatDetailScreen(context, chat)),
onDeleteChat: (chat) async {
await chatService.deleteChat(chatId: chat.id);
},
onPressStartChat: () => route(context, newChatScreen(context)),
onPressStartChat: () async =>
_routeToScreen(context, newChatScreen(context)),
);
/// The chat screen
Widget chatDetailScreen(BuildContext context, ChatModel chat) =>
ChatDetailScreen(
chat: chat,
onReadChat: (chat) async => chatService.markAsRead(chatId: chat.id),
onPressChatTitle: (chat) async {
if (chat.isGroupChat) {
return route(context, chatProfileScreen(context, null, chat));
return _routeToScreen(
context,
chatProfileScreen(context, null, chat),
);
}
var otherUserId = chat.getOtherUser(userId);
var otherUser = await chatService.getUser(userId: otherUserId).first;
if (!context.mounted) return;
return route(context, chatProfileScreen(context, otherUser, null));
return _routeToScreen(
context,
chatProfileScreen(context, otherUser, null),
);
},
onPressUserProfile: (user) =>
route(context, chatProfileScreen(context, user, null)),
onPressUserProfile: (user) async =>
_routeToScreen(context, chatProfileScreen(context, user, null)),
onUploadImage: (data) async {
var path = await chatService.uploadImage(
path: "chats/${chat.id}-$userId-${DateTime.now()}",
@ -155,6 +92,7 @@ class _NavigatorWrapper extends StatelessWidget {
},
);
/// The chat profile screen
Widget chatProfileScreen(
BuildContext context,
UserModel? user,
@ -170,38 +108,41 @@ class _NavigatorWrapper extends StatelessWidget {
var user = await chatService.getUser(userId: userId).first;
if (!context.mounted) return;
route(context, chatProfileScreen(context, user, null));
await _routeToScreen(context, chatProfileScreen(context, user, null));
},
onPressStartChat: (userId) async {
var chat = await createChat(userId);
var chat = await _createChat(userId);
if (!context.mounted) return;
return route(context, chatDetailScreen(context, chat));
return _routeToScreen(context, chatDetailScreen(context, chat));
},
);
/// The new chat screen
Widget newChatScreen(BuildContext context) => NewChatScreen(
userId: userId,
chatService: chatService,
chatOptions: chatOptions,
onPressCreateGroupChat: () =>
route(context, newGroupChatScreen(context)),
onPressCreateGroupChat: () async =>
_routeToScreen(context, newGroupChatScreen(context)),
onPressCreateChat: (user) async {
var chat = await createChat(user.id);
var chat = await _createChat(user.id);
if (!context.mounted) return;
return route(context, chatDetailScreen(context, chat));
return _routeToScreen(context, chatDetailScreen(context, chat));
},
);
/// The new group chat screen
Widget newGroupChatScreen(BuildContext context) => NewGroupChatScreen(
userId: userId,
chatService: chatService,
chatOptions: chatOptions,
onContinue: (users) =>
route(context, newGroupChatOverview(context, users)),
onContinue: (users) async =>
_routeToScreen(context, newGroupChatOverview(context, users)),
);
/// The new group chat overview screen
Widget newGroupChatOverview(BuildContext context, List<UserModel> users) =>
NewGroupChatOverview(
options: chatOptions,
@ -214,7 +155,7 @@ class _NavigatorWrapper extends StatelessWidget {
image: image,
);
}
var chat = await createGroupChat(
var chat = await _createGroupChat(
users,
title,
description,
@ -222,11 +163,12 @@ class _NavigatorWrapper extends StatelessWidget {
);
if (!context.mounted) return;
return route(context, chatDetailScreen(context, chat));
return _routeToScreen(context, chatDetailScreen(context, chat));
},
);
Future<ChatModel> createGroupChat(
/// Creates a group chat
Future<ChatModel> _createGroupChat(
List<UserModel> userModels,
String title,
String description,
@ -275,7 +217,8 @@ class _NavigatorWrapper extends StatelessWidget {
return chat;
}
Future<ChatModel> createChat(String otherUserId) async {
/// Creates a chat
Future<ChatModel> _createChat(String otherUserId) async {
ChatModel? chat;
try {
@ -311,11 +254,9 @@ class _NavigatorWrapper extends StatelessWidget {
return chat;
}
void route(BuildContext context, Widget screen) {
unawaited(
/// Routes to a new screen for the userstory
Future _routeToScreen(BuildContext context, Widget screen) async =>
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => screen),
),
);
}
);
}