changes as per asked | app icon | removal of chats/groups separation etc

This commit is contained in:
Muhammad Hamza 2021-10-13 01:58:29 +05:00
parent deeda09ca3
commit 368f9ef617
41 changed files with 418 additions and 625 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<clipPath id="clip-path">
<rect id="Rectangle_2" data-name="Rectangle 2" width="2971" height="843" transform="translate(0 0.292)" fill="#fff"/>
</clipPath>
<clipPath id="clip-simpleX">
<rect width="1024" height="1024"/>
</clipPath>
</defs>
<g id="simpleX" clip-path="url(#clip-simpleX)">
<g id="logo" transform="translate(-2047 89.708)" clip-path="url(#clip-path)">
<path id="Path_6" data-name="Path 6" d="M147.9,66.914a198.176,198.176,0,0,1,36.975,3.455,284.4,284.4,0,0,1,34.949,8.885,253.446,253.446,0,0,1,31.15,12.34q14.436,6.911,26.592,13.821L241.1,172.542l-.5-.548q-2.161-2.105-10.138-6.856a202.569,202.569,0,0,0-22.793-11.353,224.542,224.542,0,0,0-29.884-10.365,122.159,122.159,0,0,0-32.417-4.442q-44.573,0-44.573,29.122,0,8.885,4.812,14.808t14.182,10.613a133.233,133.233,0,0,0,23.553,8.636q14.182,3.952,32.923,8.885a437.841,437.841,0,0,1,46.6,15.056q20.767,8.143,35.2,20.236A82.161,82.161,0,0,1,280.355,275.7q7.851,17.275,7.851,41.459,0,29.615-11.4,50.1a94.466,94.466,0,0,1-30.137,33.073A129.137,129.137,0,0,1,203.618,418.6a219.939,219.939,0,0,1-50.145,5.675,287.161,287.161,0,0,1-40.521-2.961,299.792,299.792,0,0,1-40.521-8.639A327.515,327.515,0,0,1,34.19,399.1,215.84,215.84,0,0,1,0,380.839l36.469-70.584.649.688q2.77,2.644,12.52,8.69a199.248,199.248,0,0,0,27.6,13.822,287.382,287.382,0,0,0,36.722,12.338,157.851,157.851,0,0,0,41.027,5.429q44.067,0,44.067-26.16a21.773,21.773,0,0,0-6.585-16.287q-6.585-6.417-18.235-11.6a201.006,201.006,0,0,0-27.6-9.624q-15.955-4.446-34.7-9.874a304.855,304.855,0,0,1-43.054-16.041Q50.651,253,38.748,241.647a72.548,72.548,0,0,1-17.728-26.16Q15.2,200.676,15.2,180.933q0-27.641,10.637-48.865A105.947,105.947,0,0,1,54.7,96.283,126.757,126.757,0,0,1,97,74.318,172.123,172.123,0,0,1,147.9,66.914Zm331.4,2.961v350.45H396.237V69.875Zm218.452,0L790.957,253,884.663,69.875h89.652v350.45H891.247V210.55l-78,152.518H768.671l-78-152.518V420.325H607.6V69.875Zm833.178,0V349.249h174.241v71.076H1447.868V69.875Zm510.939,0v71.077H1876.752V209.07h141.823v65.647H1876.752v74.533h169.683v71.076H1793.684V69.875Zm-795.7,0,5.687.128a106.112,106.112,0,0,1,41.674,10.237,121.073,121.073,0,0,1,36.975,27.148,129.172,129.172,0,0,1,24.313,38.006,110.611,110.611,0,0,1,8.863,42.943,120.122,120.122,0,0,1-8.36,44.423,126.219,126.219,0,0,1-23.3,38.006,109.262,109.262,0,0,1-77.64,36.4l-5.684.123h-72.937V420.325h-83.068V69.875Zm-5.571,71.077h-64.832V236.71h67.873q14.689,0,25.323-12.342t10.638-36.03q0-12.341-3.292-21.225a43.245,43.245,0,0,0-8.863-14.808,33.932,33.932,0,0,0-12.665-8.638A39.4,39.4,0,0,0,1240.607,140.953Z" transform="translate(0 175.261)" fill="#062d56" fill-rule="evenodd"/>
<path id="Path_7" data-name="Path 7" d="M644.551,487.448,781.926,623.9l70.013-69.545-.026-.022L991.95,415.257,854.575,278.818l.025-.012-68.682-68.227-140.036,139.1-.011-.013L785.908,210.581,648.551,74.126,718.563,4.578,855.92,141.033,995.957,1.935l68.682,68.227L924.6,209.258l137.364,136.45L1202,206.616l68.685,68.227L1130.641,413.932l137.386,136.459-70.013,69.549L1060.632,483.481,920.607,622.575l137.382,136.463-70.013,69.549L850.594,692.12,710.583,831.228,641.9,763,781.916,623.9,644.541,487.458Z" transform="translate(1698.3 5.061)" fill="#07b4b9" fill-rule="evenodd"/>
<path id="Path_8" data-name="Path 8" d="M657.51,61.677,794.867,198.13,934.9,59.035l68.686,68.227L863.549,266.357l137.375,136.455-70.013,69.549L793.536,335.9,653.518,475l-68.682-68.228,140.022-139.1L587.5,131.225Z" transform="translate(1547.321 154.623)" fill="#062d56" fill-rule="evenodd"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 B

After

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -7,7 +7,7 @@ import 'package:simplex_chat/providers/drawer_providers.dart';
import 'package:simplex_chat/views/contacts/add_contact_view.dart';
import 'package:simplex_chat/views/group/add_group_view.dart';
import 'package:simplex_chat/views/onboarding/intro_view.dart';
import 'package:simplex_chat/views/scanInvitation/scan_invitation_view.dart';
import 'package:simplex_chat/views/scan_invitation/scan_invitation_view.dart';
import 'package:simplex_chat/views/setup_profile_view.dart';
import 'package:simplex_chat/views/splash_screen.dart';

View File

@ -2,21 +2,32 @@ import 'dart:convert';
class Contact {
final String name;
final String msg;
final String msgTime;
final String subtitle;
final String photo;
final bool isGroup;
Contact({this.name, this.msg, this.msgTime});
Contact({
this.name,
this.subtitle,
this.photo = '',
this.isGroup = false,
});
factory Contact.fromJson(Map<String, dynamic> json) {
return Contact(
name: json['name'], msg: json['msg'], msgTime: json['msgTime']);
name: json['name'],
subtitle: json['subtitle'],
photo: json['photo'],
isGroup: json['isGroup'],
);
}
static Map<String, dynamic> toJson(Contact contact) {
return {
'name': contact.name,
'msg': contact.msg,
'msgTime': contact.msgTime,
'subtitle': contact.subtitle,
'photo': contact.photo,
'isGroup': false,
};
}

View File

@ -1,32 +1,37 @@
import 'dart:convert';
class Group {
final String photoPath;
final String groupName;
final String groupDescription;
final String name;
final String subtitle;
final String photo;
final List<dynamic> members;
final bool isGroup;
Group(
{this.groupName,
this.photoPath,
this.groupDescription,
this.members = const []});
Group({
this.name,
this.subtitle,
this.photo = '',
this.isGroup = true,
this.members = const [],
});
factory Group.fromJson(Map<String, dynamic> json) {
return Group(
photoPath: json['gPhoto'],
groupName: json['gName'],
groupDescription: json['desc'],
members: json['contacts'],
name: json['name'],
subtitle: json['subtitle'],
photo: json['photo'],
isGroup: json['isGroup'],
members: json['members'],
);
}
static Map<String, dynamic> toJson(Group group) {
return {
'gPhoto': group.photoPath,
'gName': group.groupName,
'desc': group.groupDescription,
'contacts': group.members,
'name': group.name,
'subtitle': group.subtitle,
'photo': group.photo,
'isGroup': true,
'members': group.members,
};
}

View File

@ -1,46 +1,40 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.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/contact.dart';
import 'package:simplex_chat/model/group.dart';
import 'package:simplex_chat/providers/drawer_providers.dart';
import 'package:simplex_chat/views/conversation/conversation_view.dart';
class ContactsView extends StatefulWidget {
const ContactsView({Key key}) : super(key: key);
class Conversations extends StatefulWidget {
const Conversations({Key key}) : super(key: key);
@override
_ContactsViewState createState() => _ContactsViewState();
_ConversationsState createState() => _ConversationsState();
}
class _ContactsViewState extends State<ContactsView> {
class _ConversationsState extends State<Conversations> {
bool _eraseMedia = false;
String _photo = '';
String _displayName = '';
List<dynamic> _conversations = [];
List<Contact> _contactsList = []; // for storing contacts
List<Group> _groupList = []; // for storing groups
final List<String> _options = [
'Add contact',
'Scan invitation',
'Add group',
];
// delete a contact
void _deleteContact(Contact contact) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_contactsList.remove(contact);
});
await prefs.setString('contacts', Contact.encode(_contactsList));
var snackBar = SnackBar(
backgroundColor: Colors.red,
content: Text('${contact.name} deleted!'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
// getting data from local storage FOR NOW!!
void _getContacts() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
@ -52,8 +46,18 @@ class _ContactsViewState extends State<ContactsView> {
}
}
String _photo = '';
String _displayName = '';
// getting data from local storage FOR NOW!!
void _getGroups() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final String _groups = prefs.getString('groups');
if (_groups != null) {
setState(() {
_groupList = List.from(Group.decode(_groups));
});
}
_gettingGroupContactsChats();
}
void _getUserData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
@ -63,15 +67,24 @@ class _ContactsViewState extends State<ContactsView> {
});
}
void _gettingGroupContactsChats() {
setState(() {
_conversations = List.from(_contactsList);
_conversations = _conversations + _groupList;
});
}
@override
void initState() {
_getUserData();
_getContacts();
_getGroups();
super.initState();
}
@override
Widget build(BuildContext context) {
final _drawerProviders = Provider.of<DrawerProvider>(context);
return Scaffold(
backgroundColor: Colors.white,
body: SingleChildScrollView(
@ -83,16 +96,21 @@ class _ContactsViewState extends State<ContactsView> {
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Column(
GestureDetector(
onTap: _addNewContacts,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('Hi! $_displayName', style: kSmallHeadingStyle),
const Text('Good day!'),
],
),
),
const SizedBox(width: 10.0),
GestureDetector(
onTap: _addNewContacts,
onTap: () {
_drawerProviders.currentIndex = 0;
},
child: CircleAvatar(
backgroundImage: _photo.isEmpty
? const AssetImage('assets/dp.png') as ImageProvider
@ -107,7 +125,7 @@ class _ContactsViewState extends State<ContactsView> {
Icon(Icons.chat, color: kPrimaryColor),
SizedBox(width: 8.0),
Text(
'Chats',
'Conversations',
style: kHeadingStyle,
)
],
@ -137,29 +155,49 @@ class _ContactsViewState extends State<ContactsView> {
: ListView(
shrinkWrap: true,
children: List.generate(
_contactsList.length,
_conversations.length,
(index) => WidgetAnimator(
child: ListTile(
leading: const CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'),
leading: CircleAvatar(
backgroundImage:
// ignore: avoid_dynamic_calls
_conversations[index].photo == ''
? const AssetImage('assets/dp.png')
as ImageProvider
: FileImage(
// ignore: avoid_dynamic_calls
File(_conversations[index].photo)),
),
title: Text(_contactsList[index].name),
subtitle: Text(_contactsList[index].msg),
trailing: Text(
_contactsList[index].msgTime,
style: const TextStyle(
fontSize: 11, color: Colors.grey),
// ignore: avoid_dynamic_calls
title: Text(_conversations[index].name),
// ignore: avoid_dynamic_calls
subtitle: Text(_conversations[index].subtitle),
// ignore: avoid_dynamic_calls
trailing: Icon(
// ignore: avoid_dynamic_calls
_conversations[index].isGroup
? Icons.group
: Icons.person,
size: 18,
color: Colors.grey[400],
),
onTap: () => Navigator.push(
onTap: () async {
var value = await Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ConversationView(
contact: _contactsList[index],
),
data: _conversations[index],
),
),
);
value ??= false;
if (value) {
_getContacts();
_getGroups();
}
},
onLongPress: () =>
_conversationOptions(_contactsList[index]),
_conversationOptions(_conversations[index]),
),
),
),
@ -173,12 +211,17 @@ class _ContactsViewState extends State<ContactsView> {
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
offset: const Offset(-10, -120),
onSelected: (value) {
offset: const Offset(-10, -155),
onSelected: (value) async {
if (value == _options[0]) {
Navigator.pushNamed(context, AppRoutes.scanInvitation);
await Navigator.pushNamed(context, AppRoutes.scanInvitation);
} else if (value == _options[1]) {
await Navigator.pushNamed(context, AppRoutes.addContact);
} else {
Navigator.pushNamed(context, AppRoutes.addContact);
var value = await Navigator.pushNamed(context, AppRoutes.addGroup);
if (value) {
_getGroups();
}
}
},
itemBuilder: (context) => _options
@ -200,7 +243,39 @@ class _ContactsViewState extends State<ContactsView> {
);
}
void _conversationOptions(Contact contact) {
// delete a contact
void _deleteContact(Contact contact) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_contactsList.remove(contact);
});
await prefs.setString('contacts', Contact.encode(_contactsList));
var snackBar = SnackBar(
backgroundColor: Colors.red,
content: Text('${contact.name} deleted!'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
// delete a group
void _deleteGroup(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.name} deleted!'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
void _conversationOptions(var chat) {
showDialog(
context: context,
builder: (context) => AlertDialog(
@ -210,7 +285,7 @@ class _ContactsViewState extends State<ContactsView> {
TextButton(
onPressed: () {
Navigator.pop(context);
_deleteConversation(contact);
_deleteConversation(chat);
},
child: const Text(
'Delete Conversation',
@ -228,7 +303,7 @@ class _ContactsViewState extends State<ContactsView> {
);
}
void _deleteConversation(Contact contact) {
void _deleteConversation(var chat) {
showDialog(
context: context,
builder: (context) => StatefulBuilder(
@ -256,8 +331,16 @@ class _ContactsViewState extends State<ContactsView> {
),
actions: [
InkWell(
onTap: () {
_deleteContact(contact);
// ignore: avoid_dynamic_calls
onTap: chat.isGroup
? () {
_deleteGroup(chat);
_conversations.remove(chat);
Navigator.pop(context);
}
: () {
_deleteContact(chat);
_conversations.remove(chat);
Navigator.pop(context);
},
child: const Padding(
@ -329,6 +412,7 @@ class _ContactsViewState extends State<ContactsView> {
void _addNewContacts() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
// adding dummy contact
List<Contact> _localList = [];
final String _local = prefs.getString('contacts');
if (_local != null) {
@ -338,18 +422,7 @@ class _ContactsViewState extends State<ContactsView> {
List<Contact> _newList = [
Contact(
name: 'Harry',
msg: 'Hello!',
msgTime: 'Just now',
),
Contact(
name: 'Ayesha',
msg: 'OK!',
msgTime: 'Just now',
),
Contact(
name: 'Larry',
msg: 'Yep, Already done!',
msgTime: 'Just now',
subtitle: 'Hello!',
),
];
_newList = _localList + _newList;
@ -358,7 +431,29 @@ class _ContactsViewState extends State<ContactsView> {
final String _newContacts = Contact.encode(_newList);
await prefs.setString('contacts', _newContacts);
// adding dummy contact
List<Group> _localListGroup = [];
final String _localGroups = prefs.getString('groups');
if (_local != null) {
_localListGroup = List.from(Group.decode(_localGroups));
}
List<Group> _newGroups = [
Group(
name: 'College Friends',
subtitle: 'Lovely people',
members: ['Alice', 'James', 'Rio']),
];
_newGroups = _localListGroup + _newGroups;
// dummy ftn for filling the list
final String _newGroup = Group.encode(_newGroups);
await prefs.setString('groups', _newGroup);
_getContacts();
_getGroups();
const snackBar = SnackBar(
backgroundColor: Colors.green,
content: Text('New contacts loaded!'),

View File

@ -10,10 +10,12 @@ class ContactDetailsConversation extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(contact.name),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 12.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@ -27,6 +29,7 @@ class ContactDetailsConversation extends StatelessWidget {
],
),
),
),
);
}
}

View File

@ -1,16 +1,13 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:simplex_chat/model/contact.dart';
import 'package:simplex_chat/model/group.dart';
import 'package:simplex_chat/views/conversation/contact_detail_conversation.dart';
import 'package:simplex_chat/views/conversation/group_detail_conversation.dart';
import 'package:simplex_chat/widgets/message_bubble.dart';
class ConversationView extends StatefulWidget {
final Contact contact;
final Group group;
const ConversationView({Key key, this.contact, this.group}) : super(key: key);
final data;
const ConversationView({Key key, this.data}) : super(key: key);
@override
_ConversationViewState createState() => _ConversationViewState();
@ -49,45 +46,72 @@ class _ConversationViewState extends State<ConversationView> {
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
appBar: AppBar(
leading: InkWell(
onTap: () => Navigator.of(context).pop(true),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.arrow_back),
const SizedBox(width: 8.0),
CircleAvatar(
backgroundImage: widget.contact == null
? widget.group.photoPath == ''
? const AssetImage('assets/dp.png')
: FileImage(
File(widget.group.photoPath),
)
: const AssetImage('assets/dp.png')),
],
leading: BackButton(
onPressed: () => Navigator.of(context).pop(true),
),
),
leadingWidth: MediaQuery.of(context).size.width * 0.2,
title: Text(widget.contact != null
? widget.contact.name
: widget.group.groupName),
actions: [
IconButton(
onPressed: widget.contact != null
? () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ContactDetailsConversation(
contact: widget.contact,
)))
: () => Navigator.push(
leadingWidth: MediaQuery.of(context).size.width * 0.085,
title: InkWell(
// ignore: avoid_dynamic_calls
onTap: widget.data.isGroup
? () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => GroupDetailsConversation(
group: widget.group,
))),
icon: const Icon(Icons.info),
group: widget.data,
),
),
);
}
: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ContactDetailsConversation(
contact: widget.data,
),
),
);
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
children: [
CircleAvatar(
radius: 15,
// ignore: avoid_dynamic_calls
backgroundImage: widget.data.photo == ''
? const AssetImage('assets/dp.png')
: FileImage(
// ignore: avoid_dynamic_calls
File(widget.data.photo),
),
),
const SizedBox(width: 10.0),
Text(
// ignore: avoid_dynamic_calls
widget.data.name,
),
],
),
),
),
actions: [
IconButton(
onPressed: () {
setState(() {
_chatMessages.add(MessageBubble(
isUser: false,
// ignore: avoid_dynamic_calls
sender: widget.data.name,
text: 'Hey there! How is it going?',
));
});
},
icon: const Icon(
Icons.message,
),
)
],
),
body: Column(
@ -139,10 +163,10 @@ class _ConversationViewState extends State<ConversationView> {
),
),
)),
const SizedBox(width: 15.0),
InkWell(
onTap: () async {
if (_messageFieldController.text == '') {
const SizedBox(width: 4.0),
IconButton(
onPressed: () {
if (_messageFieldController.text.isNotEmpty) {
setState(() {
_chatMessages.add(MessageBubble(
isUser: true,
@ -154,7 +178,7 @@ class _ConversationViewState extends State<ConversationView> {
_focus.unfocus();
}
},
child: const Icon(Icons.send_rounded,
icon: const Icon(Icons.send_rounded,
size: 25.0, color: Colors.teal),
),
],

View File

@ -62,7 +62,8 @@ class _GroupDetailsConversationState extends State<GroupDetailsConversation> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.group.groupName),
centerTitle: true,
title: Text(widget.group.name),
),
body: SingleChildScrollView(
child: Padding(
@ -73,14 +74,14 @@ class _GroupDetailsConversationState extends State<GroupDetailsConversation> {
Center(
child: CircleAvatar(
radius: 70,
backgroundImage: widget.group.photoPath == ''
backgroundImage: widget.group.photo == ''
? const AssetImage('assets/dp.png')
: FileImage(File(widget.group.photoPath)),
: FileImage(File(widget.group.photo)),
),
),
const SizedBox(height: 25.0),
const Text('Group Name', style: kMediumHeadingStyle),
Text(widget.group.groupName),
Text(widget.group.name),
const Divider(),
ListTile(
leading: const Icon(Icons.person_add),
@ -197,14 +198,14 @@ class _GroupDetailsConversationState extends State<GroupDetailsConversation> {
int index = 0;
index = _groupList
.indexWhere((group) => group.groupName == widget.group.groupName);
.indexWhere((group) => group.name == widget.group.name);
// add the full _members list to the group
Group _updatedGroup = Group(
groupName: widget.group.groupName,
groupDescription: widget.group.groupDescription,
name: widget.group.name,
subtitle: widget.group.subtitle,
members: _members,
photoPath: widget.group.photoPath,
photo: widget.group.photo,
);
// put it in updated group local list
@ -235,13 +236,13 @@ class _GroupDetailsConversationState extends State<GroupDetailsConversation> {
int index = 0;
index = _groupList
.indexWhere((group) => group.groupName == widget.group.groupName);
.indexWhere((group) => group.name == widget.group.name);
// new instance of group (updated)
Group _updatedGroup = Group(
groupName: widget.group.groupName,
groupDescription: widget.group.groupDescription,
photoPath: widget.group.photoPath,
name: widget.group.name,
subtitle: widget.group.subtitle,
photo: widget.group.photo,
members: _members,
);

View File

@ -252,7 +252,6 @@ class _AddGroupViewState extends State<AddGroupView> {
if (_formKey.currentState.validate()) {
FocusScope.of(context).unfocus();
_addNewGroup(
_groupPhotoPath,
_displayNameController.text.trim(),
_descController.text.trim());
_descController.clear();
@ -398,7 +397,7 @@ class _AddGroupViewState extends State<AddGroupView> {
}
}
void _addNewGroup(String photo, String name, String desc) async {
void _addNewGroup(String name, String desc) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<Group> _localList = [];
final String _local = prefs.getString('groups');
@ -407,12 +406,13 @@ class _AddGroupViewState extends State<AddGroupView> {
}
List<Group> _groups = [
Group(
photoPath: _groupPhotoPath,
groupName: name,
groupDescription: desc,
name: name,
photo: _groupPhotoPath,
subtitle: desc,
members: _members,
),
];
debugPrint(_groups[0].isGroup.toString());
_groups = _localList + _groups;
final String _newGroups = Group.encode(_groups);

View File

@ -1,380 +0,0 @@
import 'dart:io';
import 'package:flutter/material.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 a Group',
'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');
if (_groups != null) {
setState(() {
_groupList = List.from(Group.decode(_groups));
});
}
}
String _photo = '';
String _displayName = '';
void _getUserData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_displayName = prefs.getString('displayName');
_photo = prefs.getString('photo$_displayName');
});
}
@override
void initState() {
_getUserData();
_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: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('Hi! $_displayName', style: kSmallHeadingStyle),
const Text('Good day!'),
],
),
const SizedBox(width: 10.0),
CircleAvatar(
backgroundImage: _photo.isEmpty
? const AssetImage('assets/dp.png') as ImageProvider
: FileImage(File(_photo)),
),
],
),
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: CircleAvatar(
backgroundImage: _groupList[index].photoPath == ''
? const AssetImage('assets/dp.png')
as ImageProvider
: FileImage(File(_groupList[index].photoPath)),
),
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),
),
onTap: () async {
var value = await Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
ConversationView(group: _groupList[index]),
),
);
if (value) {
_getGroups();
}
},
onLongPress: () =>
_conversationOptions(_groupList[index]),
),
),
),
),
],
),
),
floatingActionButton: PopupMenuButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
offset: const Offset(-10, -150),
onSelected: (value) async {
if (value == _options[0]) {
var value = await Navigator.pushNamed(context, AppRoutes.addGroup);
if (value == true) {
_getGroups();
}
} else if (value == _options[1]) {
await Navigator.pushNamed(context, AppRoutes.addContact);
} 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
// ignore: unused_element
void _addNewGroups() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<Group> _localList = [];
final String _local = prefs.getString('groups');
if (_local != null) {
_localList = List.from(Group.decode(_local));
}
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);
}
}

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/app_routes.dart';
@ -20,46 +19,23 @@ class MyDrawer extends StatelessWidget {
return Column(
children: [
const SizedBox(height: 30.0),
SvgPicture.asset(
'assets/logo.svg',
height: 50.0,
Image.asset(
'assets/simpleX.png',
height: 85.0,
),
const Divider(height: 50.0),
ListTile(
tileColor: _drawerProviders.currentIndex == 0
? kPrimaryColor
: Colors.transparent,
leading: Icon(
Icons.person,
color: _drawerProviders.currentIndex == 0
? Colors.white
: Colors.grey,
),
title: Text(
'Your Profile',
style: TextStyle(
color: _drawerProviders.currentIndex == 0
? Colors.white
: Colors.black,
),
),
onTap: () {
_drawerProviders.currentIndex = 0;
Navigator.pop(context);
},
),
ListTile(
tileColor: _drawerProviders.currentIndex == 1
? kPrimaryColor
: Colors.transparent,
leading: Icon(
Icons.contact_phone,
Icons.chat,
color: _drawerProviders.currentIndex == 1
? Colors.white
: Colors.grey,
),
title: Text(
'Your contacts',
'Conversations',
style: TextStyle(
color: _drawerProviders.currentIndex == 1
? Colors.white
@ -95,33 +71,21 @@ class MyDrawer extends StatelessWidget {
},
),
ListTile(
tileColor: _drawerProviders.currentIndex == 3
? kPrimaryColor
: Colors.transparent,
leading: Icon(
Icons.group,
color: _drawerProviders.currentIndex == 3
? Colors.white
: Colors.grey,
leading: const Icon(
Icons.settings,
color: Colors.grey,
),
title: Text(
'Your groups',
style: TextStyle(
color: _drawerProviders.currentIndex == 3
? Colors.white
: Colors.black,
),
),
onTap: () {
_drawerProviders.currentIndex = 3;
Navigator.pop(context);
},
title: const Text('Settings'),
onTap: () {},
),
const Spacer(),
ListTile(
leading: const Icon(Icons.refresh),
title: const Text('Switch Profile'),
subtitle: const Text('*Not supported yet!*'),
subtitle: const Text(
'Not supported yet!',
style: TextStyle(fontStyle: FontStyle.italic),
),
onTap: () => _switchProfile(context),
),
],
@ -134,11 +98,8 @@ class MyDrawer extends StatelessWidget {
void _switchProfile(BuildContext context) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await Navigator.pushNamedAndRemoveUntil(
context,
AppRoutes.setupProfile,
(route) => route.settings.name == AppRoutes.setupProfile ? true : false,
);
int _count = 0;
Navigator.of(context).popUntil((route) => _count++ >= 2);
String _name = prefs.getString('displayName');
await prefs.remove('displayName');
await prefs.remove('fullName');

View File

@ -4,8 +4,7 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/providers/drawer_providers.dart';
import 'package:simplex_chat/views/contacts/contacts_view.dart';
import 'package:simplex_chat/views/group/group_view.dart';
import 'package:simplex_chat/views/contacts/conversations.dart';
import 'package:simplex_chat/views/home/drawer.dart';
import 'package:simplex_chat/views/invitations/invitation_view.dart';
import 'package:simplex_chat/views/profile/profile_view.dart';
@ -22,9 +21,8 @@ class _HomeViewState extends State<HomeView> {
// views
final List<Widget> _views = [
const ProfileView(),
const ContactsView(),
const Conversations(),
const Invitations(),
const GroupView(),
];
@override
@ -44,7 +42,17 @@ class _HomeViewState extends State<HomeView> {
Positioned(
top: MediaQuery.of(context).size.height * 0.03,
left: MediaQuery.of(context).size.width * 0.03,
child: InkWell(
child: _drawerProviders.currentIndex == 0
? InkWell(
onTap: () {
_drawerProviders.currentIndex = 1;
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.arrow_back),
),
)
: InkWell(
onTap: () {
Scaffold.of(context).openDrawer();
},

View File

@ -72,7 +72,7 @@ class _InvitationsState extends State<Invitations> {
child: const Center(
child: Text(
"You don't have any invitations yet!",
style: kMediumHeadingStyle,
style: kSmallHeadingStyle,
textAlign: TextAlign.center,
),
),

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:progress_indicators/progress_indicators.dart';
import 'package:simplex_chat/animations/entrance_fader.dart';
import 'package:simplex_chat/app_routes.dart';
@ -13,8 +12,8 @@ class SplashScreen extends StatefulWidget {
}
class _SplashScreenState extends State<SplashScreen> {
// logincheck
void _loginCheck() {
// delay on splash screen
void _splashDelay() {
Future.delayed(const Duration(seconds: 4), () {
Navigator.pushNamed(
context,
@ -25,7 +24,7 @@ class _SplashScreenState extends State<SplashScreen> {
@override
void initState() {
_loginCheck();
_splashDelay();
super.initState();
}
@ -38,14 +37,14 @@ class _SplashScreenState extends State<SplashScreen> {
children: [
EntranceFader(
duration: const Duration(seconds: 1),
offset: const Offset(0, 20),
child: SvgPicture.asset(
'assets/logo.svg',
height: 70,
offset: const Offset(0, 15),
child: Image.asset(
'assets/simpleX.png',
height: 150,
),
),
EntranceFader(
offset: const Offset(0, 00),
offset: const Offset(0, 0),
duration: const Duration(seconds: 1),
delay: const Duration(seconds: 1),
child: JumpingDotsProgressIndicator(

View File

@ -60,6 +60,7 @@ class _CustomTextFieldState extends State<CustomTextField> {
width: width * 0.89,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8.0)),
child: TextFormField(
textCapitalization: TextCapitalization.sentences,
controller: widget.textEditingController,
textInputAction: TextInputAction.done,
keyboardType: widget.textInputType,

View File

@ -1,6 +1,20 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
archive:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.6"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.0"
async:
dependency: transitive
description:
@ -50,6 +64,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.1+5"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
cupertino_icons:
dependency: "direct main"
description:
@ -90,6 +111,13 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.2"
flutter_lints:
dependency: "direct dev"
description:
@ -135,6 +163,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
image:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.8"
image_picker:
dependency: "direct main"
description:
@ -427,6 +462,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.2"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.14.0 <3.0.0"
flutter: ">=2.5.0"

View File

@ -63,6 +63,12 @@ dev_dependencies:
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^1.0.0
flutter_launcher_icons: ^0.9.2
flutter_icons:
image_path: "assets/simpleX.png"
android: true
ios: true
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec