feat: add onback behavior for each userstory screen so it pops correctly with the android backarrow

This commit is contained in:
Freek van de Ven 2025-02-12 11:39:26 +01:00 committed by FlutterJoey
parent 15604bf264
commit d66942893f
8 changed files with 115 additions and 12 deletions

View file

@ -11,7 +11,6 @@ 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 {
@ -19,6 +18,7 @@ class FlutterChatNavigatorUserstory extends StatefulWidget {
const FlutterChatNavigatorUserstory({
required this.userId,
required this.options,
this.onExit,
super.key,
});
@ -28,6 +28,9 @@ class FlutterChatNavigatorUserstory extends StatefulWidget {
/// The chat options
final ChatOptions options;
/// Callback for when the user wants to navigate back to a previous screen
final VoidCallback? onExit;
@override
State<FlutterChatNavigatorUserstory> createState() =>
_FlutterChatNavigatorUserstoryState();
@ -62,6 +65,7 @@ class _FlutterChatNavigatorUserstoryState
userId: widget.userId,
chatService: _service,
chatOptions: widget.options,
onExit: widget.onExit,
),
),
),

View file

@ -17,6 +17,7 @@ class NavigatorWrapper extends StatelessWidget {
required this.userId,
required this.chatService,
required this.chatOptions,
this.onExit,
super.key,
});
@ -29,6 +30,9 @@ class NavigatorWrapper extends StatelessWidget {
/// The chat userstory configuration
final ChatOptions chatOptions;
/// Callback for when the user wants to navigate back
final VoidCallback? onExit;
@override
Widget build(BuildContext context) => chatScreen(context);
@ -36,6 +40,7 @@ class NavigatorWrapper extends StatelessWidget {
Widget chatScreen(BuildContext context) => ChatScreen(
chatService: chatService,
chatOptions: chatOptions,
onExit: onExit,
onPressChat: (chat) async =>
_routeToScreen(context, chatDetailScreen(context, chat)),
onDeleteChat: (chat) async {
@ -49,6 +54,7 @@ class NavigatorWrapper extends StatelessWidget {
Widget chatDetailScreen(BuildContext context, ChatModel chat) =>
ChatDetailScreen(
chat: chat,
onExit: () => Navigator.of(context).pop(),
onReadChat: (chat) async => chatService.markAsRead(chatId: chat.id),
onPressChatTitle: (chat) async {
if (chat.isGroupChat) {
@ -104,6 +110,7 @@ class NavigatorWrapper extends StatelessWidget {
userId: userId,
userModel: user,
chatModel: chat,
onExit: () => Navigator.of(context).pop(),
onTapUser: (userId) async {
var user = await chatService.getUser(userId: userId).first;
@ -123,13 +130,17 @@ class NavigatorWrapper extends StatelessWidget {
userId: userId,
chatService: chatService,
chatOptions: chatOptions,
onExit: () => Navigator.of(context).pop(),
onPressCreateGroupChat: () async =>
_routeToScreen(context, newGroupChatScreen(context)),
onPressCreateChat: (user) async {
var chat = await _createChat(user.id);
if (!context.mounted) return;
return _routeToScreen(context, chatDetailScreen(context, chat));
return _replaceCurrentScreen(
context,
chatDetailScreen(context, chat),
);
},
);
@ -138,8 +149,11 @@ class NavigatorWrapper extends StatelessWidget {
userId: userId,
chatService: chatService,
chatOptions: chatOptions,
onContinue: (users) async =>
_routeToScreen(context, newGroupChatOverview(context, users)),
onExit: () => Navigator.of(context).pop(),
onContinue: (users) async => _replaceCurrentScreen(
context,
newGroupChatOverview(context, users),
),
);
/// The new group chat overview screen
@ -147,6 +161,7 @@ class NavigatorWrapper extends StatelessWidget {
NewGroupChatOverview(
options: chatOptions,
users: users,
onExit: () => Navigator.of(context).pop(),
onComplete: (users, title, description, image) async {
String? path;
if (image != null) {
@ -163,7 +178,10 @@ class NavigatorWrapper extends StatelessWidget {
);
if (!context.mounted) return;
return _routeToScreen(context, chatDetailScreen(context, chat));
return _replaceCurrentScreen(
context,
chatDetailScreen(context, chat),
);
},
);
@ -259,4 +277,10 @@ class NavigatorWrapper extends StatelessWidget {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => screen),
);
/// Replaces the current screen with a new screen for the userstory
Future _replaceCurrentScreen(BuildContext context, Widget screen) async =>
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => screen),
);
}

View file

@ -8,13 +8,15 @@ import "package:flutter_chat/src/config/screen_types.dart";
import "package:flutter_chat/src/screens/chat_detail/widgets/default_message_builder.dart";
import "package:flutter_chat/src/screens/creation/widgets/image_picker.dart";
import "package:flutter_chat/src/util/scope.dart";
import "package:flutter_hooks/flutter_hooks.dart";
/// Chat detail screen
/// Seen when a user clicks on a chat
class ChatDetailScreen extends StatefulWidget {
class ChatDetailScreen extends StatefulHookWidget {
/// Constructs a [ChatDetailScreen].
const ChatDetailScreen({
required this.chat,
required this.onExit,
required this.onPressChatTitle,
required this.onPressUserProfile,
required this.onUploadImage,
@ -45,6 +47,9 @@ class ChatDetailScreen extends StatefulWidget {
/// Callback function to get the chat title
final String Function(ChatModel chat)? getChatTitle;
/// Callback for when the user wants to navigate back
final VoidCallback onExit;
@override
State<ChatDetailScreen> createState() => _ChatDetailScreenState();
}
@ -85,7 +90,8 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
@override
Widget build(BuildContext context) {
var chatOptions = ChatScope.of(context).options;
var chatScope = ChatScope.of(context);
var chatOptions = chatScope.options;
var appBar = _AppBar(
chatTitle: chatTitle,
onPressChatTitle: widget.onPressChatTitle,
@ -100,6 +106,11 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
onReadChat: widget.onReadChat,
);
useEffect(() {
chatScope.popHandler.add(widget.onExit);
return () => chatScope.popHandler.remove(widget.onExit);
});
if (chatOptions.builders.baseScreenBuilder == null) {
return Scaffold(
appBar: appBar,

View file

@ -2,15 +2,18 @@ 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/config/screen_types.dart";
import "package:flutter_chat/src/util/scope.dart";
import "package:flutter_hooks/flutter_hooks.dart";
import "package:flutter_profile/flutter_profile.dart";
/// The chat profile screen
/// Seen when a user taps on a chat profile
/// Also used for group chats
class ChatProfileScreen extends StatelessWidget {
class ChatProfileScreen extends HookWidget {
/// Constructs a [ChatProfileScreen]
const ChatProfileScreen({
required this.options,
required this.onExit,
required this.userId,
required this.userModel,
required this.service,
@ -41,8 +44,18 @@ class ChatProfileScreen extends StatelessWidget {
/// Callback function triggered when the start chat button is pressed
final Function(String)? onPressStartChat;
/// Callback for when the user wants to navigate back
final VoidCallback onExit;
@override
Widget build(BuildContext context) {
var chatScope = ChatScope.of(context);
useEffect(() {
chatScope.popHandler.add(onExit);
return () => chatScope.popHandler.remove(onExit);
});
if (options.builders.baseScreenBuilder == null) {
return Scaffold(
appBar: _AppBar(

View file

@ -5,17 +5,19 @@ import "package:flutter_chat/src/config/chat_translations.dart";
import "package:flutter_chat/src/config/screen_types.dart";
import "package:flutter_chat/src/services/date_formatter.dart";
import "package:flutter_chat/src/util/scope.dart";
import "package:flutter_hooks/flutter_hooks.dart";
import "package:flutter_profile/flutter_profile.dart";
/// The chat screen
/// Seen when a user is chatting
class ChatScreen extends StatelessWidget {
class ChatScreen extends HookWidget {
/// Constructs a [ChatScreen]
const ChatScreen({
required this.chatService,
required this.chatOptions,
required this.onPressChat,
required this.onDeleteChat,
required this.onExit,
this.onPressStartChat,
super.key,
});
@ -35,8 +37,19 @@ class ChatScreen extends StatelessWidget {
/// Callback function for deleting a chat.
final void Function(ChatModel chat) onDeleteChat;
/// Callback for when the user wants to navigate back
final VoidCallback? onExit;
@override
Widget build(BuildContext context) {
var chatScope = ChatScope.of(context);
useEffect(() {
if (onExit == null) return null;
chatScope.popHandler.add(onExit!);
return () => chatScope.popHandler.remove(onExit!);
});
if (chatOptions.builders.baseScreenBuilder == null) {
return Scaffold(
appBar: _AppBar(

View file

@ -5,13 +5,16 @@ import "package:flutter_chat/src/config/screen_types.dart";
import "package:flutter_chat/src/screens/creation/widgets/search_field.dart";
import "package:flutter_chat/src/screens/creation/widgets/search_icon.dart";
import "package:flutter_chat/src/screens/creation/widgets/user_list.dart";
import "package:flutter_chat/src/util/scope.dart";
import "package:flutter_hooks/flutter_hooks.dart";
/// New chat screen
/// This screen is used to create a new chat
class NewChatScreen extends StatefulWidget {
class NewChatScreen extends StatefulHookWidget {
/// Constructs a [NewChatScreen]
const NewChatScreen({
required this.userId,
required this.onExit,
required this.chatService,
required this.chatOptions,
required this.onPressCreateGroupChat,
@ -34,6 +37,9 @@ class NewChatScreen extends StatefulWidget {
/// Callback function triggered when a user is tapped
final Function(UserModel) onPressCreateChat;
/// Callback for when the user wants to navigate back
final VoidCallback onExit;
@override
State<NewChatScreen> createState() => _NewChatScreenState();
}
@ -45,6 +51,13 @@ class _NewChatScreenState extends State<NewChatScreen> {
@override
Widget build(BuildContext context) {
var chatScope = ChatScope.of(context);
useEffect(() {
chatScope.popHandler.add(widget.onExit);
return () => chatScope.popHandler.remove(widget.onExit);
});
if (widget.chatOptions.builders.baseScreenBuilder == null) {
return Scaffold(
appBar: _AppBar(

View file

@ -5,15 +5,18 @@ import "package:flutter/material.dart";
import "package:flutter_chat/src/config/chat_options.dart";
import "package:flutter_chat/src/config/screen_types.dart";
import "package:flutter_chat/src/screens/creation/widgets/image_picker.dart";
import "package:flutter_chat/src/util/scope.dart";
import "package:flutter_hooks/flutter_hooks.dart";
import "package:flutter_profile/flutter_profile.dart";
/// New group chat overview
/// Seen after the user has selected the users they
/// want to add to the group chat
class NewGroupChatOverview extends StatelessWidget {
class NewGroupChatOverview extends HookWidget {
/// Constructs a [NewGroupChatOverview]
const NewGroupChatOverview({
required this.options,
required this.onExit,
required this.users,
required this.onComplete,
super.key,
@ -25,6 +28,9 @@ class NewGroupChatOverview extends StatelessWidget {
/// The users to be added to the group chat
final List<UserModel> users;
/// Callback for when the user wants to navigate back
final VoidCallback onExit;
/// Callback function triggered when the group chat is created
final Function(
List<UserModel> users,
@ -35,6 +41,13 @@ class NewGroupChatOverview extends StatelessWidget {
@override
Widget build(BuildContext context) {
var chatScope = ChatScope.of(context);
useEffect(() {
chatScope.popHandler.add(onExit);
return () => chatScope.popHandler.remove(onExit);
});
if (options.builders.baseScreenBuilder == null) {
return Scaffold(
appBar: _AppBar(

View file

@ -5,13 +5,16 @@ import "package:flutter_chat/src/config/screen_types.dart";
import "package:flutter_chat/src/screens/creation/widgets/search_field.dart";
import "package:flutter_chat/src/screens/creation/widgets/search_icon.dart";
import "package:flutter_chat/src/screens/creation/widgets/user_list.dart";
import "package:flutter_chat/src/util/scope.dart";
import "package:flutter_hooks/flutter_hooks.dart";
/// New group chat screen
/// This screen is used to create a new group chat
class NewGroupChatScreen extends StatefulWidget {
class NewGroupChatScreen extends StatefulHookWidget {
/// Constructs a [NewGroupChatScreen]
const NewGroupChatScreen({
required this.userId,
required this.onExit,
required this.chatService,
required this.chatOptions,
required this.onContinue,
@ -27,6 +30,9 @@ class NewGroupChatScreen extends StatefulWidget {
/// The chat options
final ChatOptions chatOptions;
/// Callback for when the user wants to navigate back
final VoidCallback onExit;
/// Callback function triggered when the continue button is pressed
final Function(List<UserModel>) onContinue;
@ -43,6 +49,12 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
@override
Widget build(BuildContext context) {
var chatScope = ChatScope.of(context);
useEffect(() {
chatScope.popHandler.add(widget.onExit);
return () => chatScope.popHandler.remove(widget.onExit);
});
if (widget.chatOptions.builders.baseScreenBuilder == null) {
return Scaffold(
appBar: _AppBar(