overall bug fixes + UI changes

This commit is contained in:
Muhammad Hamza 2021-10-11 14:57:05 +05:00
parent 2275b582b4
commit 4f5ba12f2d
25 changed files with 746 additions and 445 deletions

View File

@ -6,7 +6,7 @@ class Animator extends StatefulWidget {
final Widget child; final Widget child;
final Duration time; final Duration time;
const Animator(this.child, this.time, {Key? key}) : super(key: key); const Animator(this.child, this.time, {Key key}) : super(key: key);
@override @override
_AnimatorState createState() => _AnimatorState(); _AnimatorState createState() => _AnimatorState();
@ -14,9 +14,9 @@ class Animator extends StatefulWidget {
class _AnimatorState extends State<Animator> class _AnimatorState extends State<Animator>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
Timer? timer; Timer timer;
AnimationController? animationController; AnimationController animationController;
Animation? animation; Animation animation;
@override @override
void initState() { void initState() {
@ -24,27 +24,27 @@ class _AnimatorState extends State<Animator>
animationController = AnimationController( animationController = AnimationController(
duration: const Duration(milliseconds: 290), vsync: this); duration: const Duration(milliseconds: 290), vsync: this);
animation = animation =
CurvedAnimation(parent: animationController!, curve: Curves.easeInOut); CurvedAnimation(parent: animationController, curve: Curves.easeInOut);
timer = Timer(widget.time, animationController!.forward); timer = Timer(widget.time, animationController.forward);
} }
@override @override
void dispose() { void dispose() {
animationController!.dispose(); animationController.dispose();
super.dispose(); super.dispose();
timer!.cancel(); timer.cancel();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AnimatedBuilder( return AnimatedBuilder(
animation: animation!, animation: animation,
child: widget.child, child: widget.child,
builder: (BuildContext context, Widget? child) { builder: (BuildContext context, Widget child) {
return Opacity( return Opacity(
opacity: animation!.value, opacity: animation.value,
child: Transform.translate( child: Transform.translate(
offset: Offset(0.0, (1 - animation!.value) * 20), offset: Offset(0.0, (1 - animation.value) * 20),
child: child, child: child,
), ),
); );
@ -53,11 +53,11 @@ class _AnimatorState extends State<Animator>
} }
} }
Timer? timer; Timer timer;
Duration duration = const Duration(); Duration duration = const Duration();
Duration wait() { Duration wait() {
if (timer == null || !timer!.isActive) { if (timer == null || !timer.isActive) {
timer = Timer(const Duration(microseconds: 120), () { timer = Timer(const Duration(microseconds: 120), () {
duration = const Duration(); duration = const Duration();
}); });
@ -67,12 +67,12 @@ Duration wait() {
} }
class WidgetAnimator extends StatelessWidget { class WidgetAnimator extends StatelessWidget {
final Widget? child; final Widget child;
const WidgetAnimator({this.child, Key? key}) : super(key: key); const WidgetAnimator({this.child, Key key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Animator(child!, wait()); return Animator(child, wait());
} }
} }

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
class EntranceFader extends StatefulWidget { class EntranceFader extends StatefulWidget {
/// Child to be animated on entrance /// Child to be animated on entrance
final Widget? child; final Widget child;
/// Delay after which the animation will start /// Delay after which the animation will start
final Duration delay; final Duration delay;
@ -14,7 +14,7 @@ class EntranceFader extends StatefulWidget {
final Offset offset; final Offset offset;
const EntranceFader({ const EntranceFader({
Key? key, Key key,
this.child, this.child,
this.delay = const Duration(milliseconds: 0), this.delay = const Duration(milliseconds: 0),
this.duration = const Duration(milliseconds: 400), this.duration = const Duration(milliseconds: 400),
@ -29,39 +29,39 @@ class EntranceFader extends StatefulWidget {
class EntranceFaderState extends State<EntranceFader> class EntranceFaderState extends State<EntranceFader>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
AnimationController? _controller; AnimationController _controller;
Animation? _dxAnimation; Animation _dxAnimation;
Animation? _dyAnimation; Animation _dyAnimation;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_controller = AnimationController(vsync: this, duration: widget.duration); _controller = AnimationController(vsync: this, duration: widget.duration);
_dxAnimation = _dxAnimation =
Tween(begin: widget.offset.dx, end: 0.0).animate(_controller!); Tween(begin: widget.offset.dx, end: 0.0).animate(_controller);
_dyAnimation = _dyAnimation =
Tween(begin: widget.offset.dy, end: 0.0).animate(_controller!); Tween(begin: widget.offset.dy, end: 0.0).animate(_controller);
Future.delayed(widget.delay, () { Future.delayed(widget.delay, () {
if (mounted) { if (mounted) {
_controller!.forward(); _controller.forward();
} }
}); });
} }
@override @override
void dispose() { void dispose() {
_controller!.dispose(); _controller.dispose();
super.dispose(); super.dispose();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AnimatedBuilder( return AnimatedBuilder(
animation: _controller!, animation: _controller,
builder: (context, child) => Opacity( builder: (context, child) => Opacity(
opacity: _controller!.value, opacity: _controller.value,
child: Transform.translate( child: Transform.translate(
offset: Offset(_dxAnimation!.value, _dyAnimation!.value), offset: Offset(_dxAnimation.value, _dyAnimation.value),
child: widget.child, child: widget.child,
), ),
), ),

View File

@ -14,5 +14,6 @@ class ScrollBehaviorModified extends ScrollBehavior {
case TargetPlatform.windows: case TargetPlatform.windows:
return const ClampingScrollPhysics(); return const ClampingScrollPhysics();
} }
return null;
} }
} }

View File

@ -17,7 +17,7 @@ void main() {
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({Key key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -38,9 +38,7 @@ class MyApp extends StatelessWidget {
), ),
builder: (context, widget) { builder: (context, widget) {
return ScrollConfiguration( return ScrollConfiguration(
behavior: const ScrollBehaviorModified(), behavior: const ScrollBehaviorModified(), child: widget);
child: widget!,
);
}, },
initialRoute: AppRoutes.splash, initialRoute: AppRoutes.splash,
routes: <String, WidgetBuilder>{ routes: <String, WidgetBuilder>{

View File

@ -1,9 +1,9 @@
import 'dart:convert'; import 'dart:convert';
class Contact { class Contact {
final String? name; final String name;
final String? msg; final String msg;
final String? msgTime; final String msgTime;
Contact({this.name, this.msg, this.msgTime}); Contact({this.name, this.msg, this.msgTime});
@ -26,8 +26,8 @@ class Contact {
.toList(), .toList(),
); );
static List<Contact> decode(String? contacts) => static List<Contact> decode(String contacts) =>
(json.decode(contacts!) as List<dynamic>) (json.decode(contacts) as List<dynamic>)
.map<Contact>((item) => Contact.fromJson(item)) .map<Contact>((item) => Contact.fromJson(item))
.toList(); .toList();
} }

View File

@ -1,14 +1,20 @@
import 'dart:convert'; import 'dart:convert';
class Group { class Group {
final String? groupName; final String photoPath;
final String? groupDescription; final String groupName;
final String groupDescription;
final List<dynamic> members; final List<dynamic> members;
Group({this.groupName, this.groupDescription, this.members = const []}); Group(
{this.groupName,
this.photoPath,
this.groupDescription,
this.members = const []});
factory Group.fromJson(Map<String, dynamic> json) { factory Group.fromJson(Map<String, dynamic> json) {
return Group( return Group(
photoPath: json['gPhoto'],
groupName: json['gName'], groupName: json['gName'],
groupDescription: json['desc'], groupDescription: json['desc'],
members: json['contacts'], members: json['contacts'],
@ -17,6 +23,7 @@ class Group {
static Map<String, dynamic> toJson(Group group) { static Map<String, dynamic> toJson(Group group) {
return { return {
'gPhoto': group.photoPath,
'gName': group.groupName, 'gName': group.groupName,
'desc': group.groupDescription, 'desc': group.groupDescription,
'contacts': group.members, 'contacts': group.members,
@ -29,8 +36,8 @@ class Group {
.toList(), .toList(),
); );
static List<Group> decode(String? groups) => static List<Group> decode(String groups) =>
(json.decode(groups!) as List<dynamic>) (json.decode(groups) as List<dynamic>)
.map<Group>((item) => Group.fromJson(item)) .map<Group>((item) => Group.fromJson(item))
.toList(); .toList();
} }

View File

@ -1,7 +1,7 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
class DrawerProvider extends ChangeNotifier { class DrawerProvider extends ChangeNotifier {
int _currentIndex = 0; int _currentIndex = 1;
int get currentIndex => _currentIndex; int get currentIndex => _currentIndex;
@ -9,13 +9,4 @@ class DrawerProvider extends ChangeNotifier {
_currentIndex = value; _currentIndex = value;
notifyListeners(); notifyListeners();
} }
// toggle drawer
TickerFuture toggle(
AnimationController? animationController) {
return animationController!.isDismissed
? animationController.forward()
: animationController.reverse();
}
} }

View File

@ -5,7 +5,7 @@ import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:simplex_chat/views/contacts/qr_code_details_view.dart'; import 'package:simplex_chat/views/contacts/qr_code_details_view.dart';
class AddContactView extends StatefulWidget { class AddContactView extends StatefulWidget {
const AddContactView({Key? key}) : super(key: key); const AddContactView({Key key}) : super(key: key);
@override @override
_AddContactViewState createState() => _AddContactViewState(); _AddContactViewState createState() => _AddContactViewState();
@ -13,16 +13,16 @@ class AddContactView extends StatefulWidget {
class _AddContactViewState extends State<AddContactView> { class _AddContactViewState extends State<AddContactView> {
final qrKey = GlobalKey(debugLabel: 'qr'); final qrKey = GlobalKey(debugLabel: 'qr');
QRViewController? _qrViewController; QRViewController _qrViewController;
Barcode? result; Barcode result;
@override @override
void reassemble() { void reassemble() {
super.reassemble(); super.reassemble();
if (Platform.isAndroid) { if (Platform.isAndroid) {
_qrViewController!.pauseCamera(); _qrViewController.pauseCamera();
} else if (Platform.isIOS) { } else if (Platform.isIOS) {
_qrViewController!.resumeCamera(); _qrViewController.resumeCamera();
} }
} }

View File

@ -9,14 +9,14 @@ import 'package:simplex_chat/model/contact.dart';
import 'package:simplex_chat/views/conversation/conversation_view.dart'; import 'package:simplex_chat/views/conversation/conversation_view.dart';
class ContactsView extends StatefulWidget { class ContactsView extends StatefulWidget {
const ContactsView({Key? key}) : super(key: key); const ContactsView({Key key}) : super(key: key);
@override @override
_ContactsViewState createState() => _ContactsViewState(); _ContactsViewState createState() => _ContactsViewState();
} }
class _ContactsViewState extends State<ContactsView> { class _ContactsViewState extends State<ContactsView> {
bool? _eraseMedia = false; bool _eraseMedia = false;
List<Contact> _contactsList = []; // for storing contacts List<Contact> _contactsList = []; // for storing contacts
@ -44,14 +44,16 @@ class _ContactsViewState extends State<ContactsView> {
// getting data from local storage FOR NOW!! // getting data from local storage FOR NOW!!
void _getContacts() async { void _getContacts() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
final String? _contacts = prefs.getString('contacts'); final String _contacts = prefs.getString('contacts');
setState(() { if (_contacts != null) {
_contactsList = List.from(Contact.decode(_contacts)); setState(() {
}); _contactsList = List.from(Contact.decode(_contacts));
});
}
} }
String? _photo; String _photo = '';
String? _displayName; String _displayName = '';
void _getUserData() async { void _getUserData() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
@ -89,10 +91,13 @@ class _ContactsViewState extends State<ContactsView> {
], ],
), ),
const SizedBox(width: 10.0), const SizedBox(width: 10.0),
CircleAvatar( GestureDetector(
backgroundImage: _photo == null onTap: _addNewContacts,
? const AssetImage('assets/dp.png') as ImageProvider child: CircleAvatar(
: FileImage(File(_photo!)), backgroundImage: _photo.isEmpty
? const AssetImage('assets/dp.png') as ImageProvider
: FileImage(File(_photo)),
),
) )
], ],
), ),
@ -138,10 +143,10 @@ class _ContactsViewState extends State<ContactsView> {
leading: const CircleAvatar( leading: const CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'), backgroundImage: AssetImage('assets/dp.png'),
), ),
title: Text(_contactsList[index].name!), title: Text(_contactsList[index].name),
subtitle: Text(_contactsList[index].msg!), subtitle: Text(_contactsList[index].msg),
trailing: Text( trailing: Text(
_contactsList[index].msgTime!, _contactsList[index].msgTime,
style: const TextStyle( style: const TextStyle(
fontSize: 11, color: Colors.grey), fontSize: 11, color: Colors.grey),
), ),
@ -171,9 +176,9 @@ class _ContactsViewState extends State<ContactsView> {
offset: const Offset(-10, -120), offset: const Offset(-10, -120),
onSelected: (value) { onSelected: (value) {
if (value == _options[0]) { if (value == _options[0]) {
Navigator.pushNamed(context, AppRoutes.addContact);
} else {
Navigator.pushNamed(context, AppRoutes.scanInvitation); Navigator.pushNamed(context, AppRoutes.scanInvitation);
} else {
Navigator.pushNamed(context, AppRoutes.addContact);
} }
}, },
itemBuilder: (context) => _options itemBuilder: (context) => _options
@ -325,7 +330,10 @@ class _ContactsViewState extends State<ContactsView> {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
List<Contact> _localList = []; List<Contact> _localList = [];
_localList = List.from(Contact.decode(prefs.getString('contacts'))); final String _local = prefs.getString('contacts');
if (_local != null) {
_localList = List.from(Contact.decode(_local));
}
List<Contact> _newList = [ List<Contact> _newList = [
Contact( Contact(

View File

@ -4,9 +4,9 @@ import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/widgets/custom_btn.dart'; import 'package:simplex_chat/widgets/custom_btn.dart';
class QRCodeDetailsView extends StatelessWidget { class QRCodeDetailsView extends StatelessWidget {
final Barcode? barcode; final Barcode barcode;
const QRCodeDetailsView({ const QRCodeDetailsView({
Key? key, Key key,
this.barcode, this.barcode,
}) : super(key: key); }) : super(key: key);
@ -26,7 +26,7 @@ class QRCodeDetailsView extends StatelessWidget {
), ),
const SizedBox(height: 30.0), const SizedBox(height: 30.0),
Text( Text(
barcode!.code, barcode.code,
style: kMediumHeadingStyle, style: kMediumHeadingStyle,
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),

View File

@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
import 'package:simplex_chat/widgets/message_bubble.dart'; import 'package:simplex_chat/widgets/message_bubble.dart';
class ConversationView extends StatefulWidget { class ConversationView extends StatefulWidget {
final String? name; final String name;
const ConversationView({Key? key, @required this.name}) : super(key: key); const ConversationView({Key key, @required this.name}) : super(key: key);
@override @override
_ConversationViewState createState() => _ConversationViewState(); _ConversationViewState createState() => _ConversationViewState();
@ -13,17 +13,17 @@ class _ConversationViewState extends State<ConversationView> {
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
final TextEditingController _messageFieldController = TextEditingController(); final TextEditingController _messageFieldController = TextEditingController();
FocusNode? _focus; FocusNode _focus;
bool _fieldEnabled = false; bool _fieldEnabled = false;
final List<Widget> _chatMessages = []; final List<Widget> _chatMessages = [];
@override @override
void initState() { void initState() {
_focus = FocusNode(); _focus = FocusNode();
_focus!.addListener(() { _focus.addListener(() {
debugPrint('FOCUS ${_focus!.hasFocus}'); debugPrint('FOCUS ${_focus.hasFocus}');
_fieldEnabled = _focus!.hasFocus; _fieldEnabled = _focus.hasFocus;
debugPrint('MESSAGE ENABLED! $_fieldEnabled'); debugPrint('MESSAGE ENABLED $_fieldEnabled');
}); });
super.initState(); super.initState();
} }
@ -32,7 +32,7 @@ class _ConversationViewState extends State<ConversationView> {
void dispose() { void dispose() {
_messageFieldController.dispose(); _messageFieldController.dispose();
_scrollController.dispose(); _scrollController.dispose();
_focus!.dispose(); _focus.dispose();
super.dispose(); super.dispose();
} }
@ -42,7 +42,7 @@ class _ConversationViewState extends State<ConversationView> {
onTap: () => FocusScope.of(context).unfocus(), onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold( child: Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text('${widget.name}'), title: Text(widget.name),
), ),
body: Column( body: Column(
children: [ children: [
@ -50,7 +50,7 @@ class _ConversationViewState extends State<ConversationView> {
child: Container( child: Container(
child: _chatMessages.isEmpty child: _chatMessages.isEmpty
? const Center( ? const Center(
child: Text('Send a message to get started!'), child: Text('Send a message to get started'),
) )
: SingleChildScrollView( : SingleChildScrollView(
controller: _scrollController, controller: _scrollController,
@ -96,7 +96,7 @@ class _ConversationViewState extends State<ConversationView> {
const SizedBox(width: 15.0), const SizedBox(width: 15.0),
InkWell( InkWell(
onTap: () async { onTap: () async {
if (_messageFieldController.text != '') { if (_messageFieldController.text == '') {
setState(() { setState(() {
_chatMessages.add(MessageBubble( _chatMessages.add(MessageBubble(
isUser: true, isUser: true,
@ -105,7 +105,7 @@ class _ConversationViewState extends State<ConversationView> {
)); ));
}); });
_messageFieldController.clear(); _messageFieldController.clear();
_focus!.unfocus(); _focus.unfocus();
} }
}, },
child: const Icon(Icons.send_rounded, child: const Icon(Icons.send_rounded,

View File

@ -9,34 +9,61 @@ import 'package:simplex_chat/model/group.dart';
import 'package:simplex_chat/widgets/custom_text_field.dart'; import 'package:simplex_chat/widgets/custom_text_field.dart';
class AddGroupView extends StatefulWidget { class AddGroupView extends StatefulWidget {
const AddGroupView({Key? key}) : super(key: key); const AddGroupView({Key key}) : super(key: key);
@override @override
_AddGroupViewState createState() => _AddGroupViewState(); _AddGroupViewState createState() => _AddGroupViewState();
} }
class _AddGroupViewState extends State<AddGroupView> { class _AddGroupViewState extends State<AddGroupView> {
bool _addMember = false; // Image Picker --> DP properties
List<Contact> _contactsList = []; // for storing contacts final imgPicker = ImagePicker();
File image;
String _groupPhotoPath = '';
bool _uploading = false;
bool _imageUploaded = false;
final List _members = []; final List _members = [];
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
final _displayNameController = TextEditingController(); final _displayNameController = TextEditingController();
final _descController = TextEditingController(); final _descController = TextEditingController();
// getting data from local storage FOR NOW!! bool _addMember = false;
List<Contact> _contactsList = []; // for storing contacts
// image buttons options
final _dpBtnText = ['Remove', 'Gallery', 'Camera'];
final _dpBtnColors = [Colors.red, Colors.purple, Colors.green];
final _dpBtnIcons = [
Icons.delete,
Icons.photo_rounded,
Icons.camera_alt_rounded
];
// getting data from local storage FOR NOW
void _getContacts() async { void _getContacts() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
final String? _contacts = prefs.getString('contacts'); final String _contacts = prefs.getString('contacts');
setState(() { setState(() {
_contactsList = List.from(Contact.decode(_contacts)); _contactsList = List.from(Contact.decode(_contacts));
}); });
} }
String _userPhotoPath = '';
void _getUserPhoto() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String _photo = prefs.getString('photo${prefs.getString('displayName')}');
if (_photo != null) {
setState(() {
_userPhotoPath = _photo;
});
}
debugPrint(_userPhotoPath);
}
@override @override
void initState() { void initState() {
_getUserPhoto();
_getContacts(); _getContacts();
super.initState(); super.initState();
} }
@ -68,8 +95,50 @@ class _AddGroupViewState extends State<AddGroupView> {
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
const SizedBox(height: 10.0), const SizedBox(height: 10.0),
const Center( Center(
child: GroupDP(), child: SizedBox(
height: 180.0,
width: 180.0,
child: Stack(
children: [
_imageUploaded
? CircleAvatar(
radius: 100.0,
backgroundImage:
FileImage(File(_groupPhotoPath)),
)
: const CircleAvatar(
radius: 100.0,
backgroundImage: AssetImage('assets/dp.png'),
),
Positioned(
right: 0,
bottom: 0,
child: FloatingActionButton(
backgroundColor: kSecondaryColor,
elevation: 2.0,
mini: true,
onPressed: _updateProfilePic,
child: _uploading
? const SizedBox(
height: 18.0,
width: 18.0,
child: CircularProgressIndicator(
strokeWidth: 2.0,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white),
),
)
: const Icon(
Icons.add_a_photo,
size: 20,
),
),
)
],
),
),
), ),
const SizedBox(height: 25.0), const SizedBox(height: 25.0),
const Text('Group Name', style: kSmallHeadingStyle), const Text('Group Name', style: kSmallHeadingStyle),
@ -79,8 +148,8 @@ class _AddGroupViewState extends State<AddGroupView> {
textInputType: TextInputType.name, textInputType: TextInputType.name,
hintText: 'e.g College friends', hintText: 'e.g College friends',
validatorFtn: (value) { validatorFtn: (value) {
if (value!.isEmpty) { if (value.isEmpty) {
return 'Group name cannot be empty!'; return 'Group name cannot be empty';
} }
return null; return null;
}, },
@ -148,10 +217,10 @@ class _AddGroupViewState extends State<AddGroupView> {
leading: const CircleAvatar( leading: const CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'), backgroundImage: AssetImage('assets/dp.png'),
), ),
title: Text(_contactsList[index].name!), title: Text(_contactsList[index].name),
onTap: () { onTap: () {
setState(() { setState(() {
_members.add(_contactsList[index].name!); _members.add(_contactsList[index].name);
}); });
}, },
), ),
@ -159,12 +228,14 @@ class _AddGroupViewState extends State<AddGroupView> {
) )
: Container(), : Container(),
const Divider(height: 30.0), const Divider(height: 30.0),
const ListTile( ListTile(
leading: CircleAvatar( leading: CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'), backgroundImage: _userPhotoPath == ''
? const AssetImage('assets/dp.png') as ImageProvider
: FileImage(File(_userPhotoPath)),
), ),
title: Text('You'), title: const Text('You'),
subtitle: Text( subtitle: const Text(
'Owner', 'Owner',
style: TextStyle(color: Colors.grey, fontSize: 12.0), style: TextStyle(color: Colors.grey, fontSize: 12.0),
)), )),
@ -178,9 +249,11 @@ class _AddGroupViewState extends State<AddGroupView> {
child: FloatingActionButton( child: FloatingActionButton(
heroTag: 'setup', heroTag: 'setup',
onPressed: () async { onPressed: () async {
if (_formKey.currentState!.validate()) { if (_formKey.currentState.validate()) {
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
_addNewGroup(_displayNameController.text.trim(), _addNewGroup(
_groupPhotoPath,
_displayNameController.text.trim(),
_descController.text.trim()); _descController.text.trim());
_descController.clear(); _descController.clear();
_displayNameController.clear(); _displayNameController.clear();
@ -194,102 +267,6 @@ 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: _members,
),
];
_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 {
const GroupDP({Key? key}) : super(key: key);
@override
_GroupDPState createState() => _GroupDPState();
}
class _GroupDPState extends State<GroupDP> {
// Image Picker --> DP properties
final imgPicker = ImagePicker();
File? image;
String photoUrl = '';
bool _uploading = false;
bool _imageUploaded = false;
// image buttons options
final _dpBtnText = ['Remove', 'Gallery', 'Camera'];
final _dpBtnColors = [Colors.red, Colors.purple, Colors.green];
final _dpBtnIcons = [
Icons.delete,
Icons.photo_rounded,
Icons.camera_alt_rounded
];
@override
Widget build(BuildContext context) {
return SizedBox(
height: 180.0,
width: 180.0,
child: Stack(
children: [
_imageUploaded
? CircleAvatar(
radius: 100.0,
backgroundImage: FileImage(image!),
)
: const CircleAvatar(
radius: 100.0,
backgroundImage: AssetImage('assets/dp.png'),
),
Positioned(
right: 0,
bottom: 0,
child: FloatingActionButton(
backgroundColor: kSecondaryColor,
elevation: 2.0,
mini: true,
onPressed: _updateProfilePic,
child: _uploading
? const SizedBox(
height: 18.0,
width: 18.0,
child: CircularProgressIndicator(
strokeWidth: 2.0,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: const Icon(
Icons.add_a_photo,
size: 20,
),
),
)
],
),
);
}
void _updateProfilePic() { void _updateProfilePic() {
showModalBottomSheet( showModalBottomSheet(
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
@ -356,6 +333,7 @@ class _GroupDPState extends State<GroupDP> {
setState(() { setState(() {
_imageUploaded = false; _imageUploaded = false;
image = null; image = null;
_groupPhotoPath = '';
}); });
Navigator.pop(context); Navigator.pop(context);
} }
@ -376,6 +354,7 @@ class _GroupDPState extends State<GroupDP> {
setState(() { setState(() {
_uploading = false; _uploading = false;
_imageUploaded = true; _imageUploaded = true;
_groupPhotoPath = file.path;
}); });
} else { } else {
setState(() { setState(() {
@ -405,6 +384,7 @@ class _GroupDPState extends State<GroupDP> {
setState(() { setState(() {
_uploading = false; _uploading = false;
_imageUploaded = true; _imageUploaded = true;
_groupPhotoPath = file.path;
}); });
} else { } else {
setState(() { setState(() {
@ -417,4 +397,34 @@ class _GroupDPState extends State<GroupDP> {
rethrow; rethrow;
} }
} }
void _addNewGroup(String photo, String name, String desc) 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(
photoPath: _groupPhotoPath,
groupName: name,
groupDescription: desc,
members: _members,
),
];
_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);
}
} }

View File

@ -1,5 +1,6 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/animations/bottom_animation.dart'; import 'package:simplex_chat/animations/bottom_animation.dart';
import 'package:simplex_chat/app_routes.dart'; import 'package:simplex_chat/app_routes.dart';
@ -8,14 +9,14 @@ import 'package:simplex_chat/model/group.dart';
import 'package:simplex_chat/views/conversation/conversation_view.dart'; import 'package:simplex_chat/views/conversation/conversation_view.dart';
class GroupView extends StatefulWidget { class GroupView extends StatefulWidget {
const GroupView({Key? key}) : super(key: key); const GroupView({Key key}) : super(key: key);
@override @override
State<GroupView> createState() => _GroupViewState(); State<GroupView> createState() => _GroupViewState();
} }
class _GroupViewState extends State<GroupView> { class _GroupViewState extends State<GroupView> {
bool? _eraseMedia = false; bool _eraseMedia = false;
final List<String> _options = [ final List<String> _options = [
'Add group', 'Add group',
'Scan invitation', 'Scan invitation',
@ -42,14 +43,28 @@ class _GroupViewState extends State<GroupView> {
// getting data from local storage FOR NOW!! // getting data from local storage FOR NOW!!
void _getGroups() async { void _getGroups() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
final String? _groups = prefs.getString('groups'); 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(() { setState(() {
_groupList = List.from(Group.decode(_groups)); _displayName = prefs.getString('displayName');
_photo = prefs.getString('photo$_displayName');
}); });
} }
@override @override
void initState() { void initState() {
_getUserData();
_getGroups(); _getGroups();
super.initState(); super.initState();
} }
@ -62,15 +77,23 @@ class _GroupViewState extends State<GroupView> {
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0), padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0),
child: Column( child: Column(
children: [ children: [
Align( Row(
alignment: Alignment.centerRight, mainAxisAlignment: MainAxisAlignment.end,
child: GestureDetector( children: [
onTap: _addNewGroups, Column(
child: SvgPicture.asset( crossAxisAlignment: CrossAxisAlignment.end,
'assets/logo.svg', children: [
height: 40.0, 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), const SizedBox(height: 15.0),
Row( Row(
@ -111,11 +134,14 @@ class _GroupViewState extends State<GroupView> {
_groupList.length, _groupList.length,
(index) => WidgetAnimator( (index) => WidgetAnimator(
child: ListTile( child: ListTile(
leading: const CircleAvatar( leading: CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'), backgroundImage: _groupList[index].photoPath == ''
? const AssetImage('assets/dp.png')
as ImageProvider
: FileImage(File(_groupList[index].photoPath)),
), ),
title: Text(_groupList[index].groupName!), title: Text(_groupList[index].groupName),
subtitle: Text(_groupList[index].groupDescription!), subtitle: Text(_groupList[index].groupDescription),
trailing: Text( trailing: Text(
'Members: ${_groupList[index].members.length}', 'Members: ${_groupList[index].members.length}',
style: const TextStyle( style: const TextStyle(
@ -296,10 +322,14 @@ class _GroupViewState extends State<GroupView> {
} }
// dummy ftn for loading new contacts // dummy ftn for loading new contacts
// ignore: unused_element
void _addNewGroups() async { void _addNewGroups() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
List<Group> _localList = []; List<Group> _localList = [];
_localList = List.from(Group.decode(prefs.getString('groups'))); final String _local = prefs.getString('groups');
if (_local != null) {
_localList = List.from(Group.decode(_local));
}
List<Group> _groups = [ List<Group> _groups = [
Group( Group(

View File

@ -7,19 +7,17 @@ import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/providers/drawer_providers.dart'; import 'package:simplex_chat/providers/drawer_providers.dart';
class MyDrawer extends StatelessWidget { class MyDrawer extends StatelessWidget {
final AnimationController? animationController; const MyDrawer({Key key}) : super(key: key);
const MyDrawer({Key? key, this.animationController}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final _drawerProviders = Provider.of<DrawerProvider>(context); final _drawerProviders = Provider.of<DrawerProvider>(context);
return SizedBox( return SizedBox(
width: MediaQuery.of(context).size.width * 0.82, width: MediaQuery.of(context).size.width * 0.82,
child: Material( child: Padding(
color: Colors.white, padding: const EdgeInsets.all(10.0),
child: Padding( child: Builder(builder: (context) {
padding: const EdgeInsets.all(10.0), return Column(
child: Column(
children: [ children: [
const SizedBox(height: 30.0), const SizedBox(height: 30.0),
SvgPicture.asset( SvgPicture.asset(
@ -32,30 +30,22 @@ class MyDrawer extends StatelessWidget {
? kPrimaryColor ? kPrimaryColor
: Colors.transparent, : Colors.transparent,
leading: Icon( leading: Icon(
Icons.contact_phone, Icons.person,
color: _drawerProviders.currentIndex == 0 color: _drawerProviders.currentIndex == 0
? Colors.white ? Colors.white
: Colors.grey, : Colors.grey,
), ),
title: Text( title: Text(
'Your contacts', 'Your Profile',
style: TextStyle( style: TextStyle(
color: _drawerProviders.currentIndex == 0 color: _drawerProviders.currentIndex == 0
? Colors.white ? Colors.white
: Colors.black, : Colors.black,
), ),
), ),
subtitle: Text(
'Start a conversation right away!',
style: TextStyle(
color: _drawerProviders.currentIndex == 0
? Colors.white
: Colors.grey,
),
),
onTap: () { onTap: () {
_drawerProviders.currentIndex = 0; _drawerProviders.currentIndex = 0;
_drawerProviders.toggle(animationController); Navigator.pop(context);
}, },
), ),
ListTile( ListTile(
@ -63,30 +53,22 @@ class MyDrawer extends StatelessWidget {
? kPrimaryColor ? kPrimaryColor
: Colors.transparent, : Colors.transparent,
leading: Icon( leading: Icon(
Icons.insert_invitation, Icons.contact_phone,
color: _drawerProviders.currentIndex == 1 color: _drawerProviders.currentIndex == 1
? Colors.white ? Colors.white
: Colors.grey, : Colors.grey,
), ),
title: Text( title: Text(
'Invitations', 'Your contacts',
style: TextStyle( style: TextStyle(
color: _drawerProviders.currentIndex == 1 color: _drawerProviders.currentIndex == 1
? Colors.white ? Colors.white
: Colors.black, : Colors.black,
), ),
), ),
subtitle: Text(
'Increase your contact circle!',
style: TextStyle(
color: _drawerProviders.currentIndex == 1
? Colors.white
: Colors.grey,
),
),
onTap: () { onTap: () {
_drawerProviders.currentIndex = 1; _drawerProviders.currentIndex = 1;
_drawerProviders.toggle(animationController); Navigator.pop(context);
}, },
), ),
ListTile( ListTile(
@ -94,47 +76,62 @@ class MyDrawer extends StatelessWidget {
? kPrimaryColor ? kPrimaryColor
: Colors.transparent, : Colors.transparent,
leading: Icon( leading: Icon(
Icons.group, Icons.insert_invitation,
color: _drawerProviders.currentIndex == 2 color: _drawerProviders.currentIndex == 2
? Colors.white ? Colors.white
: Colors.grey, : Colors.grey,
), ),
title: Text( title: Text(
'Your groups', 'Invitations',
style: TextStyle( style: TextStyle(
color: _drawerProviders.currentIndex == 2 color: _drawerProviders.currentIndex == 2
? Colors.white ? Colors.white
: Colors.black, : Colors.black,
), ),
), ),
subtitle: Text( onTap: () {
'Get in touch with numbers!', _drawerProviders.currentIndex = 2;
Navigator.pop(context);
},
),
ListTile(
tileColor: _drawerProviders.currentIndex == 3
? kPrimaryColor
: Colors.transparent,
leading: Icon(
Icons.group,
color: _drawerProviders.currentIndex == 3
? Colors.white
: Colors.grey,
),
title: Text(
'Your groups',
style: TextStyle( style: TextStyle(
color: _drawerProviders.currentIndex == 2 color: _drawerProviders.currentIndex == 3
? Colors.white ? Colors.white
: Colors.grey, : Colors.black,
), ),
), ),
onTap: () { onTap: () {
_drawerProviders.currentIndex = 2; _drawerProviders.currentIndex = 3;
_drawerProviders.toggle(animationController); Navigator.pop(context);
}, },
), ),
const Spacer(), const Spacer(),
ListTile( ListTile(
leading: const Icon(Icons.exit_to_app_rounded), leading: const Icon(Icons.refresh),
title: const Text('Logout'), title: const Text('Switch Profile'),
subtitle: const Text('Good bye! See you soon :)'), subtitle: const Text('*Not supported yet!*'),
onTap: () => _logout(context), onTap: () => _switchProfile(context),
), ),
], ],
), );
), }),
), ),
); );
} }
void _logout(BuildContext context) async { void _switchProfile(BuildContext context) async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
await Navigator.pushNamedAndRemoveUntil( await Navigator.pushNamedAndRemoveUntil(
@ -142,7 +139,7 @@ class MyDrawer extends StatelessWidget {
AppRoutes.setupProfile, AppRoutes.setupProfile,
(route) => route.settings.name == AppRoutes.setupProfile ? true : false, (route) => route.settings.name == AppRoutes.setupProfile ? true : false,
); );
String? _name = prefs.getString('displayName'); String _name = prefs.getString('displayName');
await prefs.remove('displayName'); await prefs.remove('displayName');
await prefs.remove('fullName'); await prefs.remove('fullName');
await prefs.remove('photo$_name'); await prefs.remove('photo$_name');

View File

@ -1,4 +1,3 @@
import 'dart:math' as math;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
@ -9,138 +8,60 @@ import 'package:simplex_chat/views/contacts/contacts_view.dart';
import 'package:simplex_chat/views/group/group_view.dart'; import 'package:simplex_chat/views/group/group_view.dart';
import 'package:simplex_chat/views/home/drawer.dart'; import 'package:simplex_chat/views/home/drawer.dart';
import 'package:simplex_chat/views/invitations/invitation_view.dart'; import 'package:simplex_chat/views/invitations/invitation_view.dart';
import 'package:simplex_chat/views/profile/profile_view.dart';
class HomeView extends StatefulWidget { class HomeView extends StatefulWidget {
final double? maxSlide; final double maxSlide;
const HomeView({ const HomeView({Key key, this.maxSlide}) : super(key: key);
Key? key,
this.maxSlide,
}) : super(key: key);
@override @override
_HomeViewState createState() => _HomeViewState(); _HomeViewState createState() => _HomeViewState();
} }
class _HomeViewState extends State<HomeView> with TickerProviderStateMixin { class _HomeViewState extends State<HomeView> {
AnimationController? animationController;
bool? _canBeDragged;
// views // views
final List<Widget> _views = [ final List<Widget> _views = [
const ProfileView(),
const ContactsView(), const ContactsView(),
const Invitations(), const Invitations(),
const GroupView(), const GroupView(),
]; ];
@override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 250));
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final _drawerProviders = Provider.of<DrawerProvider>(context); final _drawerProviders = Provider.of<DrawerProvider>(context);
return WillPopScope( return WillPopScope(
onWillPop: _onWillPop, onWillPop: _onWillPop,
child: GestureDetector( child: SafeArea(
onHorizontalDragStart: _onDragStart, child: Scaffold(
onHorizontalDragUpdate: _onDragUpdate, drawer: const Drawer(
onHorizontalDragEnd: _onDragEnd, child: MyDrawer(),
behavior: HitTestBehavior.translucent, ),
child: AnimatedBuilder( body: Builder(builder: (context) {
animation: animationController!, return Stack(
builder: (context, _) { children: [
return Material( _views[_drawerProviders.currentIndex],
color: Colors.white70, Positioned(
child: SafeArea( top: MediaQuery.of(context).size.height * 0.03,
child: Stack( left: MediaQuery.of(context).size.width * 0.03,
children: [ child: InkWell(
Transform.translate( onTap: () {
offset: Offset( Scaffold.of(context).openDrawer();
widget.maxSlide! * (animationController!.value - 1), },
0), child: Padding(
child: Transform( padding: const EdgeInsets.all(8.0),
transform: Matrix4.identity() child: SvgPicture.asset('assets/menu.svg'),
..setEntry(3, 2, 0.001)
..rotateY(
math.pi / 2 * (1 - animationController!.value)),
alignment: Alignment.centerRight,
child: MyDrawer(
animationController: animationController,
),
),
), ),
Transform.translate( ),
offset: Offset(
widget.maxSlide! * animationController!.value, 0),
child: Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(
-math.pi / 2 * animationController!.value),
alignment: Alignment.centerLeft,
child: _views[_drawerProviders.currentIndex]),
),
Positioned(
top: MediaQuery.of(context).padding.top,
left: MediaQuery.of(context).size.width * 0.03 +
animationController!.value * widget.maxSlide!,
child: InkWell(
onTap: () {
_drawerProviders.toggle(animationController);
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SvgPicture.asset(
'assets/menu.svg',
),
),
),
),
],
), ),
), ],
); );
}, }),
), ),
), ),
); );
} }
void _onDragStart(DragStartDetails details) {
bool isDragOpenFromLeft = animationController!.isDismissed;
bool isDragCloseFromRight = animationController!.isCompleted;
_canBeDragged = isDragOpenFromLeft || isDragCloseFromRight;
}
void _onDragUpdate(DragUpdateDetails details) {
if (_canBeDragged!) {
double delta = details.primaryDelta! / widget.maxSlide!;
animationController!.value += delta;
}
}
void _onDragEnd(DragEndDetails details) {
double _kMinFlingVelocity = 365.0;
if (animationController!.isDismissed || animationController!.isCompleted) {
return;
}
if (details.velocity.pixelsPerSecond.dx.abs() >= _kMinFlingVelocity) {
double visualVelocity = details.velocity.pixelsPerSecond.dx /
MediaQuery.of(context).size.width;
animationController!.fling(velocity: visualVelocity);
} else if (animationController!.value < 0.5) {
animationController!.reverse();
} else {
animationController!.forward();
}
}
Future<bool> _onWillPop() async { Future<bool> _onWillPop() async {
return (await showDialog( return (await showDialog(
context: context, context: context,

View File

@ -1,9 +1,33 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/constants.dart'; import 'package:simplex_chat/constants.dart';
class Invitations extends StatelessWidget { class Invitations extends StatefulWidget {
const Invitations({Key? key}) : super(key: key); const Invitations({Key key}) : super(key: key);
@override
State<Invitations> createState() => _InvitationsState();
}
class _InvitationsState extends State<Invitations> {
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();
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -13,12 +37,23 @@ class Invitations extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0), padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0),
child: Column( child: Column(
children: [ children: [
Align( Row(
alignment: Alignment.centerRight, mainAxisAlignment: MainAxisAlignment.end,
child: SvgPicture.asset( children: [
'assets/logo.svg', Column(
height: 40.0, 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), const SizedBox(height: 15.0),
Row( Row(

View File

@ -4,7 +4,7 @@ import 'package:simplex_chat/app_routes.dart';
import 'package:simplex_chat/constants.dart'; import 'package:simplex_chat/constants.dart';
class IntroView extends StatelessWidget { class IntroView extends StatelessWidget {
const IntroView({Key? key}) : super(key: key); const IntroView({Key key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -0,0 +1,318 @@
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/widgets/custom_text_field.dart';
class ProfileView extends StatefulWidget {
const ProfileView({Key key}) : super(key: key);
@override
_ProfileViewState createState() => _ProfileViewState();
}
class _ProfileViewState extends State<ProfileView> {
final _formKey = GlobalKey<FormState>();
// controllers
final TextEditingController _displayNameController = TextEditingController();
final TextEditingController _fullNameController = TextEditingController();
// Image Picker --> DP properties
final imgPicker = ImagePicker();
File image;
String _photo = '';
bool _uploading = false;
// image buttons options
final _dpBtnText = ['Remove', 'Gallery', 'Camera'];
final _dpBtnColors = [Colors.red, Colors.purple, Colors.green];
final _dpBtnIcons = [
Icons.delete,
Icons.photo_rounded,
Icons.camera_alt_rounded
];
String _displayName = '';
String _fullName = '';
void _getUserData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_fullName = prefs.getString('fullName');
_displayName = prefs.getString('displayName');
_photo = prefs.getString('photo$_displayName');
});
_displayNameController.text = _displayName;
if (_fullName != null) _fullNameController.text = _fullName;
}
@override
void initState() {
_getUserData();
super.initState();
}
@override
void dispose() {
_displayNameController.dispose();
_fullNameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 30),
Center(
child: SizedBox(
height: 180.0,
width: 180.0,
child: Stack(
children: [
_photo != ''
? CircleAvatar(
radius: 100.0,
backgroundImage: FileImage(File(_photo)),
)
: const CircleAvatar(
radius: 100.0,
backgroundImage: AssetImage('assets/dp.png'),
),
Positioned(
right: 0,
bottom: 0,
child: FloatingActionButton(
backgroundColor: kSecondaryColor,
elevation: 2.0,
mini: true,
onPressed: _updateProfilePic,
child: _uploading
? const SizedBox(
height: 18.0,
width: 18.0,
child: CircularProgressIndicator(
strokeWidth: 2.0,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white),
),
)
: const Icon(
Icons.add_a_photo,
size: 20,
),
),
)
],
),
)),
const SizedBox(height: 25.0),
const Text('Display Name', style: kSmallHeadingStyle),
const SizedBox(height: 10.0),
CustomTextField(
textEditingController: _displayNameController,
textInputType: TextInputType.name,
hintText: 'e.g John',
validatorFtn: (value) {
if (value.isEmpty) {
return 'Display name cannot be empty';
}
return null;
},
),
const SizedBox(height: 25.0),
const Text('Full Name', style: kSmallHeadingStyle),
const SizedBox(height: 10.0),
CustomTextField(
textEditingController: _fullNameController,
textInputType: TextInputType.name,
hintText: 'e.g John Doe',
),
const SizedBox(height: 25.0),
const Text(
'Your display name is what your contact will know you :)',
style: TextStyle(letterSpacing: 1.2),
)
],
),
),
),
),
),
),
floatingActionButton: Visibility(
visible: MediaQuery.of(context).viewInsets.bottom == 0,
child: FloatingActionButton(
heroTag: 'setup',
onPressed: () async {
if (_formKey.currentState.validate()) {
FocusScope.of(context).unfocus();
await _createProfile();
const snackBar = SnackBar(
backgroundColor: Colors.green,
content: Text('Profile updated!'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
},
child: const Icon(Icons.check),
),
),
);
}
// create profile and store in local
Future<void> _createProfile() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('displayName', _displayNameController.text.trim());
await prefs.setString('fullName', _fullNameController.text.trim());
await prefs.setString('photo${_displayNameController.text.trim()}', _photo);
debugPrint(prefs.getString('photo'));
}
void _updateProfilePic() {
showModalBottomSheet(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
topRight: Radius.circular(10.0),
),
),
context: context,
builder: (context) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(360.0)),
height: 7.0,
width: 50.0,
),
const SizedBox(height: 20.0),
const Align(
alignment: Alignment.centerLeft,
child: Text(
' Profile photo',
style: kHeadingStyle,
),
),
const SizedBox(height: 15.0),
Row(
children: List.generate(
3,
(index) => Column(
mainAxisSize: MainAxisSize.min,
children: [
MaterialButton(
color: _dpBtnColors.map((e) => e).elementAt(index),
shape: const CircleBorder(),
onPressed: index == 0
? () => _removePic()
: index == 1
? () => _galleryPic()
: () => _cameraPic(),
child: Icon(
_dpBtnIcons.map((e) => e).elementAt(index),
color: Colors.white,
),
),
Text(
_dpBtnText.map((e) => e).elementAt(index),
textAlign: TextAlign.center,
)
],
),
))
],
),
),
);
}
void _removePic() {
setState(() {
image = null;
_photo = '';
});
Navigator.pop(context);
}
void _cameraPic() async {
try {
setState(() {
_uploading = true;
});
// picking Image from Camera
final file = await imgPicker.getImage(
source: ImageSource.camera,
);
if (file != null) {
image = File(file.path);
setState(() {
_uploading = false;
_photo = file.path;
});
} else {
setState(() {
_uploading = false;
});
}
Navigator.pop(context);
} catch (e) {
rethrow;
}
}
void _galleryPic() async {
try {
debugPrint('gallery pic');
setState(() {
_uploading = true;
});
// picking Image from local storage
final file = await imgPicker.getImage(
source: ImageSource.gallery,
);
if (file != null) {
image = File(file.path);
setState(() {
_uploading = false;
_photo = file.path;
});
} else {
setState(() {
_uploading = false;
});
}
Navigator.pop(context);
} catch (e) {
rethrow;
}
}
}

View File

@ -4,7 +4,7 @@ import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/widgets/custom_btn.dart'; import 'package:simplex_chat/widgets/custom_btn.dart';
class ScanInvitationView extends StatelessWidget { class ScanInvitationView extends StatelessWidget {
const ScanInvitationView({Key? key}) : super(key: key); const ScanInvitationView({Key key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -8,7 +8,7 @@ import 'package:simplex_chat/views/home/home_view.dart';
import 'package:simplex_chat/widgets/custom_text_field.dart'; import 'package:simplex_chat/widgets/custom_text_field.dart';
class SetupProfileView extends StatefulWidget { class SetupProfileView extends StatefulWidget {
const SetupProfileView({Key? key}) : super(key: key); const SetupProfileView({Key key}) : super(key: key);
@override @override
_SetupProfileViewState createState() => _SetupProfileViewState(); _SetupProfileViewState createState() => _SetupProfileViewState();
@ -22,7 +22,7 @@ class _SetupProfileViewState extends State<SetupProfileView> {
// Image Picker --> DP properties // Image Picker --> DP properties
final imgPicker = ImagePicker(); final imgPicker = ImagePicker();
File? image; File image;
String photoUrl = ''; String photoUrl = '';
bool _uploading = false; bool _uploading = false;
bool _imageUploaded = false; bool _imageUploaded = false;
@ -63,7 +63,7 @@ class _SetupProfileViewState extends State<SetupProfileView> {
_imageUploaded _imageUploaded
? CircleAvatar( ? CircleAvatar(
radius: 100.0, radius: 100.0,
backgroundImage: FileImage(image!), backgroundImage: FileImage(image),
) )
: const CircleAvatar( : const CircleAvatar(
radius: 100.0, radius: 100.0,
@ -105,8 +105,8 @@ class _SetupProfileViewState extends State<SetupProfileView> {
textInputType: TextInputType.name, textInputType: TextInputType.name,
hintText: 'e.g John', hintText: 'e.g John',
validatorFtn: (value) { validatorFtn: (value) {
if (value!.isEmpty) { if (value.isEmpty) {
return 'Display name cannot be empty!'; return 'Display name cannot be empty';
} }
return null; return null;
}, },
@ -118,12 +118,6 @@ class _SetupProfileViewState extends State<SetupProfileView> {
textEditingController: _fullNameController, textEditingController: _fullNameController,
textInputType: TextInputType.name, textInputType: TextInputType.name,
hintText: 'e.g John Doe', hintText: 'e.g John Doe',
validatorFtn: (value) {
if (value!.isEmpty) {
return 'Full name cannot be empty!';
}
return null;
},
), ),
const SizedBox(height: 25.0), const SizedBox(height: 25.0),
const Text( const Text(
@ -142,7 +136,7 @@ class _SetupProfileViewState extends State<SetupProfileView> {
child: FloatingActionButton( child: FloatingActionButton(
heroTag: 'setup', heroTag: 'setup',
onPressed: () async { onPressed: () async {
if (_formKey.currentState!.validate()) { if (_formKey.currentState.validate()) {
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
await _createProfile(); await _createProfile();
@ -269,6 +263,7 @@ class _SetupProfileViewState extends State<SetupProfileView> {
void _galleryPic() async { void _galleryPic() async {
try { try {
debugPrint('gallery pic');
setState(() { setState(() {
_uploading = true; _uploading = true;
}); });

View File

@ -1,14 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:progress_indicators/progress_indicators.dart'; import 'package:progress_indicators/progress_indicators.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/animations/entrance_fader.dart'; import 'package:simplex_chat/animations/entrance_fader.dart';
import 'package:simplex_chat/app_routes.dart';
import 'package:simplex_chat/constants.dart'; import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/views/home/home_view.dart';
import 'package:simplex_chat/views/onBoarding/intro_view.dart';
class SplashScreen extends StatefulWidget { class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key); const SplashScreen({Key key}) : super(key: key);
@override @override
_SplashScreenState createState() => _SplashScreenState(); _SplashScreenState createState() => _SplashScreenState();
@ -16,19 +14,11 @@ class SplashScreen extends StatefulWidget {
class _SplashScreenState extends State<SplashScreen> { class _SplashScreenState extends State<SplashScreen> {
// logincheck // logincheck
void _loginCheck() async { void _loginCheck() {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? _name = prefs.getString('displayName');
Future.delayed(const Duration(seconds: 4), () { Future.delayed(const Duration(seconds: 4), () {
Navigator.push( Navigator.pushNamed(
context, context,
MaterialPageRoute( AppRoutes.intro,
builder: (_) => _name == null
? const IntroView()
: HomeView(
maxSlide: MediaQuery.of(context).size.width * 0.82,
),
),
); );
}); });
} }

View File

@ -7,13 +7,13 @@ class CustomButton extends StatelessWidget {
@required this.onPressed, @required this.onPressed,
@required this.color, @required this.color,
@required this.child, @required this.child,
Key? key}) Key key})
: super(key: key); : super(key: key);
final double? width; final double width;
final double? height; final double height;
final void Function()? onPressed; final void Function() onPressed;
final Widget? child; final Widget child;
final Color? color; final Color color;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -1,27 +1,27 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class CustomTextField extends StatefulWidget { class CustomTextField extends StatefulWidget {
final TextEditingController? textEditingController; final TextEditingController textEditingController;
final TextInputType? textInputType; final TextInputType textInputType;
final FocusNode? node; final FocusNode node;
final String? hintText; final String hintText;
final bool? isPassword; final bool isPassword;
final IconData? icon; final IconData icon;
final Color? iconColor; final Color iconColor;
final Color? passIconColor; final Color passIconColor;
final IconData? trailing; final IconData trailing;
final void Function()? trailingCallBack; final void Function() trailingCallBack;
final Function(String)? onChangeFtn; final Function(String) onChangeFtn;
final void Function()? onEditComplete; final void Function() onEditComplete;
final String? Function(String?)? validatorFtn; final String Function(String) validatorFtn;
final Function(String)? onFieldSubmit; final Function(String) onFieldSubmit;
final String? errorText; final String errorText;
const CustomTextField({ const CustomTextField({
Key? key, Key key,
@required this.textEditingController, @required this.textEditingController,
@required this.textInputType, @required this.textInputType,
this.trailing, this.trailing,

View File

@ -3,14 +3,14 @@ import 'package:simplex_chat/constants.dart';
class MessageBubble extends StatefulWidget { class MessageBubble extends StatefulWidget {
const MessageBubble({ const MessageBubble({
Key? key, Key key,
this.sender, this.sender,
this.text, this.text,
this.isUser, this.isUser,
}) : super(key: key); }) : super(key: key);
final String? sender; final String sender;
final String? text; final String text;
final bool? isUser; final bool isUser;
@override @override
_MessageBubbleState createState() => _MessageBubbleState(); _MessageBubbleState createState() => _MessageBubbleState();
@ -23,15 +23,15 @@ class _MessageBubbleState extends State<MessageBubble> {
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
child: Column( child: Column(
crossAxisAlignment: crossAxisAlignment:
widget.isUser! ? CrossAxisAlignment.end : CrossAxisAlignment.start, widget.isUser ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
widget.sender!, widget.sender,
style: const TextStyle(fontSize: 12, color: Colors.grey), style: const TextStyle(fontSize: 12, color: Colors.grey),
), ),
const SizedBox(height: 2.0), const SizedBox(height: 2.0),
Material( Material(
borderRadius: widget.isUser! borderRadius: widget.isUser
? const BorderRadius.only( ? const BorderRadius.only(
topLeft: Radius.circular(10), topLeft: Radius.circular(10),
bottomLeft: Radius.circular(10), bottomLeft: Radius.circular(10),
@ -41,11 +41,11 @@ class _MessageBubbleState extends State<MessageBubble> {
bottomLeft: Radius.circular(10), bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10)), bottomRight: Radius.circular(10)),
elevation: 1.0, elevation: 1.0,
color: widget.isUser! ? Colors.teal : kPrimaryColor, color: widget.isUser ? Colors.teal : kPrimaryColor,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15), padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
child: Text( child: Text(
widget.text!, widget.text,
style: const TextStyle(fontSize: 15, color: Colors.white), style: const TextStyle(fontSize: 15, color: Colors.white),
), ),
), ),

View File

@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
sdk: ">=2.12.0 <3.0.0" sdk: ">=2.10.0 <3.0.0"
# Dependencies specify other packages that your package needs in order to work. # Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions # To automatically upgrade your package dependencies to the latest versions