mirror of
https://github.com/Iconica-Development/flutter_chat.git
synced 2025-05-18 18:33:49 +02:00
feat: add semantics for buttons
This commit is contained in:
parent
b3b8b1828e
commit
371ff6c335
15 changed files with 579 additions and 366 deletions
|
@ -24,6 +24,7 @@
|
|||
- Added enabled boolean to the messageInputBuilder and made parameters named
|
||||
- Added autoScrollTriggerOffset to the ChatPaginationControls to adjust when the auto scroll should be enabled
|
||||
- Added the ability to set the color of the CircularProgressIndicator of the ImageLoadingSnackbar by theme.snackBarTheme.actionTextColor
|
||||
- Added semantics for variable text, buttons and textfields
|
||||
|
||||
## 4.0.0
|
||||
- Move to the new user story architecture
|
||||
|
|
|
@ -33,6 +33,25 @@ class ChatSemantics {
|
|||
required this.newChatBioInput,
|
||||
required this.newChatSearchInput,
|
||||
required this.newGroupChatSearchInput,
|
||||
required this.profileStartChatButton,
|
||||
required this.chatsStartChatButton,
|
||||
required this.chatsDeleteConfirmButton,
|
||||
required this.newChatCreateGroupChatButton,
|
||||
required this.newGroupChatCreateGroupChatButton,
|
||||
required this.newGroupChatNextButton,
|
||||
required this.imagePickerCancelButton,
|
||||
required this.chatSelectImageIconButton,
|
||||
required this.chatSendMessageIconButton,
|
||||
required this.newChatSearchIconButton,
|
||||
required this.newGroupChatSearchIconButton,
|
||||
required this.chatBackButton,
|
||||
required this.chatTitleButton,
|
||||
required this.newGroupChatSelectImage,
|
||||
required this.newGroupChatRemoveImage,
|
||||
required this.newGroupChatRemoveUser,
|
||||
required this.profileTapUserButton,
|
||||
required this.chatsOpenChatButton,
|
||||
required this.userListTapUser,
|
||||
});
|
||||
|
||||
/// Default translations for the chat component view
|
||||
|
@ -58,6 +77,25 @@ class ChatSemantics {
|
|||
this.newChatBioInput = "input_text_bio",
|
||||
this.newChatSearchInput = "input_text_search",
|
||||
this.newGroupChatSearchInput = "input_text_search",
|
||||
this.profileStartChatButton = "button_start_chat",
|
||||
this.chatsStartChatButton = "button_start_chat",
|
||||
this.chatsDeleteConfirmButton = "button_delete_chat_confirm",
|
||||
this.newChatCreateGroupChatButton = "button_create_group_chat",
|
||||
this.newGroupChatCreateGroupChatButton = "button_create_group_chat",
|
||||
this.newGroupChatNextButton = "button_next",
|
||||
this.imagePickerCancelButton = "button_cancel",
|
||||
this.chatSelectImageIconButton = "button_icon_select_image",
|
||||
this.chatSendMessageIconButton = "button_icon_send_message",
|
||||
this.newChatSearchIconButton = "button_icon_search",
|
||||
this.newGroupChatSearchIconButton = "button_icon_search",
|
||||
this.chatBackButton = "button_back",
|
||||
this.chatTitleButton = "button_open_profile",
|
||||
this.newGroupChatSelectImage = "button_select_image",
|
||||
this.newGroupChatRemoveImage = "button_remove_image",
|
||||
this.newGroupChatRemoveUser = "button_remove_user",
|
||||
this.profileTapUserButton = _defaultProfileTapUserButton,
|
||||
this.chatsOpenChatButton = _defaultChatsOpenChatButton,
|
||||
this.userListTapUser = _defaultUserListTapUser,
|
||||
});
|
||||
|
||||
// Text
|
||||
|
@ -87,6 +125,33 @@ class ChatSemantics {
|
|||
final String newChatSearchInput;
|
||||
final String newGroupChatSearchInput;
|
||||
|
||||
// Buttons
|
||||
final String profileStartChatButton;
|
||||
final String chatsStartChatButton;
|
||||
final String chatsDeleteConfirmButton;
|
||||
final String newChatCreateGroupChatButton;
|
||||
final String newGroupChatCreateGroupChatButton;
|
||||
final String newGroupChatNextButton;
|
||||
final String imagePickerCancelButton;
|
||||
|
||||
// Icon buttons
|
||||
final String chatSelectImageIconButton;
|
||||
final String chatSendMessageIconButton;
|
||||
final String newChatSearchIconButton;
|
||||
final String newGroupChatSearchIconButton;
|
||||
|
||||
// Inkwells
|
||||
final String chatBackButton;
|
||||
final String chatTitleButton;
|
||||
final String newGroupChatSelectImage;
|
||||
final String newGroupChatRemoveImage;
|
||||
final String newGroupChatRemoveUser;
|
||||
|
||||
// Indexed inkwells
|
||||
final String Function(int index) profileTapUserButton;
|
||||
final String Function(int index) chatsOpenChatButton;
|
||||
final String Function(int index) userListTapUser;
|
||||
|
||||
ChatSemantics copyWith({
|
||||
String? profileTitle,
|
||||
String? profileDescription,
|
||||
|
@ -109,6 +174,25 @@ class ChatSemantics {
|
|||
String? newChatBioInput,
|
||||
String? newChatSearchInput,
|
||||
String? newGroupChatSearchInput,
|
||||
String? profileStartChatButton,
|
||||
String? chatsStartChatButton,
|
||||
String? chatsDeleteConfirmButton,
|
||||
String? newChatCreateGroupChatButton,
|
||||
String? newGroupChatCreateGroupChatButton,
|
||||
String? newGroupChatNextButton,
|
||||
String? imagePickerCancelButton,
|
||||
String? chatSelectImageIconButton,
|
||||
String? chatSendMessageIconButton,
|
||||
String? newChatSearchIconButton,
|
||||
String? newGroupChatSearchIconButton,
|
||||
String? chatBackButton,
|
||||
String? chatTitleButton,
|
||||
String? newGroupChatSelectImage,
|
||||
String? newGroupChatRemoveImage,
|
||||
String? newGroupChatRemoveUser,
|
||||
String Function(int)? profileTapUserButton,
|
||||
String Function(int)? chatsOpenChatButton,
|
||||
String Function(int)? userListTapUser,
|
||||
}) =>
|
||||
ChatSemantics(
|
||||
profileTitle: profileTitle ?? this.profileTitle,
|
||||
|
@ -137,6 +221,38 @@ class ChatSemantics {
|
|||
newChatSearchInput: newChatSearchInput ?? this.newChatSearchInput,
|
||||
newGroupChatSearchInput:
|
||||
newGroupChatSearchInput ?? this.newGroupChatSearchInput,
|
||||
profileStartChatButton:
|
||||
profileStartChatButton ?? this.profileStartChatButton,
|
||||
chatsStartChatButton: chatsStartChatButton ?? this.chatsStartChatButton,
|
||||
chatsDeleteConfirmButton:
|
||||
chatsDeleteConfirmButton ?? this.chatsDeleteConfirmButton,
|
||||
newChatCreateGroupChatButton:
|
||||
newChatCreateGroupChatButton ?? this.newChatCreateGroupChatButton,
|
||||
newGroupChatCreateGroupChatButton: newGroupChatCreateGroupChatButton ??
|
||||
this.newGroupChatCreateGroupChatButton,
|
||||
newGroupChatNextButton:
|
||||
newGroupChatNextButton ?? this.newGroupChatNextButton,
|
||||
imagePickerCancelButton:
|
||||
imagePickerCancelButton ?? this.imagePickerCancelButton,
|
||||
chatSelectImageIconButton:
|
||||
chatSelectImageIconButton ?? this.chatSelectImageIconButton,
|
||||
chatSendMessageIconButton:
|
||||
chatSendMessageIconButton ?? this.chatSendMessageIconButton,
|
||||
newChatSearchIconButton:
|
||||
newChatSearchIconButton ?? this.newChatSearchIconButton,
|
||||
newGroupChatSearchIconButton:
|
||||
newGroupChatSearchIconButton ?? this.newGroupChatSearchIconButton,
|
||||
chatBackButton: chatBackButton ?? this.chatBackButton,
|
||||
chatTitleButton: chatTitleButton ?? this.chatTitleButton,
|
||||
newGroupChatSelectImage:
|
||||
newGroupChatSelectImage ?? this.newGroupChatSelectImage,
|
||||
newGroupChatRemoveImage:
|
||||
newGroupChatRemoveImage ?? this.newGroupChatRemoveImage,
|
||||
newGroupChatRemoveUser:
|
||||
newGroupChatRemoveUser ?? this.newGroupChatRemoveUser,
|
||||
profileTapUserButton: profileTapUserButton ?? this.profileTapUserButton,
|
||||
chatsOpenChatButton: chatsOpenChatButton ?? this.chatsOpenChatButton,
|
||||
userListTapUser: userListTapUser ?? this.userListTapUser,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -150,3 +266,6 @@ String _defaultChatsChatSubTitle(int index) => "text_chat_sub_title_$index";
|
|||
String _defaultChatsChatLastUsed(int index) => "text_chat_last_used_$index";
|
||||
String _defaultChatsChatUnreadMessages(int index) =>
|
||||
"text_chat_unread_messages_$index";
|
||||
String _defaultProfileTapUserButton(int index) => "button_tap_user_$index";
|
||||
String _defaultChatsOpenChatButton(int index) => "button_open_chat_$index";
|
||||
String _defaultUserListTapUser(int index) => "button_tap_user_$index";
|
||||
|
|
|
@ -18,6 +18,7 @@ class FlutterChatEntryWidget extends StatefulWidget {
|
|||
this.counterBackgroundColor = Colors.red,
|
||||
this.textStyle,
|
||||
this.semanticIdUnreadMessages = "text_unread_messages_count",
|
||||
this.semanticIdOpenButton = "button_open_chat",
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
@ -51,6 +52,9 @@ class FlutterChatEntryWidget extends StatefulWidget {
|
|||
/// Semantic Id for the unread messages text
|
||||
final String semanticIdUnreadMessages;
|
||||
|
||||
/// Semantic Id for the unread messages text
|
||||
final String semanticIdOpenButton;
|
||||
|
||||
@override
|
||||
State<FlutterChatEntryWidget> createState() => _FlutterChatEntryWidgetState();
|
||||
}
|
||||
|
@ -83,61 +87,65 @@ class _FlutterChatEntryWidgetState extends State<FlutterChatEntryWidget> {
|
|||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => InkWell(
|
||||
onTap: () async =>
|
||||
widget.onTap?.call() ??
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => FlutterChatNavigatorUserstory(
|
||||
userId: widget.userId,
|
||||
options: widget.options ?? ChatOptions(),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: StreamBuilder<int>(
|
||||
stream: chatService.getUnreadMessagesCount(),
|
||||
builder: (BuildContext context, snapshot) => Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: widget.widgetSize,
|
||||
height: widget.widgetSize,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: widget.backgroundColor,
|
||||
),
|
||||
child: _AnimatedNotificationIcon(
|
||||
icon: Icon(
|
||||
widget.icon,
|
||||
color: widget.iconColor,
|
||||
size: widget.widgetSize / 1.5,
|
||||
Widget build(BuildContext context) => CustomSemantics(
|
||||
identifier: widget.semanticIdOpenButton,
|
||||
buttonWithVariableText: true,
|
||||
child: InkWell(
|
||||
onTap: () async =>
|
||||
widget.onTap?.call() ??
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => FlutterChatNavigatorUserstory(
|
||||
userId: widget.userId,
|
||||
options: widget.options ?? ChatOptions(),
|
||||
),
|
||||
notifications: snapshot.data ?? 0,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 0.0,
|
||||
top: 0.0,
|
||||
child: Container(
|
||||
width: widget.widgetSize / 2,
|
||||
height: widget.widgetSize / 2,
|
||||
child: StreamBuilder<int>(
|
||||
stream: chatService.getUnreadMessagesCount(),
|
||||
builder: (BuildContext context, snapshot) => Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: widget.widgetSize,
|
||||
height: widget.widgetSize,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: widget.counterBackgroundColor,
|
||||
color: widget.backgroundColor,
|
||||
),
|
||||
child: Center(
|
||||
child: CustomSemantics(
|
||||
identifier: widget.semanticIdUnreadMessages,
|
||||
value: snapshot.data?.toString() ?? "0",
|
||||
child: Text(
|
||||
snapshot.data?.toString() ?? "0",
|
||||
style: widget.textStyle,
|
||||
child: _AnimatedNotificationIcon(
|
||||
icon: Icon(
|
||||
widget.icon,
|
||||
color: widget.iconColor,
|
||||
size: widget.widgetSize / 1.5,
|
||||
),
|
||||
notifications: snapshot.data ?? 0,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 0.0,
|
||||
top: 0.0,
|
||||
child: Container(
|
||||
width: widget.widgetSize / 2,
|
||||
height: widget.widgetSize / 2,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: widget.counterBackgroundColor,
|
||||
),
|
||||
child: Center(
|
||||
child: CustomSemantics(
|
||||
identifier: widget.semanticIdUnreadMessages,
|
||||
value: snapshot.data?.toString() ?? "0",
|
||||
child: Text(
|
||||
snapshot.data?.toString() ?? "0",
|
||||
style: widget.textStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -3,8 +3,8 @@ import "dart:typed_data";
|
|||
|
||||
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_accessibility/flutter_accessibility.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/chat_detail/widgets/chat_bottom.dart";
|
||||
import "package:flutter_chat/src/screens/chat_detail/widgets/chat_widgets.dart";
|
||||
|
@ -199,9 +199,12 @@ class _ChatAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
|
||||
Widget? appBarIcon;
|
||||
if (onPressBack != null) {
|
||||
appBarIcon = InkWell(
|
||||
onTap: onPressBack,
|
||||
child: const Icon(Icons.arrow_back_ios),
|
||||
appBarIcon = CustomSemantics(
|
||||
identifier: options.semantics.chatBackButton,
|
||||
child: InkWell(
|
||||
onTap: onPressBack,
|
||||
child: const Icon(Icons.arrow_back_ios),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -209,19 +212,23 @@ class _ChatAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
iconTheme: theme.appBarTheme.iconTheme,
|
||||
centerTitle: true,
|
||||
leading: appBarIcon,
|
||||
title: InkWell(
|
||||
splashColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
hoverColor: Colors.transparent,
|
||||
onTap: onPressChatTitle,
|
||||
child: CustomSemantics(
|
||||
identifier: options.semantics.chatChatTitle,
|
||||
value: chatTitle ?? "",
|
||||
child: options.builders.chatTitleBuilder?.call(chatTitle ?? "") ??
|
||||
Text(
|
||||
chatTitle ?? "",
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
title: CustomSemantics(
|
||||
identifier: options.semantics.chatTitleButton,
|
||||
buttonWithVariableText: true,
|
||||
child: InkWell(
|
||||
splashColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
hoverColor: Colors.transparent,
|
||||
onTap: onPressChatTitle,
|
||||
child: CustomSemantics(
|
||||
identifier: options.semantics.chatChatTitle,
|
||||
value: chatTitle ?? "",
|
||||
child: options.builders.chatTitleBuilder?.call(chatTitle ?? "") ??
|
||||
Text(
|
||||
chatTitle ?? "",
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -69,20 +69,26 @@ class ChatBottomInputSection extends HookWidget {
|
|||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
alignment: Alignment.bottomRight,
|
||||
onPressed: isLoading ? null : onPressSelectImage,
|
||||
icon: Icon(
|
||||
Icons.image_outlined,
|
||||
color: options.iconEnabledColor,
|
||||
CustomSemantics(
|
||||
identifier: options.semantics.chatSelectImageIconButton,
|
||||
child: IconButton(
|
||||
alignment: Alignment.bottomRight,
|
||||
onPressed: isLoading ? null : onPressSelectImage,
|
||||
icon: Icon(
|
||||
Icons.image_outlined,
|
||||
color: options.iconEnabledColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
alignment: Alignment.bottomRight,
|
||||
disabledColor: options.iconDisabledColor,
|
||||
color: options.iconEnabledColor,
|
||||
onPressed: isLoading ? null : onClickSendMessage,
|
||||
icon: const Icon(Icons.send_rounded),
|
||||
CustomSemantics(
|
||||
identifier: options.semantics.chatSendMessageIconButton,
|
||||
child: IconButton(
|
||||
alignment: Alignment.bottomRight,
|
||||
disabledColor: options.iconDisabledColor,
|
||||
color: options.iconEnabledColor,
|
||||
onPressed: isLoading ? null : onClickSendMessage,
|
||||
icon: const Icon(Icons.send_rounded),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -119,7 +125,8 @@ class ChatBottomInputSection extends HookWidget {
|
|||
top: 16,
|
||||
bottom: 16,
|
||||
),
|
||||
// this ensures that that there is space at the end of the textfield
|
||||
// this ensures that that there is space at the end of the
|
||||
// textfield
|
||||
suffixIcon: AbsorbPointer(
|
||||
child: Opacity(
|
||||
opacity: 0.0,
|
||||
|
|
|
@ -297,7 +297,7 @@ class _DefaultChatImage extends StatelessWidget {
|
|||
options.imageProviderResolver(context, Uri.parse(imageUrl)),
|
||||
fit: BoxFit.fitWidth,
|
||||
errorBuilder: (context, error, stackTrace) => Text(
|
||||
// TODO: Non-replaceable text
|
||||
// TODO(Jacques): Non-replaceable text
|
||||
"Something went wrong with loading the image",
|
||||
style: textTheme.bodyLarge?.copyWith(
|
||||
color: messageTheme.textColor,
|
||||
|
|
|
@ -130,55 +130,63 @@ class _Body extends StatelessWidget {
|
|||
var chatUserDisplay = Wrap(
|
||||
children: [
|
||||
if (chat != null) ...[
|
||||
...chat!.users.map(
|
||||
(tappedUser) => Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 8,
|
||||
right: 8,
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: () => onTapUser?.call(tappedUser),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FutureBuilder<UserModel>(
|
||||
future: service.getUser(userId: tappedUser).first,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.waiting) {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
...chat!.users.asMap().entries.map(
|
||||
(entry) {
|
||||
var index = entry.key;
|
||||
var tappedUser = entry.value;
|
||||
|
||||
var user = snapshot.data;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 8,
|
||||
right: 8,
|
||||
),
|
||||
child: CustomSemantics(
|
||||
identifier: options.semantics.profileTapUserButton(index),
|
||||
child: InkWell(
|
||||
onTap: () => onTapUser?.call(tappedUser),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FutureBuilder<UserModel>(
|
||||
future: service.getUser(userId: tappedUser).first,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.waiting) {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
var user = snapshot.data;
|
||||
|
||||
return options.builders.userAvatarBuilder?.call(
|
||||
context,
|
||||
user,
|
||||
44,
|
||||
) ??
|
||||
Avatar(
|
||||
boxfit: BoxFit.cover,
|
||||
user: User(
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
imageUrl:
|
||||
user.imageUrl != null || user.imageUrl != ""
|
||||
if (user == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return options.builders.userAvatarBuilder?.call(
|
||||
context,
|
||||
user,
|
||||
44,
|
||||
) ??
|
||||
Avatar(
|
||||
boxfit: BoxFit.cover,
|
||||
user: User(
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
imageUrl: user.imageUrl != null ||
|
||||
user.imageUrl != ""
|
||||
? user.imageUrl
|
||||
: null,
|
||||
),
|
||||
size: 60,
|
||||
);
|
||||
},
|
||||
),
|
||||
size: 60,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
|
@ -286,18 +294,21 @@ class _Body extends StatelessWidget {
|
|||
vertical: 24,
|
||||
horizontal: 80,
|
||||
),
|
||||
child: FilledButton(
|
||||
onPressed: () {
|
||||
onPressStartChat?.call(user!.id);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
options.translations.newChatButton,
|
||||
style: theme.textTheme.displayLarge,
|
||||
),
|
||||
],
|
||||
child: CustomSemantics(
|
||||
identifier: options.semantics.profileStartChatButton,
|
||||
child: FilledButton(
|
||||
onPressed: () {
|
||||
onPressStartChat?.call(user!.id);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
options.translations.newChatButton,
|
||||
style: theme.textTheme.displayLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -197,6 +197,8 @@ class _BodyState extends State<_Body> {
|
|||
semantics.chatsChatLastUsed(index),
|
||||
semanticIdUnreadMessages:
|
||||
semantics.chatsChatUnreadMessages(index),
|
||||
semanticIdButton:
|
||||
semantics.chatsOpenChatButton(index),
|
||||
);
|
||||
|
||||
return !chat.canBeDeleted
|
||||
|
@ -275,18 +277,21 @@ class _BodyState extends State<_Body> {
|
|||
vertical: 24,
|
||||
horizontal: 4,
|
||||
),
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
fixedSize: const Size(254, 44),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(56),
|
||||
child: CustomSemantics(
|
||||
identifier: options.semantics.chatsStartChatButton,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
fixedSize: const Size(254, 44),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(56),
|
||||
),
|
||||
),
|
||||
onPressed: widget.onPressStartChat,
|
||||
child: Text(
|
||||
translations.newChatButton,
|
||||
style: theme.textTheme.displayLarge,
|
||||
),
|
||||
),
|
||||
onPressed: widget.onPressStartChat,
|
||||
child: Text(
|
||||
translations.newChatButton,
|
||||
style: theme.textTheme.displayLarge,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -303,6 +308,7 @@ class _ChatItem extends StatelessWidget {
|
|||
required this.semanticIdSubTitle,
|
||||
required this.semanticIdLastUsed,
|
||||
required this.semanticIdUnreadMessages,
|
||||
required this.semanticIdButton,
|
||||
});
|
||||
|
||||
final ChatModel chat;
|
||||
|
@ -311,6 +317,7 @@ class _ChatItem extends StatelessWidget {
|
|||
final String semanticIdSubTitle;
|
||||
final String semanticIdLastUsed;
|
||||
final String semanticIdUnreadMessages;
|
||||
final String semanticIdButton;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -330,29 +337,33 @@ class _ChatItem extends StatelessWidget {
|
|||
semanticIdUnreadMessages: semanticIdUnreadMessages,
|
||||
);
|
||||
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
onPressChat(chat);
|
||||
},
|
||||
child: options.builders.chatRowContainerBuilder?.call(
|
||||
context,
|
||||
chatListItem,
|
||||
) ??
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: theme.dividerColor,
|
||||
width: 0.5,
|
||||
return CustomSemantics(
|
||||
identifier: semanticIdButton,
|
||||
buttonWithVariableText: true,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
onPressChat(chat);
|
||||
},
|
||||
child: options.builders.chatRowContainerBuilder?.call(
|
||||
context,
|
||||
chatListItem,
|
||||
) ??
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: theme.dividerColor,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: chatListItem,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: chatListItem,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -530,6 +541,10 @@ Future<bool?> _deleteDialog(
|
|||
) async {
|
||||
var theme = Theme.of(context);
|
||||
|
||||
var scope = ChatScope.of(context);
|
||||
|
||||
var options = scope.options;
|
||||
|
||||
return showModalBottomSheet<bool>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => Container(
|
||||
|
@ -555,20 +570,23 @@ Future<bool?> _deleteDialog(
|
|||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 60),
|
||||
child: FilledButton(
|
||||
onPressed: () {
|
||||
Navigator.of(
|
||||
context,
|
||||
).pop(true);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
translations.deleteChatModalConfirm,
|
||||
style: theme.textTheme.displayLarge,
|
||||
),
|
||||
],
|
||||
child: CustomSemantics(
|
||||
identifier: options.semantics.chatsDeleteConfirmButton,
|
||||
child: FilledButton(
|
||||
onPressed: () {
|
||||
Navigator.of(
|
||||
context,
|
||||
).pop(true);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
translations.deleteChatModalConfirm,
|
||||
style: theme.textTheme.displayLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -145,6 +145,7 @@ class _AppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
SearchIcon(
|
||||
isSearching: isSearching,
|
||||
onPressed: onPressedSearchIcon,
|
||||
semanticId: options.semantics.newChatSearchIconButton,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -187,23 +188,26 @@ class _Body extends StatelessWidget {
|
|||
right: 32,
|
||||
top: 20,
|
||||
),
|
||||
child: FilledButton(
|
||||
onPressed: onPressCreateGroupChat,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.groups_2,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
Text(
|
||||
translations.newGroupChatButton,
|
||||
style: theme.textTheme.displayLarge,
|
||||
),
|
||||
],
|
||||
child: CustomSemantics(
|
||||
identifier: options.semantics.newChatCreateGroupChatButton,
|
||||
child: FilledButton(
|
||||
onPressed: onPressCreateGroupChat,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.groups_2,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
Text(
|
||||
translations.newGroupChatButton,
|
||||
style: theme.textTheme.displayLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -158,31 +158,35 @@ class _BodyState extends State<_Body> {
|
|||
Center(
|
||||
child: Stack(
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () async => onPressSelectImage(
|
||||
context,
|
||||
options,
|
||||
(image) {
|
||||
setState(() {
|
||||
this.image = image;
|
||||
});
|
||||
},
|
||||
),
|
||||
child: Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFD9D9D9),
|
||||
borderRadius: BorderRadius.circular(40),
|
||||
image: image != null
|
||||
? DecorationImage(
|
||||
image: MemoryImage(image!),
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
CustomSemantics(
|
||||
identifier: options.semantics.newGroupChatSelectImage,
|
||||
child: InkWell(
|
||||
onTap: () async => onPressSelectImage(
|
||||
context,
|
||||
options,
|
||||
(image) {
|
||||
setState(() {
|
||||
this.image = image;
|
||||
});
|
||||
},
|
||||
),
|
||||
child: Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFD9D9D9),
|
||||
borderRadius: BorderRadius.circular(40),
|
||||
image: image != null
|
||||
? DecorationImage(
|
||||
image: MemoryImage(image!),
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: image == null
|
||||
? const Icon(Icons.image)
|
||||
: null,
|
||||
),
|
||||
child:
|
||||
image == null ? const Icon(Icons.image) : null,
|
||||
),
|
||||
),
|
||||
if (image != null)
|
||||
|
@ -197,15 +201,19 @@ class _BodyState extends State<_Body> {
|
|||
borderRadius: BorderRadius.circular(40),
|
||||
),
|
||||
child: Center(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
image = null;
|
||||
});
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.close,
|
||||
size: 12,
|
||||
child: CustomSemantics(
|
||||
identifier:
|
||||
options.semantics.newGroupChatRemoveImage,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
image = null;
|
||||
});
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.close,
|
||||
size: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -354,31 +362,34 @@ class _BodyState extends State<_Body> {
|
|||
),
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: isButtonEnabled,
|
||||
builder: (context, isEnabled, child) => FilledButton(
|
||||
onPressed: users.isNotEmpty
|
||||
? () async {
|
||||
if (!isPressed) {
|
||||
isPressed = true;
|
||||
if (formKey.currentState!.validate()) {
|
||||
await widget.onComplete(
|
||||
users,
|
||||
_chatNameController.text,
|
||||
_bioController.text,
|
||||
image,
|
||||
);
|
||||
builder: (context, isEnabled, child) => CustomSemantics(
|
||||
identifier: "",
|
||||
child: FilledButton(
|
||||
onPressed: users.isNotEmpty
|
||||
? () async {
|
||||
if (!isPressed) {
|
||||
isPressed = true;
|
||||
if (formKey.currentState!.validate()) {
|
||||
await widget.onComplete(
|
||||
users,
|
||||
_chatNameController.text,
|
||||
_bioController.text,
|
||||
image,
|
||||
);
|
||||
}
|
||||
isPressed = false;
|
||||
}
|
||||
isPressed = false;
|
||||
}
|
||||
}
|
||||
: null,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
translations.createGroupChatButton,
|
||||
style: theme.textTheme.displayLarge,
|
||||
),
|
||||
],
|
||||
: null,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
translations.createGroupChatButton,
|
||||
style: theme.textTheme.displayLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -402,38 +413,41 @@ class _SelectedUser extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
var chatScope = ChatScope.of(context);
|
||||
var options = chatScope.options;
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
onRemove(user);
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: options.builders.userAvatarBuilder?.call(
|
||||
context,
|
||||
user,
|
||||
40,
|
||||
) ??
|
||||
Avatar(
|
||||
boxfit: BoxFit.cover,
|
||||
user: User(
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
imageUrl: user.imageUrl != "" ? user.imageUrl : null,
|
||||
return CustomSemantics(
|
||||
identifier: options.semantics.newGroupChatRemoveUser,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
onRemove(user);
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: options.builders.userAvatarBuilder?.call(
|
||||
context,
|
||||
user,
|
||||
40,
|
||||
) ??
|
||||
Avatar(
|
||||
boxfit: BoxFit.cover,
|
||||
user: User(
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
imageUrl: user.imageUrl != "" ? user.imageUrl : null,
|
||||
),
|
||||
size: 40,
|
||||
),
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
Positioned.directional(
|
||||
textDirection: Directionality.of(context),
|
||||
end: 0,
|
||||
child: const Icon(
|
||||
Icons.cancel,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
Positioned.directional(
|
||||
textDirection: Directionality.of(context),
|
||||
end: 0,
|
||||
child: const Icon(
|
||||
Icons.cancel,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import "package:chat_repository_interface/chat_repository_interface.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_accessibility/flutter_accessibility.dart";
|
||||
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";
|
||||
|
@ -155,6 +156,7 @@ class _AppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
SearchIcon(
|
||||
isSearching: isSearching,
|
||||
onPressed: onPressedSearchIcon,
|
||||
semanticId: options.semantics.newGroupChatSearchIconButton,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -272,18 +274,21 @@ class _NextButton extends StatelessWidget {
|
|||
),
|
||||
child: Visibility(
|
||||
visible: selectedUsers.isNotEmpty,
|
||||
child: FilledButton(
|
||||
onPressed: () async {
|
||||
await onPressGroupChatOverview(selectedUsers);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
options.translations.next,
|
||||
style: theme.textTheme.displayLarge,
|
||||
),
|
||||
],
|
||||
child: CustomSemantics(
|
||||
identifier: options.semantics.newGroupChatNextButton,
|
||||
child: FilledButton(
|
||||
onPressed: () async {
|
||||
await onPressGroupChatOverview(selectedUsers);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
options.translations.next,
|
||||
style: theme.textTheme.displayLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import "dart:typed_data";
|
||||
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_accessibility/flutter_accessibility.dart";
|
||||
import "package:flutter_chat/src/config/chat_options.dart";
|
||||
import "package:flutter_chat/src/config/chat_translations.dart";
|
||||
import "package:flutter_chat/src/util/scope.dart";
|
||||
|
@ -71,13 +72,16 @@ class DefaultImagePickerDialog extends StatelessWidget {
|
|||
Icons.insert_drive_file_rounded,
|
||||
size: 60,
|
||||
),
|
||||
closeButtonBuilder: (ontap) => TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(
|
||||
translations.cancelImagePickerBtn,
|
||||
style: textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 18,
|
||||
decoration: TextDecoration.underline,
|
||||
closeButtonBuilder: (ontap) => CustomSemantics(
|
||||
identifier: options.semantics.imagePickerCancelButton,
|
||||
child: TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(
|
||||
translations.cancelImagePickerBtn,
|
||||
style: textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 18,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -6,6 +6,7 @@ class SearchIcon extends StatelessWidget {
|
|||
const SearchIcon({
|
||||
required this.isSearching,
|
||||
required this.onPressed,
|
||||
required this.semanticId,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
@ -15,14 +16,20 @@ class SearchIcon extends StatelessWidget {
|
|||
/// Callback function triggered when the search icon is pressed
|
||||
final VoidCallback onPressed;
|
||||
|
||||
/// Semantic id for icon button
|
||||
final String semanticId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var theme = Theme.of(context);
|
||||
return IconButton(
|
||||
onPressed: onPressed,
|
||||
icon: Icon(
|
||||
isSearching ? Icons.close : Icons.search,
|
||||
color: theme.appBarTheme.iconTheme?.color ?? Colors.white,
|
||||
return Semantics(
|
||||
identifier: semanticId,
|
||||
child: IconButton(
|
||||
onPressed: onPressed,
|
||||
icon: Icon(
|
||||
isSearching ? Icons.close : Icons.search,
|
||||
color: theme.appBarTheme.iconTheme?.color ?? Colors.white,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -80,70 +80,20 @@ class _UserListState extends State<UserList> {
|
|||
itemBuilder: (context, index) {
|
||||
var user = filteredUsers[index];
|
||||
var isSelected = widget.selectedUsers.any((u) => u.id == user.id);
|
||||
return InkWell(
|
||||
onTap: () async {
|
||||
if (widget.creatingGroup) {
|
||||
return handleGroupChatTap(user);
|
||||
} else {
|
||||
return handlePersonalChatTap(user);
|
||||
}
|
||||
},
|
||||
child: options.builders.chatRowContainerBuilder?.call(
|
||||
context,
|
||||
Row(
|
||||
children: [
|
||||
options.builders.userAvatarBuilder
|
||||
?.call(context, user, 44) ??
|
||||
Avatar(
|
||||
boxfit: BoxFit.cover,
|
||||
user: User(
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
imageUrl:
|
||||
user.imageUrl != "" ? user.imageUrl : null,
|
||||
),
|
||||
size: 44,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
CustomSemantics(
|
||||
identifier: options.semantics
|
||||
.newChatUserListUserFullName(index),
|
||||
value: user.fullname ?? translations.anonymousUser,
|
||||
child: Text(
|
||||
user.fullname ?? translations.anonymousUser,
|
||||
style: theme.textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
if (widget.creatingGroup) ...[
|
||||
const Spacer(),
|
||||
Checkbox(
|
||||
value: isSelected,
|
||||
onChanged: (value) {
|
||||
handleGroupChatTap(user);
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
) ??
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: theme.dividerColor,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
return CustomSemantics(
|
||||
identifier: options.semantics.userListTapUser(index),
|
||||
buttonWithVariableText: true,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
if (widget.creatingGroup) {
|
||||
return handleGroupChatTap(user);
|
||||
} else {
|
||||
return handlePersonalChatTap(user);
|
||||
}
|
||||
},
|
||||
child: options.builders.chatRowContainerBuilder?.call(
|
||||
context,
|
||||
Row(
|
||||
children: [
|
||||
options.builders.userAvatarBuilder
|
||||
?.call(context, user, 44) ??
|
||||
|
@ -183,8 +133,63 @@ class _UserListState extends State<UserList> {
|
|||
],
|
||||
],
|
||||
),
|
||||
) ??
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: theme.dividerColor,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
options.builders.userAvatarBuilder
|
||||
?.call(context, user, 44) ??
|
||||
Avatar(
|
||||
boxfit: BoxFit.cover,
|
||||
user: User(
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
imageUrl: user.imageUrl != ""
|
||||
? user.imageUrl
|
||||
: null,
|
||||
),
|
||||
size: 44,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
CustomSemantics(
|
||||
identifier: options.semantics
|
||||
.newChatUserListUserFullName(index),
|
||||
value: user.fullname ?? translations.anonymousUser,
|
||||
child: Text(
|
||||
user.fullname ?? translations.anonymousUser,
|
||||
style: theme.textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
if (widget.creatingGroup) ...[
|
||||
const Spacer(),
|
||||
Checkbox(
|
||||
value: isSelected,
|
||||
onChanged: (value) {
|
||||
handleGroupChatTap(user);
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
@ -15,6 +15,9 @@ dependencies:
|
|||
intl: any
|
||||
flutter_hooks: ^0.20.5
|
||||
|
||||
flutter_accessibility:
|
||||
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
||||
version: ^0.0.2
|
||||
flutter_image_picker:
|
||||
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
||||
version: ^4.0.0
|
||||
|
|
Loading…
Reference in a new issue