group added

This commit is contained in:
Muhammad Hamza
2021-10-10 10:54:02 +05:00
parent 70dbf3966d
commit e1d5e29365
3 changed files with 468 additions and 42 deletions
+36
View File
@@ -0,0 +1,36 @@
import 'dart:convert';
class Group {
final String? groupName;
final String? groupDescription;
final List<dynamic> members;
Group({this.groupName, this.groupDescription, this.members = const []});
factory Group.fromJson(Map<String, dynamic> json) {
return Group(
groupName: json['gName'],
groupDescription: json['desc'],
members: json['contacts'],
);
}
static Map<String, dynamic> toJson(Group group) {
return {
'gName': group.groupName,
'desc': group.groupDescription,
'contacts': group.members,
};
}
static String encode(List<Group> groups) => json.encode(
groups
.map<Map<String, dynamic>>((group) => Group.toJson(group))
.toList(),
);
static List<Group> decode(String? groups) =>
(json.decode(groups!) as List<dynamic>)
.map<Group>((item) => Group.fromJson(item))
.toList();
}
@@ -2,7 +2,9 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/model/group.dart';
import 'package:simplex_chat/widgets/custom_text_field.dart';
class AddGroupView extends StatefulWidget {
@@ -13,11 +15,15 @@ class AddGroupView extends StatefulWidget {
}
class _AddGroupViewState extends State<AddGroupView> {
final _formKey = GlobalKey<FormState>();
final _displayNameController = TextEditingController();
final _descController = TextEditingController();
@override
void dispose() {
_displayNameController.dispose();
_descController.dispose();
super.dispose();
}
@@ -27,57 +33,79 @@ class _AddGroupViewState extends State<AddGroupView> {
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
appBar: AppBar(
leading: BackButton(
onPressed: () => Navigator.of(context).pop(false),
),
title: const Text('New Group'),
),
body: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 10.0),
const Center(
child: GroupDP(),
),
const SizedBox(height: 25.0),
const Text('Group Name', style: kSmallHeadingStyle),
const SizedBox(height: 10.0),
CustomTextField(
textEditingController: _displayNameController,
textInputType: TextInputType.name,
hintText: 'e.g College friends',
validatorFtn: (value) {
if (value!.isEmpty) {
return 'Group name cannot be empty!';
}
return null;
},
),
const SizedBox(height: 10.0),
ListTile(
leading: const Icon(Icons.person_add),
title: const Text('Add a member'),
onTap: () {},
),
const Divider(height: 30.0),
const ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 10.0),
const Center(
child: GroupDP(),
),
title: Text('You'),
trailing: Text(
'Owner',
style: TextStyle(color: Colors.grey, fontSize: 12.0),
)),
],
const SizedBox(height: 25.0),
const Text('Group Name', style: kSmallHeadingStyle),
const SizedBox(height: 10.0),
CustomTextField(
textEditingController: _displayNameController,
textInputType: TextInputType.name,
hintText: 'e.g College friends',
validatorFtn: (value) {
if (value!.isEmpty) {
return 'Group name cannot be empty!';
}
return null;
},
),
const SizedBox(height: 10.0),
const Text('Group Description', style: kSmallHeadingStyle),
const SizedBox(height: 10.0),
CustomTextField(
textEditingController: _descController,
textInputType: TextInputType.text,
hintText: 'e.g Friends from UK',
),
const SizedBox(height: 10.0),
ListTile(
leading: const Icon(Icons.person_add),
title: const Text('Add a member'),
onTap: () {},
),
const Divider(height: 30.0),
const ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'),
),
title: Text('You'),
trailing: Text(
'Owner',
style: TextStyle(color: Colors.grey, fontSize: 12.0),
)),
],
),
),
),
),
floatingActionButton: Visibility(
visible: MediaQuery.of(context).viewInsets.bottom == 0,
child: FloatingActionButton(
heroTag: 'setup',
onPressed: () {
FocusScope.of(context).unfocus();
Navigator.pop(context);
onPressed: () async {
if (_formKey.currentState!.validate()) {
FocusScope.of(context).unfocus();
_addNewGroup(_displayNameController.text.trim(),
_descController.text.trim());
_descController.clear();
_displayNameController.clear();
Navigator.of(context).pop(true);
}
},
child: const Icon(Icons.check),
),
@@ -85,6 +113,33 @@ class _AddGroupViewState extends State<AddGroupView> {
),
);
}
void _addNewGroup(String name, String desc) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<Group> _localList = [];
_localList = List.from(Group.decode(prefs.getString('groups')));
List<Group> _groups = [
Group(
groupName: name,
groupDescription: desc,
members: <String>[],
),
];
_groups = _localList + _groups;
final String _newGroups = Group.encode(_groups);
await prefs.setString('groups', _newGroups);
var snackBar = SnackBar(
backgroundColor: Colors.green,
content: Text('$name added!'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
}
class GroupDP extends StatefulWidget {
@@ -0,0 +1,335 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/animations/bottom_animation.dart';
import 'package:simplex_chat/app_routes.dart';
import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/model/group.dart';
import 'package:simplex_chat/views/conversation/conversation_view.dart';
class GroupView extends StatefulWidget {
const GroupView({Key? key}) : super(key: key);
@override
State<GroupView> createState() => _GroupViewState();
}
class _GroupViewState extends State<GroupView> {
bool? _eraseMedia = false;
final List<String> _options = [
'Add group',
'Scan invitation',
];
List<Group> _groupList = [];
// delete a group
void _deleteContact(Group group) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_groupList.remove(group);
});
await prefs.setString('groups', Group.encode(_groupList));
var snackBar = SnackBar(
backgroundColor: Colors.red,
content: Text('${group.groupName} deleted!'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
// getting data from local storage FOR NOW!!
void _getGroups() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final String? _groups = prefs.getString('groups');
setState(() {
_groupList = List.from(Group.decode(_groups));
});
}
@override
void initState() {
_getGroups();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0),
child: Column(
children: [
Align(
alignment: Alignment.centerRight,
child: GestureDetector(
onTap: _addNewGroups,
child: SvgPicture.asset(
'assets/logo.svg',
height: 40.0,
),
),
),
const SizedBox(height: 15.0),
Row(
children: const [
Icon(Icons.group, color: kPrimaryColor),
SizedBox(width: 8.0),
Text(
'Groups',
style: kHeadingStyle,
)
],
),
const SizedBox(height: 5.0),
_groupList.isEmpty
? SizedBox(
height: MediaQuery.of(context).size.height * 0.7,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
"You don't have any groups yet!",
style: kMediumHeadingStyle,
textAlign: TextAlign.center,
),
SizedBox(height: 8.0),
Text(
'Click the icon below to add a contact',
textAlign: TextAlign.center,
),
],
),
),
)
: ListView(
shrinkWrap: true,
children: List.generate(
_groupList.length,
(index) => WidgetAnimator(
child: ListTile(
leading: const CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'),
),
title: Text(_groupList[index].groupName!),
subtitle: Text(_groupList[index].groupDescription!),
trailing: Text(
'Members: ${_groupList[index].members.length}',
style: const TextStyle(
fontSize: 11, color: Colors.grey),
),
onLongPress: () =>
_conversationOptions(_groupList[index]),
),
),
),
),
],
),
),
floatingActionButton: PopupMenuButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
offset: const Offset(-10, -120),
onSelected: (value) async {
if (value == _options[0]) {
var value = await Navigator.pushNamed(context, AppRoutes.addGroup);
if (value == true) {
_getGroups();
}
} else {
await Navigator.pushNamed(context, AppRoutes.scanInvitation);
}
},
itemBuilder: (context) => _options
.map(
(opt) => PopupMenuItem(
value: opt,
child: Text(opt),
),
)
.toList(),
child: const FloatingActionButton(
heroTag: 'group',
onPressed: null,
child: Icon(
Icons.group_add,
),
),
),
);
}
void _conversationOptions(Group group) {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
_deleteConversation(group);
},
child: const Text(
'Delete Group',
style: TextStyle(color: Colors.red),
)),
TextButton(
onPressed: () {
Navigator.pop(context);
_disconnect();
},
child: const Text('Leave Group')),
],
),
),
);
}
void _deleteConversation(Group group) {
showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setState) => AlertDialog(
title: const Text('Are you Sure?'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('All group history will be deleted from your device!'),
const SizedBox(height: 15.0),
Row(
children: [
Checkbox(
value: _eraseMedia,
onChanged: (value) {
setState(() {
_eraseMedia = value;
});
}),
const Text('Erase files & Media')
],
),
],
),
actions: [
InkWell(
onTap: () {
_deleteContact(group);
Navigator.pop(context);
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.check, color: Colors.green),
),
),
InkWell(
onTap: () => Navigator.pop(context),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.cancel_outlined, color: Colors.red),
),
)
],
),
),
);
}
void _disconnect() {
showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setState) => AlertDialog(
title: const Text('Are you Sure?'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Leaving a group will erase all the data from your device and you will no longer be able to contact again!'),
const SizedBox(height: 15.0),
Row(
children: [
Checkbox(
value: _eraseMedia,
onChanged: (value) {
setState(() {
_eraseMedia = value;
});
}),
const Text('Erase files & Media')
],
),
],
),
actions: [
InkWell(
onTap: () => Navigator.pop(context),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.check, color: Colors.green),
),
),
InkWell(
onTap: () => Navigator.pop(context),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.cancel_outlined, color: Colors.red),
),
)
],
),
),
);
}
// dummy ftn for loading new contacts
void _addNewGroups() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<Group> _localList = [];
_localList = List.from(Group.decode(prefs.getString('groups')));
List<Group> _groups = [
Group(
groupName: 'Family group',
groupDescription: 'Some description here',
members: <String>[
'Hamza',
'Alice',
'John',
'Bob',
],
),
Group(
groupName: 'Friends',
groupDescription: 'Miss you all',
members: <String>[
'Alice',
'John',
'Bob',
],
),
];
_groups = _localList + _groups;
// dummy ftn for filling the list
final String _newGroups = Group.encode(_groups);
await prefs.setString('groups', _newGroups);
_getGroups();
const snackBar = SnackBar(
backgroundColor: Colors.green,
content: Text('New Groups loaded!'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
}