diff --git a/assets/svg/ic_pencil.svg b/assets/svg/ic_pencil.svg
new file mode 100644
index 00000000..d66ed484
--- /dev/null
+++ b/assets/svg/ic_pencil.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/svg/ic_trust_deactivated.svg b/assets/svg/ic_trust_deactivated.svg
new file mode 100644
index 00000000..b3cef1bc
--- /dev/null
+++ b/assets/svg/ic_trust_deactivated.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/desktop_screens_new/groups_screen/widgets/desktop_cover_image_picker.dart b/lib/desktop_screens_new/groups_screen/widgets/desktop_cover_image_picker.dart
index 54194a67..3e275f7c 100644
--- a/lib/desktop_screens_new/groups_screen/widgets/desktop_cover_image_picker.dart
+++ b/lib/desktop_screens_new/groups_screen/widgets/desktop_cover_image_picker.dart
@@ -87,7 +87,7 @@ class DesktopCoverImagePicker extends StatelessWidget {
),
)
: Container(
- padding: const EdgeInsets.fromLTRB(108, 12, 108, 16),
+ padding: const EdgeInsets.only(top: 12, bottom: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: ColorConstants.pickerBackgroundColor,
@@ -104,7 +104,6 @@ class DesktopCoverImagePicker extends StatelessWidget {
AppVectors.icDesktopImage,
width: 48,
height: 32,
- fit: BoxFit.fitWidth,
),
],
),
diff --git a/lib/screens/group_contacts_screen/widgets/group_contact_list_tile.dart b/lib/screens/common_widgets/contact_list_tile.dart
similarity index 100%
rename from lib/screens/group_contacts_screen/widgets/group_contact_list_tile.dart
rename to lib/screens/common_widgets/contact_list_tile.dart
diff --git a/lib/screens/common_widgets/cover_image_picker.dart b/lib/screens/common_widgets/cover_image_picker.dart
new file mode 100644
index 00000000..39ddc5a4
--- /dev/null
+++ b/lib/screens/common_widgets/cover_image_picker.dart
@@ -0,0 +1,114 @@
+import 'dart:typed_data';
+
+import 'package:atsign_atmosphere_pro/utils/colors.dart';
+import 'package:atsign_atmosphere_pro/utils/images.dart';
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:atsign_atmosphere_pro/utils/vectors.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+class CoverImagePicker extends StatelessWidget {
+ final Function() onTap;
+ final Uint8List? groupImage;
+ final double height;
+ final EdgeInsetsGeometry margin;
+ final bool showOptions;
+ final Function()? onCancel;
+
+ const CoverImagePicker({
+ required this.onTap,
+ required this.groupImage,
+ required this.height,
+ this.margin = const EdgeInsets.symmetric(horizontal: 28),
+ this.showOptions = false,
+ this.onCancel,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return InkWell(
+ onTap: onTap,
+ child: Container(
+ height: height,
+ width: double.infinity,
+ margin: margin,
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10),
+ color: ColorConstants.dividerContextMenuColor,
+ ),
+ child: Stack(
+ fit: StackFit.expand,
+ children: [
+ groupImage != null
+ ? ClipRRect(
+ borderRadius: BorderRadius.circular(10),
+ child: Image.memory(
+ groupImage!,
+ fit: BoxFit.cover,
+ ),
+ )
+ : Column(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ "Insert Cover Image",
+ style: CustomTextStyles.greyW50014,
+ ),
+ SizedBox(height: 8),
+ Image.asset(
+ ImageConstants.icImage,
+ width: 48,
+ height: 32,
+ ),
+ ],
+ ),
+ if (showOptions)
+ Positioned(
+ top: 12,
+ right: 12,
+ left: 12,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ InkWell(
+ onTap: onCancel,
+ child: Container(
+ padding: const EdgeInsets.all(8),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10),
+ color: Colors.white54.withOpacity(0.5),
+ ),
+ child: SvgPicture.asset(
+ AppVectors.icCancel,
+ width: 16,
+ height: 16,
+ color: Colors.black,
+ fit: BoxFit.cover,
+ ),
+ ),
+ ),
+ if (groupImage != null)
+ Container(
+ padding: const EdgeInsets.all(8),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10),
+ color: Colors.white54.withOpacity(0.5),
+ ),
+ child: SvgPicture.asset(
+ AppVectors.icEdit,
+ width: 16,
+ height: 16,
+ fit: BoxFit.cover,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/screens/contact_new_version/contact_detail_screen.dart b/lib/screens/contact_new_version/contact_detail_screen.dart
index 7ea37aba..2abf445b 100644
--- a/lib/screens/contact_new_version/contact_detail_screen.dart
+++ b/lib/screens/contact_new_version/contact_detail_screen.dart
@@ -156,46 +156,39 @@ class _ContactDetailScreenState extends State {
SizedBox(height: 4),
Flexible(
child: isEditNickname
- ? Row(
- children: [
- Flexible(
- child: TextField(
- maxLines: 1,
- decoration: InputDecoration(
- contentPadding: EdgeInsets.only(
- left: 16,
- ),
- hintText: 'Enter Nickname',
- hintStyle: TextStyle(
- fontSize: 14.toFont,
- fontWeight: FontWeight.w500,
- color: ColorConstants.textBlack,
- ),
- border: OutlineInputBorder(
- borderRadius:
- BorderRadius.circular(5),
- borderSide: BorderSide.none,
- ),
- labelStyle: TextStyle(
- fontSize: 14.toFont,
- ),
- fillColor: Colors.white,
- filled: true,
- suffixIcon: InkWell(
- onTap: () {
- nicknameController.clear();
- },
- child: Icon(
- Icons.clear,
- color: Colors.black,
- size: 16,
- ),
- ),
- ),
- controller: nicknameController,
+ ? TextField(
+ maxLines: 1,
+ decoration: InputDecoration(
+ contentPadding: EdgeInsets.only(
+ left: 16,
+ ),
+ hintText: 'Enter Nickname',
+ hintStyle: TextStyle(
+ fontSize: 14.toFont,
+ fontWeight: FontWeight.w500,
+ color: ColorConstants.textBlack,
+ ),
+ border: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(5),
+ borderSide: BorderSide.none,
+ ),
+ labelStyle: TextStyle(
+ fontSize: 14.toFont,
+ ),
+ fillColor: Colors.white,
+ filled: true,
+ suffixIcon: InkWell(
+ onTap: () {
+ nicknameController.clear();
+ },
+ child: Icon(
+ Icons.clear,
+ color: Colors.black,
+ size: 16,
),
),
- ],
+ ),
+ controller: nicknameController,
)
: Text(
widget.contact.tags?['nickname'] ??
diff --git a/lib/screens/contact_new_version/contact_screen.dart b/lib/screens/contact_new_version/contact_screen.dart
index 22fc314b..8c9a33c1 100644
--- a/lib/screens/contact_new_version/contact_screen.dart
+++ b/lib/screens/contact_new_version/contact_screen.dart
@@ -1,5 +1,4 @@
import 'package:at_common_flutter/services/size_config.dart';
-import 'package:at_contacts_group_flutter/screens/group_view/group_view.dart';
import 'package:at_contacts_group_flutter/services/group_service.dart';
import 'package:atsign_atmosphere_pro/data_models/enums/contact_type.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/app_bar_custom.dart';
@@ -10,6 +9,7 @@ import 'package:atsign_atmosphere_pro/screens/contact_new_version/contact_detail
import 'package:atsign_atmosphere_pro/screens/contact_new_version/create_group_screen.dart';
import 'package:atsign_atmosphere_pro/screens/contact_new_version/widget/contact_skeleton_loading_widget.dart';
import 'package:atsign_atmosphere_pro/screens/contact_new_version/widget/list_contact_widget.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/group_contacts_screen.dart';
import 'package:atsign_atmosphere_pro/utils/colors.dart';
import 'package:atsign_atmosphere_pro/view_models/contact_provider.dart';
import 'package:atsign_atmosphere_pro/view_models/create_group_provider.dart';
@@ -274,7 +274,7 @@ class _ContactScreenState extends State
await Navigator.push(
context,
MaterialPageRoute(
- builder: (context) => GroupView(
+ builder: (context) => GroupContactsScreen(
group: group,
),
),
diff --git a/lib/screens/contact_new_version/create_group_screen.dart b/lib/screens/contact_new_version/create_group_screen.dart
index f289a51f..69a8bffc 100644
--- a/lib/screens/contact_new_version/create_group_screen.dart
+++ b/lib/screens/contact_new_version/create_group_screen.dart
@@ -2,17 +2,16 @@ import 'package:at_common_flutter/at_common_flutter.dart';
import 'package:at_commons/at_commons.dart';
import 'package:at_contact/at_contact.dart';
import 'package:at_contacts_group_flutter/at_contacts_group_flutter.dart';
+import 'package:atsign_atmosphere_pro/screens/common_widgets/cover_image_picker.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/custom_toast.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/input_widget.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/search_widget.dart';
import 'package:atsign_atmosphere_pro/screens/contact_new_version/widget/list_contact_widget.dart';
import 'package:atsign_atmosphere_pro/utils/colors.dart';
-import 'package:atsign_atmosphere_pro/utils/images.dart';
import 'package:atsign_atmosphere_pro/utils/text_strings.dart';
import 'package:atsign_atmosphere_pro/utils/vectors.dart';
import 'package:atsign_atmosphere_pro/view_models/create_group_provider.dart';
import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
@@ -115,7 +114,14 @@ class _CreateGroupScreenState extends State {
},
),
),
- _buildImage(value.selectedImageByteData),
+ SizedBox(height: 16),
+ CoverImagePicker(
+ onTap: () async {
+ await _provider.selectCoverImage();
+ },
+ groupImage: value.selectedImageByteData,
+ height: 88,
+ ),
Padding(
padding: const EdgeInsets.only(
top: 22,
@@ -254,48 +260,4 @@ class _CreateGroupScreenState extends State {
);
});
}
-
- Widget _buildImage(Uint8List? selectedImage) {
- return InkWell(
- onTap: () async {
- await _provider.selectCoverImage();
- },
- child: Container(
- height: 89,
- width: double.infinity,
- margin: const EdgeInsets.fromLTRB(27, 14, 27, 0),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(10),
- color: Color(0xFFECECEC),
- ),
- child: selectedImage != null
- ? ClipRRect(
- borderRadius: BorderRadius.circular(10),
- child: Image.memory(
- selectedImage,
- fit: BoxFit.cover,
- ),
- )
- : Center(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Text(
- "Insert Cover Image",
- style: TextStyle(
- fontSize: 14,
- fontWeight: FontWeight.w500,
- color: ColorConstants.grey,
- ),
- ),
- SizedBox(height: 8),
- Image.asset(
- ImageConstants.icImage,
- ),
- ],
- ),
- ),
- ),
- );
- }
}
diff --git a/lib/screens/group_contacts/group_contacts_screen.dart b/lib/screens/group_contacts/group_contacts_screen.dart
new file mode 100644
index 00000000..599023b0
--- /dev/null
+++ b/lib/screens/group_contacts/group_contacts_screen.dart
@@ -0,0 +1,308 @@
+import 'package:at_contact/at_contact.dart';
+import 'package:atsign_atmosphere_pro/screens/common_widgets/cover_image_picker.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_app_bar.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_custom_button.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_edit_options_widget.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_manage_members_widget.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_member_list_view.dart';
+import 'package:atsign_atmosphere_pro/utils/colors.dart';
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:atsign_atmosphere_pro/utils/vectors.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+class GroupContactsScreen extends StatefulWidget {
+ final AtGroup group;
+
+ const GroupContactsScreen({required this.group});
+
+ @override
+ State createState() => _GroupContactsScreenState();
+}
+
+class _GroupContactsScreenState extends State {
+ bool showEditOptions = false;
+ bool isEditingName = false;
+ bool isEditingImage = false;
+ late TextEditingController groupNameController =
+ TextEditingController(text: widget.group.groupName);
+
+ @override
+ Widget build(BuildContext context) {
+ return PopScope(
+ canPop: !isEditingName,
+ onPopInvoked: (didPop) {
+ if (isEditingName) {
+ groupNameController.text = widget.group.groupName ?? '';
+ setState(() {
+ isEditingName = false;
+ });
+ }
+ },
+ child: Scaffold(
+ backgroundColor: ColorConstants.background,
+ appBar: GroupsAppBar(
+ title: isEditingName ? 'Edit' : widget.group.groupName ?? '',
+ onBack: () {
+ if (isEditingName) {
+ groupNameController.text = widget.group.groupName ?? '';
+ setState(() {
+ isEditingName = false;
+ });
+ } else {
+ Navigator.pop(context);
+ }
+ },
+ actions: [
+ isEditingName || isEditingImage
+ ? SvgPicture.asset(
+ AppVectors.icOptions,
+ width: 16,
+ fit: BoxFit.cover,
+ color: Colors.black,
+ )
+ : InkWell(
+ onTap: () {
+ setState(() {
+ showEditOptions = !showEditOptions;
+ });
+ },
+ child: SvgPicture.asset(
+ AppVectors.icPencil,
+ width: 24,
+ height: 24,
+ fit: BoxFit.cover,
+ color: showEditOptions
+ ? ColorConstants.orangeColor
+ : Colors.black,
+ ),
+ ),
+ ],
+ ),
+ body: SafeArea(
+ child: Stack(
+ children: [
+ Column(
+ children: [
+ SizedBox(height: 20),
+ isEditingName
+ ? buildEditGroupNameWidget()
+ : CoverImagePicker(
+ showOptions: isEditingImage,
+ onTap: () {},
+ groupImage: widget.group.groupPicture,
+ height: 120,
+ ),
+ SizedBox(height: isEditingName || isEditingImage ? 24 : 12),
+ isEditingName || isEditingImage
+ ? GroupsCustomButton(
+ title: 'Save',
+ onTap: () {},
+ borderRadius: 5,
+ )
+ : GroupsCustomButton(
+ title: 'Transfer File',
+ suffix: SvgPicture.asset(
+ AppVectors.icTransfer,
+ width: 24,
+ height: 16,
+ fit: BoxFit.cover,
+ ),
+ onTap: () {},
+ borderRadius: 7,
+ ),
+ Expanded(
+ child: GroupsMemberListView(
+ members: widget.group.members!,
+ ),
+ ),
+ ],
+ ),
+ if (showEditOptions) ...[
+ Positioned(
+ top: 0,
+ bottom: 0,
+ right: 0,
+ left: 0,
+ child: InkWell(
+ onTap: () {
+ setState(() {
+ showEditOptions = false;
+ });
+ },
+ ),
+ ),
+ Container(
+ padding: EdgeInsets.only(top: 20, left: 20, right: 20),
+ child: GroupsEditOptionsWidget(
+ onEditName: () {
+ setState(() {
+ showEditOptions = false;
+ isEditingName = true;
+ });
+ },
+ onCoverImage: () {
+ setState(() {
+ showEditOptions = false;
+ isEditingImage = true;
+ });
+ },
+ onManageMembers: () {
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => GroupsManageMembersWidget(),
+ ),
+ );
+ },
+ onDelete: () async {
+ await showDeleteConfirmDialog();
+ },
+ ),
+ ),
+ ]
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget buildEditGroupNameWidget() {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 28),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Text(
+ 'Group Name',
+ style: CustomTextStyles.blackW5008,
+ ),
+ SizedBox(height: 4),
+ TextField(
+ maxLines: 1,
+ style: CustomTextStyles.blackW50014,
+ decoration: InputDecoration(
+ contentPadding: EdgeInsets.only(
+ left: 28,
+ top: 16,
+ bottom: 16,
+ ),
+ border: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(5),
+ borderSide: BorderSide.none,
+ ),
+ fillColor: Colors.white,
+ filled: true,
+ isDense: true,
+ suffix: InkWell(
+ onTap: () {
+ groupNameController.clear();
+ },
+ child: Container(
+ padding: EdgeInsets.symmetric(horizontal: 12),
+ child: SvgPicture.asset(
+ AppVectors.icCancel,
+ color: Colors.black,
+ height: 8,
+ width: 8,
+ fit: BoxFit.cover,
+ ),
+ ),
+ ),
+ ),
+ controller: groupNameController,
+ )
+ ],
+ ),
+ );
+ }
+
+ Future showDeleteConfirmDialog() async {
+ await showDialog(
+ context: context,
+ barrierDismissible: false,
+ builder: (context) {
+ return AlertDialog(
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(7),
+ ),
+ contentPadding:
+ const EdgeInsets.symmetric(horizontal: 28, vertical: 24),
+ content: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ 'Delete',
+ style: CustomTextStyles.blackBold(size: 14),
+ ),
+ SizedBox(height: 8),
+ Text(
+ 'Are you sure you want to delete ${widget.group.groupName}?',
+ style: CustomTextStyles.blackW40013,
+ textAlign: TextAlign.center,
+ ),
+ RichText(
+ text: TextSpan(
+ children: [
+ TextSpan(
+ text: 'Note',
+ style: CustomTextStyles.blackItalicW40013,
+ ),
+ TextSpan(
+ text: ': this action cannot be undone.',
+ style: CustomTextStyles.blackW40013,
+ )
+ ],
+ ),
+ ),
+ SizedBox(height: 8),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 12),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ InkWell(
+ onTap: () {
+ Navigator.pop(context);
+ },
+ child: Container(
+ height: 36,
+ width: 84,
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(4),
+ border: Border.all(color: Colors.black),
+ ),
+ child: Text(
+ 'Cancel',
+ style: CustomTextStyles.blackUnderlineW40012,
+ ),
+ ),
+ ),
+ InkWell(
+ child: Container(
+ height: 36,
+ width: 84,
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(4),
+ color: ColorConstants.bottomBlack,
+ ),
+ child: Text(
+ 'Move',
+ style: CustomTextStyles.whiteBold12,
+ ),
+ ),
+ )
+ ],
+ ),
+ )
+ ],
+ ),
+ );
+ },
+ );
+ }
+}
diff --git a/lib/screens/group_contacts/widgets/groups_app_bar.dart b/lib/screens/group_contacts/widgets/groups_app_bar.dart
new file mode 100644
index 00000000..f038c50a
--- /dev/null
+++ b/lib/screens/group_contacts/widgets/groups_app_bar.dart
@@ -0,0 +1,57 @@
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:atsign_atmosphere_pro/utils/vectors.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+class GroupsAppBar extends StatelessWidget implements PreferredSizeWidget {
+ final String title;
+ final List actions;
+ final Function()? onBack;
+
+ const GroupsAppBar({
+ required this.title,
+ this.actions = const [],
+ this.onBack,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return SafeArea(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 24),
+ child: Row(
+ children: [
+ SizedBox(width: 8),
+ InkWell(
+ onTap: onBack ??
+ () {
+ Navigator.pop(context);
+ },
+ child: SizedBox(
+ width: 24,
+ height: 24,
+ child: SvgPicture.asset(
+ AppVectors.icBack,
+ height: 20,
+ width: 8,
+ fit: BoxFit.cover,
+ alignment: Alignment.center,
+ ),
+ ),
+ ),
+ SizedBox(width: 32),
+ Text(
+ title,
+ style: CustomTextStyles.desktopPrimaryW50018,
+ ),
+ Spacer(),
+ ...actions
+ ],
+ ),
+ ),
+ );
+ }
+
+ @override
+ Size get preferredSize => Size.fromHeight(28);
+}
diff --git a/lib/screens/group_contacts/widgets/groups_custom_button.dart b/lib/screens/group_contacts/widgets/groups_custom_button.dart
new file mode 100644
index 00000000..a6cb4b8c
--- /dev/null
+++ b/lib/screens/group_contacts/widgets/groups_custom_button.dart
@@ -0,0 +1,60 @@
+import 'package:atsign_atmosphere_pro/utils/colors.dart';
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:flutter/material.dart';
+
+class GroupsCustomButton extends StatelessWidget {
+ final Function() onTap;
+ final bool isLoading;
+ final String title;
+ final double spaceSize;
+ final Widget? suffix;
+ final double borderRadius;
+
+ const GroupsCustomButton({
+ required this.onTap,
+ this.isLoading = false,
+ required this.title,
+ this.spaceSize = 28,
+ this.suffix,
+ required this.borderRadius,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ height: 52,
+ width: double.infinity,
+ decoration: BoxDecoration(
+ color: ColorConstants.raisinBlack,
+ borderRadius: BorderRadius.circular(borderRadius),
+ ),
+ margin: EdgeInsets.symmetric(horizontal: 28),
+ child: InkWell(
+ onTap: onTap,
+ child: Center(
+ child: isLoading
+ ? SizedBox(
+ width: 20,
+ height: 20,
+ child: CircularProgressIndicator(
+ color: Colors.white,
+ ),
+ )
+ : Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ title,
+ style: CustomTextStyles.whiteBold(size: 14),
+ ),
+ if (suffix != null) ...[
+ SizedBox(width: spaceSize),
+ suffix!,
+ ]
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/screens/group_contacts/widgets/groups_edit_options_widget.dart b/lib/screens/group_contacts/widgets/groups_edit_options_widget.dart
new file mode 100644
index 00000000..43f0fbe5
--- /dev/null
+++ b/lib/screens/group_contacts/widgets/groups_edit_options_widget.dart
@@ -0,0 +1,102 @@
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:flutter/material.dart';
+
+class GroupsEditOptionsWidget extends StatelessWidget {
+ final Function() onEditName;
+ final Function() onCoverImage;
+ final Function() onManageMembers;
+ final Function() onDelete;
+
+ const GroupsEditOptionsWidget({
+ required this.onEditName,
+ required this.onCoverImage,
+ required this.onManageMembers,
+ required this.onDelete,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ clipBehavior: Clip.antiAlias,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(7),
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withOpacity(0.25),
+ blurRadius: 18,
+ offset: Offset(0, 4),
+ )
+ ],
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ InkWell(
+ onTap: onEditName,
+ child: SizedBox(
+ height: 56,
+ child: Center(
+ child: Text(
+ 'Edit Name',
+ style: CustomTextStyles.blackW40017,
+ ),
+ ),
+ ),
+ ),
+ Divider(
+ height: 0,
+ thickness: 1,
+ color: Colors.black,
+ ),
+ InkWell(
+ onTap: onCoverImage,
+ child: SizedBox(
+ height: 56,
+ child: Center(
+ child: Text(
+ 'Edit Cover Image',
+ style: CustomTextStyles.blackW40017,
+ ),
+ ),
+ ),
+ ),
+ Divider(
+ height: 0,
+ thickness: 1,
+ color: Colors.black,
+ ),
+ InkWell(
+ onTap: onManageMembers,
+ child: SizedBox(
+ height: 56,
+ child: Center(
+ child: Text(
+ 'Manage Members',
+ style: CustomTextStyles.blackW40017,
+ ),
+ ),
+ ),
+ ),
+ Divider(
+ height: 0,
+ thickness: 1,
+ color: Colors.black,
+ ),
+ InkWell(
+ onTap: onDelete,
+ child: SizedBox(
+ height: 56,
+ child: Center(
+ child: Text(
+ 'Delete',
+ style: CustomTextStyles.blackW40017,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/screens/group_contacts/widgets/groups_manage_members_widget.dart b/lib/screens/group_contacts/widgets/groups_manage_members_widget.dart
new file mode 100644
index 00000000..31a43c60
--- /dev/null
+++ b/lib/screens/group_contacts/widgets/groups_manage_members_widget.dart
@@ -0,0 +1,259 @@
+import 'package:at_common_flutter/services/size_config.dart';
+import 'package:at_contact/at_contact.dart';
+import 'package:at_contacts_group_flutter/models/group_contacts_model.dart';
+import 'package:at_contacts_group_flutter/services/group_service.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_app_bar.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_member_item_widget.dart';
+import 'package:atsign_atmosphere_pro/utils/colors.dart';
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:atsign_atmosphere_pro/utils/vectors.dart';
+import 'package:atsign_atmosphere_pro/view_models/trusted_sender_view_model.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:provider/provider.dart';
+
+class GroupsManageMembersWidget extends StatefulWidget {
+ const GroupsManageMembersWidget();
+
+ @override
+ State createState() =>
+ _GroupsManageMembersWidgetState();
+}
+
+class _GroupsManageMembersWidgetState extends State {
+ TextEditingController searchController = TextEditingController();
+ late TrustedContactProvider trustedContactProvider =
+ context.read();
+ final _groupService = GroupService();
+
+ @override
+ void initState() {
+ super.initState();
+ WidgetsBinding.instance.addPostFrameCallback((_) async {
+ await _groupService.fetchGroupsAndContacts();
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: ColorConstants.background,
+ appBar: GroupsAppBar(title: 'Manage Members'),
+ body: SafeArea(
+ child: Stack(
+ children: [
+ Column(
+ children: [
+ SizedBox(height: 28),
+ Padding(
+ padding: EdgeInsets.only(left: 36, right: 20),
+ child: buildSearchWidget(),
+ ),
+ SizedBox(height: 12),
+ Expanded(
+ child: StreamBuilder>(
+ stream: _groupService.allContactsStream,
+ initialData: _groupService.allContacts,
+ builder: (context, snapshot) {
+ final contactList = (snapshot.data ?? [])
+ .where((e) => e?.contact != null)
+ .map((e) => e!.contact!)
+ .toList();
+ return ListView.separated(
+ physics: ClampingScrollPhysics(),
+ padding: EdgeInsets.only(
+ left: 32,
+ right: 32,
+ bottom: 112,
+ ),
+ itemBuilder: (context, index) {
+ return buildContactList(contactList)[index];
+ },
+ separatorBuilder: (context, index) {
+ return SizedBox(height: 12);
+ },
+ itemCount: buildContactList(contactList).length,
+ );
+ },
+ ),
+ ),
+ ],
+ ),
+ Positioned(
+ bottom: 28,
+ left: 48,
+ right: 48,
+ child: buildSelectContactButton(),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget buildSearchWidget() {
+ return Row(
+ children: [
+ Expanded(
+ child: TextField(
+ maxLines: 1,
+ style: CustomTextStyles.blackW50014,
+ decoration: InputDecoration(
+ contentPadding: EdgeInsets.only(
+ left: 20,
+ top: 20,
+ bottom: 20,
+ right: 24,
+ ),
+ hintText: 'Search',
+ hintStyle: CustomTextStyles.greyW50014,
+ border: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(10),
+ borderSide: BorderSide.none,
+ ),
+ fillColor: Colors.white,
+ filled: true,
+ isDense: true,
+ suffixIcon: Icon(
+ Icons.search,
+ size: 20,
+ color: ColorConstants.grey,
+ ),
+ ),
+ controller: searchController,
+ ),
+ ),
+ SizedBox(width: 12),
+ InkWell(
+ child: Container(
+ width: 40,
+ height: 40,
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ color: ColorConstants.iconButtonColor,
+ shape: BoxShape.circle,
+ ),
+ child: SvgPicture.asset(
+ AppVectors.icTrust,
+ width: 24,
+ height: 20,
+ color: Colors.black,
+ fit: BoxFit.cover,
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+
+ List buildContactList(List list) {
+ final List result = [];
+ if (list.isNotEmpty) {
+ final List sortedList = sortGroupAlphabetical(list);
+ bool isSameCharWithPrev(int i) =>
+ (((sortedList[i].atSign ?? '').isNotEmpty
+ ? sortedList[i].atSign![1]
+ : ' ') !=
+ ((sortedList[i - 1].atSign ?? '').isNotEmpty
+ ? sortedList[i - 1].atSign![1]
+ : ' '));
+
+ bool isPrevCharacter(int i) => RegExp(r'^[a-z]+$').hasMatch(
+ (((sortedList[i - 1].atSign ?? '').isNotEmpty
+ ? sortedList[i - 1].atSign![1]
+ : ' '))[0]
+ .toLowerCase());
+
+ for (int i = 0; i < sortedList.length; i++) {
+ if (i == 0 || (isSameCharWithPrev(i) && isPrevCharacter(i))) {
+ result.add(buildAlphabeticalTitle(
+ (sortedList[i].atSign ?? '').isNotEmpty
+ ? sortedList[i].atSign![1]
+ : ''));
+ }
+ result.add(
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 4),
+ child: GroupsMemberItemWidget(
+ member: sortedList[i],
+ isTrusted: trustedContactProvider.trustedContacts.any(
+ (element) => element.atSign == sortedList[i].atSign,
+ ),
+ ),
+ ),
+ );
+ }
+ }
+ return result;
+ }
+
+ Widget buildAlphabeticalTitle(String char) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(vertical: 4),
+ child: Row(
+ children: [
+ char.isNotEmpty && RegExp(r'^[a-z]+$').hasMatch(char.toLowerCase())
+ ? Text(
+ char.toUpperCase(),
+ style: CustomTextStyles.darkSliverBold20,
+ )
+ : Text(
+ 'Others',
+ style: CustomTextStyles.darkSliverBold20,
+ ),
+ SizedBox(width: 20),
+ Divider(
+ height: 1.toHeight,
+ color: ColorConstants.dividerColor,
+ )
+ ],
+ ),
+ );
+ }
+
+ List sortGroupAlphabetical(List list) {
+ final List emptyTitleContact = list
+ .where((e) =>
+ (e.atSign ?? '').isEmpty ||
+ !RegExp(r'^[a-z]+$').hasMatch(
+ (e.atSign ?? '')[1].toLowerCase(),
+ ))
+ .toList();
+ final List nonEmptyTitleContact = list
+ .where((e) =>
+ (e.atSign ?? '').isNotEmpty &&
+ RegExp(r'^[a-z]+$').hasMatch(
+ (e.atSign ?? '')[1].toLowerCase(),
+ ))
+ .toList();
+ nonEmptyTitleContact.sort(
+ (a, b) => (a.atSign?[1] ?? '').compareTo(
+ (b.atSign?[1] ?? ''),
+ ),
+ );
+ return [...nonEmptyTitleContact, ...emptyTitleContact];
+ }
+
+ Widget buildSelectContactButton() {
+ return Container(
+ padding: EdgeInsets.symmetric(vertical: 16),
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ color: Colors.black,
+ borderRadius: BorderRadius.circular(7),
+ ),
+ child: RichText(
+ text: TextSpan(
+ text: 'Selects Contact ',
+ style: CustomTextStyles.whiteBold16,
+ children: [
+ TextSpan(
+ text: '(3)',
+ style: CustomTextStyles.whiteW40016,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/screens/group_contacts/widgets/groups_member_item_widget.dart b/lib/screens/group_contacts/widgets/groups_member_item_widget.dart
new file mode 100644
index 00000000..81fbd08d
--- /dev/null
+++ b/lib/screens/group_contacts/widgets/groups_member_item_widget.dart
@@ -0,0 +1,91 @@
+import 'dart:typed_data';
+
+import 'package:at_contact/at_contact.dart';
+import 'package:atsign_atmosphere_pro/screens/common_widgets/contact_initial.dart';
+import 'package:atsign_atmosphere_pro/utils/colors.dart';
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:atsign_atmosphere_pro/utils/vectors.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+class GroupsMemberItemWidget extends StatelessWidget {
+ final AtContact member;
+ final bool isTrusted;
+ final bool isSelected;
+
+ const GroupsMemberItemWidget({
+ required this.member,
+ required this.isTrusted,
+ this.isSelected = false,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ padding: EdgeInsets.fromLTRB(16, 16, 24, 16),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(10),
+ border: isSelected
+ ? Border.all(
+ color: ColorConstants.orange,
+ width: 2,
+ )
+ : null,
+ ),
+ child: Row(
+ children: [
+ member.tags?['image'] != null
+ ? ClipRRect(
+ borderRadius: BorderRadius.circular(18),
+ child: Image.memory(
+ Uint8List.fromList(member.tags?['image'].cast()),
+ width: 40,
+ height: 40,
+ fit: BoxFit.cover,
+ ),
+ )
+ : ContactInitial(
+ initials: member.atSign?.substring(1),
+ borderRadius: 18,
+ ),
+ SizedBox(width: 16),
+ Expanded(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ member.atSign ?? '',
+ style: CustomTextStyles.blackW60013,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
+ if (member.tags?['nickname'] != null)
+ Text(
+ member.tags?['nickname'],
+ style: CustomTextStyles.blackW60013,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
+ ],
+ )),
+ SizedBox(width: 16),
+ SizedBox(
+ width: 28,
+ height: 28,
+ child: SvgPicture.asset(
+ isTrusted
+ ? AppVectors.icTrustActivated
+ : AppVectors.icTrustDeactivated,
+ width: 24,
+ height: 20,
+ fit: BoxFit.cover,
+ alignment: Alignment.center,
+ ),
+ )
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/screens/group_contacts/widgets/groups_member_list_view.dart b/lib/screens/group_contacts/widgets/groups_member_list_view.dart
new file mode 100644
index 00000000..0aac6fe0
--- /dev/null
+++ b/lib/screens/group_contacts/widgets/groups_member_list_view.dart
@@ -0,0 +1,43 @@
+import 'package:at_contact/at_contact.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_member_item_widget.dart';
+import 'package:atsign_atmosphere_pro/view_models/trusted_sender_view_model.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+class GroupsMemberListView extends StatefulWidget {
+ final Set members;
+
+ const GroupsMemberListView({
+ required this.members,
+ });
+
+ @override
+ State createState() => _GroupsMemberListViewState();
+}
+
+class _GroupsMemberListViewState extends State {
+ late TrustedContactProvider trustedContactProvider =
+ context.read();
+
+ @override
+ Widget build(BuildContext context) {
+ return ListView.separated(
+ padding: EdgeInsets.symmetric(horizontal: 36, vertical: 28),
+ shrinkWrap: true,
+ physics: NeverScrollableScrollPhysics(),
+ itemBuilder: (context, index) {
+ return GroupsMemberItemWidget(
+ member: widget.members.elementAt(index),
+ isTrusted: trustedContactProvider.trustedContacts.any(
+ (element) =>
+ element.atSign == widget.members.elementAt(index).atSign,
+ ),
+ );
+ },
+ separatorBuilder: (context, index) {
+ return SizedBox(height: 16);
+ },
+ itemCount: widget.members.length,
+ );
+ }
+}
diff --git a/lib/screens/trusted_contacts/trusted_contacts.dart b/lib/screens/trusted_contacts/trusted_contacts.dart
index 5717d159..855cae04 100644
--- a/lib/screens/trusted_contacts/trusted_contacts.dart
+++ b/lib/screens/trusted_contacts/trusted_contacts.dart
@@ -9,7 +9,7 @@ import 'package:atsign_atmosphere_pro/screens/common_widgets/contact_initial.dar
import 'package:atsign_atmosphere_pro/screens/common_widgets/custom_button.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/custom_circle_avatar.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/provider_handler.dart';
-import 'package:atsign_atmosphere_pro/screens/group_contacts_screen/widgets/group_contact_list_tile.dart';
+import 'package:atsign_atmosphere_pro/screens/common_widgets/contact_list_tile.dart';
import 'package:atsign_atmosphere_pro/screens/trusted_contacts/widgets/remove_trusted_contact_dialog.dart';
import 'package:atsign_atmosphere_pro/utils/images.dart';
import 'package:atsign_atmosphere_pro/utils/text_strings.dart';
diff --git a/lib/screens/welcome_screen/widgets/overlapping_contacts.dart b/lib/screens/welcome_screen/widgets/overlapping_contacts.dart
index 17b6294e..7f87541b 100644
--- a/lib/screens/welcome_screen/widgets/overlapping_contacts.dart
+++ b/lib/screens/welcome_screen/widgets/overlapping_contacts.dart
@@ -7,7 +7,7 @@ import 'package:at_contact/at_contact.dart';
import 'package:at_contacts_group_flutter/models/group_contacts_model.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/contact_initial.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/custom_circle_avatar.dart';
-import 'package:atsign_atmosphere_pro/screens/group_contacts_screen/widgets/group_contact_list_tile.dart';
+import 'package:atsign_atmosphere_pro/screens/common_widgets/contact_list_tile.dart';
import 'package:atsign_atmosphere_pro/screens/welcome_screen/widgets/contact_card.dart';
import 'package:atsign_atmosphere_pro/services/common_utility_functions.dart';
import 'package:atsign_atmosphere_pro/view_models/trusted_sender_view_model.dart';
diff --git a/lib/utils/text_styles.dart b/lib/utils/text_styles.dart
index 31864d3d..9aac0c8f 100644
--- a/lib/utils/text_styles.dart
+++ b/lib/utils/text_styles.dart
@@ -373,6 +373,12 @@ class CustomTextStyles {
letterSpacing: 0.1,
fontWeight: FontWeight.normal);
+ static TextStyle desktopPrimaryW50018 = TextStyle(
+ color: Colors.black,
+ fontSize: 18,
+ fontWeight: FontWeight.w500,
+ );
+
static TextStyle desktopPrimaryRegular16 = TextStyle(
color: Colors.black,
fontSize: 16,
@@ -405,6 +411,12 @@ class CustomTextStyles {
fontWeight: FontWeight.w500,
);
+ static TextStyle whiteW40016 = TextStyle(
+ color: Colors.white,
+ fontSize: 16,
+ fontWeight: FontWeight.w400,
+ );
+
static TextStyle blackW50020 = const TextStyle(
color: Colors.black,
fontSize: 20,
@@ -412,7 +424,10 @@ class CustomTextStyles {
);
static TextStyle orangeW50014 = TextStyle(
- color: ColorConstants.orange, fontSize: 14, fontWeight: FontWeight.w500);
+ color: ColorConstants.orange,
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ );
static TextStyle whiteBold12 = const TextStyle(
color: Colors.white,
@@ -432,6 +447,26 @@ class CustomTextStyles {
fontWeight: FontWeight.w400,
);
+ static TextStyle blackUnderlineW40012 = const TextStyle(
+ color: Colors.black,
+ fontSize: 12,
+ fontWeight: FontWeight.w400,
+ decoration: TextDecoration.underline,
+ );
+
+ static TextStyle blackW40013 = const TextStyle(
+ color: Colors.black,
+ fontSize: 13,
+ fontWeight: FontWeight.w400,
+ );
+
+ static TextStyle blackItalicW40013 = const TextStyle(
+ color: Colors.black,
+ fontSize: 13,
+ fontWeight: FontWeight.w400,
+ fontStyle: FontStyle.italic,
+ );
+
static TextStyle darkSliverBold20 = TextStyle(
color: ColorConstants.darkSliver,
fontSize: 20,
@@ -474,6 +509,12 @@ class CustomTextStyles {
fontSize: 12,
);
+ static TextStyle blackW40017 = TextStyle(
+ color: Colors.black,
+ fontWeight: FontWeight.w400,
+ fontSize: 17,
+ );
+
static TextStyle raisinBlackW40010 = TextStyle(
color: ColorConstants.raisinBlack,
fontSize: 10,
@@ -498,6 +539,12 @@ class CustomTextStyles {
fontSize: 11,
);
+ static TextStyle blackW5008 = TextStyle(
+ color: Colors.black,
+ fontWeight: FontWeight.w500,
+ fontSize: 8,
+ );
+
static TextStyle blackW50014 = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w500,
@@ -515,4 +562,10 @@ class CustomTextStyles {
fontSize: 11,
fontWeight: FontWeight.w400,
);
+
+ static TextStyle greyW50014 = TextStyle(
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ color: ColorConstants.grey,
+ );
}
diff --git a/lib/utils/vectors.dart b/lib/utils/vectors.dart
index 80108e90..36e7dc6d 100644
--- a/lib/utils/vectors.dart
+++ b/lib/utils/vectors.dart
@@ -98,4 +98,7 @@ class AppVectors {
static String icDownloadDisable = '$_basePath/ic_download_disable.svg';
static String icSendDisable = '$_basePath/ic_send_disable.svg';
static String icDeleteDisable = '$_basePath/ic_delete_disable.svg';
+ static String icPencil = '$_basePath/ic_pencil.svg';
+ static String icMobileImage = '$_basePath/ic_mobile_image.svg';
+ static String icTrustDeactivated = '$_basePath/ic_trust_deactivated.svg';
}
diff --git a/test/widget_tests/group_contact_screen_test/group_contact_list_test.dart b/test/widget_tests/group_contact_screen_test/group_contact_list_test.dart
index 3a9a6b08..28a364c3 100644
--- a/test/widget_tests/group_contact_screen_test/group_contact_list_test.dart
+++ b/test/widget_tests/group_contact_screen_test/group_contact_list_test.dart
@@ -1,6 +1,6 @@
import 'package:at_common_flutter/at_common_flutter.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/contact_initial.dart';
-import 'package:atsign_atmosphere_pro/screens/group_contacts_screen/widgets/group_contact_list_tile.dart';
+import 'package:atsign_atmosphere_pro/screens/common_widgets/contact_list_tile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';