local storage + ui fixes

This commit is contained in:
Muhammad Hamza
2021-10-10 16:30:01 +05:00
parent 6704fc7cbb
commit 36c3acdf2c
8 changed files with 379 additions and 148 deletions
@@ -1,21 +1,21 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/animations/bottom_animation.dart';
import 'package:simplex_chat/app_routes.dart';
import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/model/contact.dart';
import 'package:simplex_chat/model/group.dart';
import 'package:simplex_chat/views/conversation/conversation_view.dart';
class HomeViewWidget extends StatefulWidget {
const HomeViewWidget({Key? key}) : super(key: key);
class ContactsView extends StatefulWidget {
const ContactsView({Key? key}) : super(key: key);
@override
_HomeViewWidgetState createState() => _HomeViewWidgetState();
_ContactsViewState createState() => _ContactsViewState();
}
class _HomeViewWidgetState extends State<HomeViewWidget> {
class _ContactsViewState extends State<ContactsView> {
bool? _eraseMedia = false;
List<Contact> _contactsList = []; // for storing contacts
@@ -50,8 +50,20 @@ class _HomeViewWidgetState extends State<HomeViewWidget> {
});
}
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();
_getContacts();
super.initState();
}
@@ -66,15 +78,23 @@ class _HomeViewWidgetState extends State<HomeViewWidget> {
child: Center(
child: Column(
children: [
Align(
alignment: Alignment.centerRight,
child: GestureDetector(
onTap: _addNewContacts,
child: SvgPicture.asset(
'assets/logo.svg',
height: 40.0,
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 == null
? const AssetImage('assets/dp.png') as ImageProvider
: FileImage(File(_photo!)),
)
],
),
const SizedBox(height: 15.0),
Row(
@@ -129,7 +149,7 @@ class _HomeViewWidgetState extends State<HomeViewWidget> {
context,
MaterialPageRoute(
builder: (_) => ConversationView(
contact: _contactsList[index],
name: _contactsList[index].name,
),
),
),
@@ -1,10 +1,9 @@
import 'package:flutter/material.dart';
import 'package:simplex_chat/model/contact.dart';
import 'package:simplex_chat/widgets/message_bubble.dart';
class ConversationView extends StatefulWidget {
final Contact? contact;
const ConversationView({Key? key, @required this.contact}) : super(key: key);
final String? name;
const ConversationView({Key? key, @required this.name}) : super(key: key);
@override
_ConversationViewState createState() => _ConversationViewState();
@@ -43,7 +42,7 @@ class _ConversationViewState extends State<ConversationView> {
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
appBar: AppBar(
title: Text('${widget.contact!.name}'),
title: Text('${widget.name}'),
),
body: Column(
children: [
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/model/contact.dart';
import 'package:simplex_chat/model/group.dart';
import 'package:simplex_chat/widgets/custom_text_field.dart';
@@ -15,11 +16,31 @@ class AddGroupView extends StatefulWidget {
}
class _AddGroupViewState extends State<AddGroupView> {
bool _addMember = false;
List<Contact> _contactsList = []; // for storing contacts
final List _members = [];
final _formKey = GlobalKey<FormState>();
final _displayNameController = TextEditingController();
final _descController = TextEditingController();
// getting data from local storage FOR NOW!!
void _getContacts() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final String? _contacts = prefs.getString('contacts');
setState(() {
_contactsList = List.from(Contact.decode(_contacts));
});
}
@override
void initState() {
_getContacts();
super.initState();
}
@override
void dispose() {
_displayNameController.dispose();
@@ -73,18 +94,77 @@ class _AddGroupViewState extends State<AddGroupView> {
hintText: 'e.g Friends from UK',
),
const SizedBox(height: 10.0),
_members.isNotEmpty
? const Text('Members Added')
: Container(),
SizedBox(height: _members.isNotEmpty ? 10.0 : 0.0),
_members.isNotEmpty
? SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(
_members.length,
(index) => Padding(
padding:
const EdgeInsets.symmetric(horizontal: 5.0),
child: InkWell(
onTap: () {
setState(() {
_members.remove(_members[index]);
});
},
child: Container(
padding: const EdgeInsets.all(7.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: Colors.grey.withAlpha(100)),
child: Text(_members[index]),
),
),
),
)),
)
: Container(),
SizedBox(height: _members.isNotEmpty ? 10.0 : 0.0),
ListTile(
leading: const Icon(Icons.person_add),
title: const Text('Add a member'),
onTap: () {},
onTap: () {
setState(() {
_addMember = !_addMember;
});
},
),
SizedBox(height: _addMember ? 10.0 : 0.0),
_addMember ? const Text('Contacts Available') : Container(),
SizedBox(height: _addMember ? 10.0 : 0.0),
_addMember
? ListView(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: List.generate(
_contactsList.length,
(index) => ListTile(
leading: const CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'),
),
title: Text(_contactsList[index].name!),
onTap: () {
setState(() {
_members.add(_contactsList[index].name!);
});
},
),
),
)
: Container(),
const Divider(height: 30.0),
const ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'),
),
title: Text('You'),
trailing: Text(
subtitle: Text(
'Owner',
style: TextStyle(color: Colors.grey, fontSize: 12.0),
)),
@@ -123,7 +203,7 @@ class _AddGroupViewState extends State<AddGroupView> {
Group(
groupName: name,
groupDescription: desc,
members: <String>[],
members: _members,
),
];
_groups = _localList + _groups;
@@ -121,6 +121,13 @@ class _GroupViewState extends State<GroupView> {
style: const TextStyle(
fontSize: 11, color: Colors.grey),
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ConversationView(
name: _groupList[index].groupName),
),
),
onLongPress: () =>
_conversationOptions(_groupList[index]),
),
@@ -1,6 +1,8 @@
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';
import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/providers/drawer_providers.dart';
@@ -123,7 +125,7 @@ class MyDrawer extends StatelessWidget {
leading: const Icon(Icons.exit_to_app_rounded),
title: const Text('Logout'),
subtitle: const Text('Good bye! See you soon :)'),
onTap: () => Navigator.pop(context),
onTap: () => _logout(context),
),
],
),
@@ -131,4 +133,18 @@ class MyDrawer extends StatelessWidget {
),
);
}
void _logout(BuildContext context) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await Navigator.pushNamedAndRemoveUntil(
context,
AppRoutes.setupProfile,
(route) => route.settings.name == AppRoutes.setupProfile ? true : false,
);
String? _name = prefs.getString('displayName');
await prefs.remove('displayName');
await prefs.remove('fullName');
await prefs.remove('photo$_name');
}
}
@@ -1,12 +1,13 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
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/home/drawer.dart';
import 'package:simplex_chat/views/home/home_view_widget.dart';
import 'package:simplex_chat/views/invitations/invitation_view.dart';
class HomeView extends StatefulWidget {
@@ -26,7 +27,7 @@ class _HomeViewState extends State<HomeView> with TickerProviderStateMixin {
// views
final List<Widget> _views = [
const HomeViewWidget(),
const ContactsView(),
const Invitations(),
const GroupView(),
];
@@ -34,6 +35,7 @@ class _HomeViewState extends State<HomeView> with TickerProviderStateMixin {
@override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 250));
}
@@ -41,64 +43,69 @@ class _HomeViewState extends State<HomeView> with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
final _drawerProviders = Provider.of<DrawerProvider>(context);
return GestureDetector(
onHorizontalDragStart: _onDragStart,
onHorizontalDragUpdate: _onDragUpdate,
onHorizontalDragEnd: _onDragEnd,
behavior: HitTestBehavior.translucent,
child: AnimatedBuilder(
animation: animationController!,
builder: (context, _) {
return Material(
color: Colors.white70,
child: SafeArea(
child: Stack(
children: [
Transform.translate(
offset: Offset(
widget.maxSlide! * (animationController!.value - 1), 0),
child: Transform(
transform: Matrix4.identity()
..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(
return WillPopScope(
onWillPop: _onWillPop,
child: GestureDetector(
onHorizontalDragStart: _onDragStart,
onHorizontalDragUpdate: _onDragUpdate,
onHorizontalDragEnd: _onDragEnd,
behavior: HitTestBehavior.translucent,
child: AnimatedBuilder(
animation: animationController!,
builder: (context, _) {
return Material(
color: Colors.white70,
child: SafeArea(
child: Stack(
children: [
Transform.translate(
offset: Offset(
widget.maxSlide! * (animationController!.value - 1),
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',
..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',
),
),
),
),
],
),
),
),
);
},
);
},
),
),
);
}
@@ -133,4 +140,30 @@ class _HomeViewState extends State<HomeView> with TickerProviderStateMixin {
animationController!.forward();
}
}
Future<bool> _onWillPop() async {
return (await showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Exit Application', style: kMediumHeadingStyle),
content: const Text('Are You Sure?'),
actions: <Widget>[
TextButton(
child: const Text(
'Yes',
style: TextStyle(
color: Colors.red,
),
),
onPressed: () => SystemNavigator.pop(),
),
TextButton(
child: const Text('No'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
)) ??
false;
}
}
@@ -2,6 +2,7 @@ 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/views/home/home_view.dart';
import 'package:simplex_chat/widgets/custom_text_field.dart';
@@ -19,6 +20,18 @@ class _SetupProfileViewState extends State<SetupProfileView> {
final TextEditingController _displayNameController = TextEditingController();
final TextEditingController _fullNameController = TextEditingController();
// Image Picker --> DP properties
final imgPicker = ImagePicker();
File? image;
String photoUrl = '';
bool _uploading = false;
bool _imageUploaded = false;
// image buttons options
final _dpBtnText = ['Gallery', 'Camera'];
final _dpBtnColors = [Colors.purple, Colors.green];
final _dpBtnIcons = [Icons.photo_rounded, Icons.camera_alt_rounded];
@override
void dispose() {
_displayNameController.dispose();
@@ -40,13 +53,50 @@ class _SetupProfileViewState extends State<SetupProfileView> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Align(
alignment: Alignment.centerLeft,
child: BackButton(
onPressed: () => Navigator.pop(context),
const SizedBox(height: 30),
Center(
child: 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,
),
),
)
],
),
),
const Center(child: UserProfilePic()),
)),
const SizedBox(height: 25.0),
const Text('Display Name', style: kSmallHeadingStyle),
const SizedBox(height: 10.0),
@@ -91,10 +141,13 @@ class _SetupProfileViewState extends State<SetupProfileView> {
visible: MediaQuery.of(context).viewInsets.bottom == 0,
child: FloatingActionButton(
heroTag: 'setup',
onPressed: () {
onPressed: () async {
if (_formKey.currentState!.validate()) {
FocusScope.of(context).unfocus();
Navigator.push(
await _createProfile();
await Navigator.push(
context,
MaterialPageRoute(
builder: (_) => HomeView(
@@ -102,6 +155,10 @@ class _SetupProfileViewState extends State<SetupProfileView> {
),
),
);
_displayNameController.clear();
_fullNameController.clear();
image = null;
}
},
child: const Icon(Icons.check),
@@ -109,70 +166,16 @@ class _SetupProfileViewState extends State<SetupProfileView> {
),
);
}
}
class UserProfilePic extends StatefulWidget {
const UserProfilePic({Key? key}) : super(key: key);
// 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()}', photoUrl);
@override
_UserProfilePicState createState() => _UserProfilePicState();
}
class _UserProfilePicState extends State<UserProfilePic> {
// Image Picker --> DP properties
final imgPicker = ImagePicker();
File? image;
String photoUrl = '';
bool _uploading = false;
bool _imageUploaded = false;
// image buttons options
final _dpBtnText = ['Gallery', 'Camera'];
final _dpBtnColors = [Colors.purple, Colors.green];
final _dpBtnIcons = [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,
),
),
)
],
),
);
debugPrint(prefs.getString('photo'));
}
void _updateProfilePic() {
@@ -250,6 +253,7 @@ class _UserProfilePicState extends State<UserProfilePic> {
setState(() {
_uploading = false;
_imageUploaded = true;
photoUrl = file.path;
});
} else {
setState(() {
@@ -279,6 +283,7 @@ class _UserProfilePicState extends State<UserProfilePic> {
setState(() {
_uploading = false;
_imageUploaded = true;
photoUrl = file.path;
});
} else {
setState(() {
@@ -0,0 +1,71 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.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/constants.dart';
import 'package:simplex_chat/views/home/home_view.dart';
import 'package:simplex_chat/views/onBoarding/intro_view.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
@override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
// logincheck
void _loginCheck() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? _name = prefs.getString('displayName');
Future.delayed(const Duration(seconds: 4), () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => _name == null
? const IntroView()
: HomeView(
maxSlide: MediaQuery.of(context).size.width * 0.82,
),
),
);
});
}
@override
void initState() {
_loginCheck();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
EntranceFader(
duration: const Duration(seconds: 1),
offset: const Offset(0, 20),
child: SvgPicture.asset(
'assets/logo.svg',
height: 70,
),
),
EntranceFader(
offset: const Offset(0, 00),
duration: const Duration(seconds: 1),
delay: const Duration(seconds: 1),
child: JumpingDotsProgressIndicator(
fontSize: 40,
color: kPrimaryColor,
),
),
],
),
),
);
}
}