diff --git a/android/app/build.gradle b/android/app/build.gradle index 14f23ee..f5a84cd 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -22,11 +22,12 @@ if (flutterVersionName == null) { } apply plugin: 'com.android.application' +apply plugin: 'com.google.gms.google-services' apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion flutter.compileSdkVersion + compileSdkVersion 33 ndkVersion flutter.ndkVersion compileOptions { @@ -39,10 +40,11 @@ android { applicationId "com.example.connector" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. - minSdkVersion flutter.minSdkVersion + minSdkVersion 23 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName + multiDexEnabled true } buildTypes { @@ -57,3 +59,9 @@ android { flutter { source '../..' } + +dependencies { + // Import the Firebase BoM + implementation platform('com.google.firebase:firebase-bom:31.2.3') + +} diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..a596b3d --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,47 @@ +{ + "project_info": { + "project_number": "161225848235", + "project_id": "connector-71f47", + "storage_bucket": "connector-71f47.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:161225848235:android:c8d4df7ae7c1e12e748eee", + "android_client_info": { + "package_name": "com.example.connector" + } + }, + "oauth_client": [ + { + "client_id": "161225848235-blfn2vatpgbjdi5q8rorft3cmgnobmg6.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.example.connector", + "certificate_hash": "a2267a3dcdf7bebeb0d5ffb54dea2e640e680994" + } + }, + { + "client_id": "161225848235-aq3s7thct6vohut7g1kvdb9s3t7u4k9t.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyB5SWT-ufPTXgxFQAIp7F0ndIPqMXHLB2E" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "161225848235-aq3s7thct6vohut7g1kvdb9s3t7u4k9t.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 954f04a..ddcee01 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,8 @@ + + + CircularProgressIndicator(), + errorWidget: (context,url,error)=>Icon(Icons.error,), + height: 40, + ), + ), + SizedBox(width: 5,), + Text(friendName,style: TextStyle(fontSize: 20),) + ], + ), + ), + + body: Column( + children: [ + Expanded(child: Container( + padding: EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(25), + topRight: Radius.circular(25) + ) + ), + child: StreamBuilder( + stream: FirebaseFirestore.instance.collection("users").doc(currentUserId).collection('messages').doc(friendId).collection('chats').orderBy("date",descending: true).snapshots(), + builder: (context,AsyncSnapshot snapshot){ + if(snapshot.hasData){ + if(snapshot.data.docs.length < 1){ + return Center( + child: Text("Say Hi"), + ); + } + return ListView.builder( + itemCount: snapshot.data.docs.length, + reverse: true, + physics: BouncingScrollPhysics(), + itemBuilder: (context,index){ + bool isMe = snapshot.data.docs[index]['senderId'] == currentUserId; + return SingleMessage(message: snapshot.data.docs[index]['message'], isMe: isMe); + }); + } + return Center( + child: CircularProgressIndicator() + ); + }), + )), + MessageTextField(currentUserId, friendId), + ], + ), + + ); + } +} \ No newline at end of file diff --git a/lib/Pages/home_page.dart b/lib/Pages/home_page.dart new file mode 100644 index 0000000..a2802c6 --- /dev/null +++ b/lib/Pages/home_page.dart @@ -0,0 +1,85 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:connector/Pages/chat_page.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; + + +class HomePage extends StatefulWidget { + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.grey[300], + body: StreamBuilder( + stream: FirebaseFirestore.instance.collection('users').doc('${FirebaseAuth.instance.currentUser?.uid}').collection('messages').snapshots(), + builder: (context,AsyncSnapshot snapshot){ + print("Hello world"); + if(snapshot.hasData){ + if(snapshot.data.docs.length < 1){ + return Center( + child: Text("No Chats Available !"), + ); + } + print("Hello world"); + return ListView.builder( + itemCount: snapshot.data.docs.length, + itemBuilder: (context,index){ + print("Hello Worlds"); + var friendId = snapshot.data.docs[index].id; + var lastMsg = snapshot.data.docs[index]['last_msg']; + return FutureBuilder( + future: FirebaseFirestore.instance.collection('users').doc(friendId).get(), + builder: (context,AsyncSnapshot asyncSnapshot){ + print("Hello Worlds"); + if(asyncSnapshot.hasData){ + print("He;;p world"); + var friend = asyncSnapshot.data.data(); + print("Help world"); + return Card( + child: ListTile( + leading: ClipRRect( + borderRadius: BorderRadius.circular(80), + child: CachedNetworkImage( + imageUrl:friend['photourl']??'', + placeholder: (conteext,url)=>CircularProgressIndicator(), + errorWidget: (context,url,error)=>Icon(Icons.error,), + height: 50, + ), + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + selectedTileColor: Colors.black87, + title: Text(friend['username'],style: TextStyle(fontWeight: FontWeight.bold)), + subtitle: Container( + child: Text("$lastMsg",style: TextStyle(color: Colors.grey),overflow: TextOverflow.ellipsis,), + ), + onTap: (){ + Navigator.push(context, MaterialPageRoute(builder: (context)=>ChatPage( + currentUserId: '${FirebaseAuth.instance.currentUser?.uid}'??'', + friendId: friend['uid']??'', + friendName: friend['username']??'', + friendImage: friend['photourl']??''))); + }, + ), + ); + } + return LinearProgressIndicator(valueColor: AlwaysStoppedAnimation(Colors.black)); + }, + + ); + }); + } + return Center(child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Colors.black)),); + }), + + + + ); + } +} \ No newline at end of file diff --git a/lib/Pages/profile_page.dart b/lib/Pages/profile_page.dart new file mode 100644 index 0000000..b31cebb --- /dev/null +++ b/lib/Pages/profile_page.dart @@ -0,0 +1,92 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:connector/Screens/my_text_field.dart'; +import 'package:connector/Screens/sign_out_button.dart'; +import 'package:connector/Widgets/update_profile_button.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/material.dart'; + +import '../Screens/splash_screen.dart'; +import '../Screens/update_profile_screen.dart'; +import '../Widgets/save_button.dart'; + +class ProfilePage extends StatefulWidget { + @override + State createState() => _ProfilePageState(); +} + +class _ProfilePageState extends State { + User? user = FirebaseAuth.instance.currentUser; + + @override + Widget build(BuildContext context) { + final usernameController = TextEditingController(); + final emailController = TextEditingController(); + final passwordController1 = TextEditingController(); + final passwordController2 = TextEditingController(); + final passwordController3 = TextEditingController(); + return Scaffold( + backgroundColor: Colors.grey[300], + body:SafeArea( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + + children: [ + + //const SizedBox(height: 60), + CachedNetworkImage( + imageUrl:'${user?.photoURL}'??'', + placeholder: (conteext,url)=>CircularProgressIndicator(), + errorWidget: (context,url,error)=>Icon(Icons.error,), + imageBuilder: (context, imageProvider) => Container( + width: 130.0, + height: 130.0, + decoration: BoxDecoration( + shape: BoxShape.circle, + image: DecorationImage( + image: imageProvider, fit: BoxFit.cover), + ), + ), + ), + const SizedBox(height: 20), + Text('${user?.displayName}',style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + )), + const SizedBox(height: 30), + UpdateProfileButton(onTap: updateProfile), + const SizedBox(height: 20), + SignOutButton(onTap: signUserOut) + ], + ), + ), + ) + ); + } + + signUserOut() async{ + // Initialize Firebase Auth + final FirebaseAuth _auth = FirebaseAuth.instance; + + // Sign out the user + await _auth.signOut(); + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (BuildContext context) =>SplashScreen(), + ), + ); + } + + + + updateProfile() { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (BuildContext context) =>UpdateProfileScreen(), + ), + ); + } +} diff --git a/lib/Screens/home_screen.dart b/lib/Screens/home_screen.dart new file mode 100644 index 0000000..c1467dd --- /dev/null +++ b/lib/Screens/home_screen.dart @@ -0,0 +1,101 @@ +import 'package:connector/Screens/search_screen.dart'; +import 'package:flutter/material.dart'; + +import '../Pages/home_page.dart'; +import '../Pages/profile_page.dart'; + + + +class HomeScreen extends StatefulWidget { + @override + _HomeScreenState createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State { + int _currentIndex = 0; + bool is_message_page=true; + + final List _pages = [ PageOne(), PageTwo(), PageThree(), ]; + + @override + Widget build(BuildContext context) { + + return Scaffold( + backgroundColor: Colors.grey[300], + appBar: AppBar( + backgroundColor: Colors.blue, + title: Text('Connector'), + ), + body: _pages[_currentIndex], + bottomNavigationBar: BottomNavigationBar( + currentIndex: _currentIndex, + selectedItemColor: Colors.black87, + onTap: (int index) { + setState(() { + _currentIndex = index; + if(_currentIndex!=0) is_message_page=false; + else is_message_page=true; + }); + }, + items: [ + BottomNavigationBarItem( + icon: Icon(Icons.message), + label: 'Messsges', + ), + BottomNavigationBarItem( + icon: Icon(Icons.dashboard), + label: 'Stories', + ), + BottomNavigationBarItem( + icon: Icon(Icons.person), + label: 'Profile', + ), + ], + ), + floatingActionButton:Visibility( + visible: is_message_page, + child: FloatingActionButton( + onPressed: () { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (BuildContext context) =>SearchScreen(), + ), + ); + }, + child: Icon(Icons.search), + backgroundColor: Colors.blue, + ), + + ) + //floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, + ); + } +} + +class PageOne extends StatelessWidget { + @override + Widget build(BuildContext context) { + return HomePage(); + } +} + +class PageTwo extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Center( + child: Text('No stories available',style: TextStyle( + fontSize: 15, + )), + ); + } +} + +class PageThree extends StatelessWidget { + @override + Widget build(BuildContext context) { + return ProfilePage(); + } +} + + diff --git a/lib/Screens/search_screen.dart b/lib/Screens/search_screen.dart new file mode 100644 index 0000000..c6aa0bb --- /dev/null +++ b/lib/Screens/search_screen.dart @@ -0,0 +1,138 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; + +import '../Pages/chat_page.dart'; +import 'home_screen.dart'; + + +class SearchScreen extends StatefulWidget { + + @override + _SearchScreenState createState() => _SearchScreenState(); +} + +class _SearchScreenState extends State { + TextEditingController searchController = TextEditingController(); + List searchResult =[]; + bool isLoading = false; + bool userFound=false; + + void onSearch()async{ + setState(() { + searchResult = []; + isLoading = true; + }); + final QuerySnapshot> result = await FirebaseFirestore.instance + .collection('users') + .where('username', isEqualTo: searchController.text) + .limit(1) + .get(); + + if (result.docs.length == 1 ) { + final DocumentSnapshot> userDoc = result.docs.first; + final userData = userDoc.data(); + userFound=true; + searchResult=[]; + searchResult.add(userData!); + print(searchResult); + // print other user data as needed + } else { + userFound=false; + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("No User Found"))); + print('User not found!'); + } + + setState(() { + isLoading = false; + }); + } + + + + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: BackButton( + onPressed: (){ + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (BuildContext context) =>HomeScreen(), + ), + ); + }, + ), + backgroundColor: Colors.blue, + title: Text("Search your Friend"), + ), + body: Column( + children: [ + Row( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.all(15.0), + child: TextField( + controller: searchController, + decoration: InputDecoration( + hintText: "type username....", + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Colors.white), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.grey.shade400), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + ), + fillColor: Colors.grey.shade200, + filled: true, + ), + ), + ), + ), + IconButton(onPressed: (){ + onSearch(); + }, icon: Icon(Icons.search)), + const SizedBox(width: 10), + ], + + ), + if(searchResult.length > 0) + Expanded(child: ListView.builder( + itemCount: searchResult.length, + shrinkWrap: true, + itemBuilder: (context,index){ + return ListTile( + leading: CachedNetworkImage( + imageUrl:'${FirebaseAuth.instance.currentUser?.photoURL}', + placeholder: (conteext,url)=>CircularProgressIndicator(), + errorWidget: (context,url,error)=>Icon(Icons.error,), + height: 40, + ), + title: Text(searchResult[index]['username']), + subtitle: Text(searchResult[index]['email']), + trailing: IconButton(onPressed: (){ + setState(() { + searchController.text = ""; + }); + Navigator.push(context, MaterialPageRoute(builder: (context)=>ChatPage( + currentUserId: '${FirebaseAuth.instance.currentUser?.uid}'??'', + friendId: searchResult[index]['uid']??'', + friendName: searchResult[index]['username']??'', + friendImage: searchResult[index]['photurl']??''))); + }, icon: Icon(Icons.message)), + ); + })) + else if(isLoading == true) + Center(child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Colors.black))) + ], + ), + + ); + } +} \ No newline at end of file diff --git a/lib/Screens/sign_in_screen.dart b/lib/Screens/sign_in_screen.dart index 825567e..a6a9d00 100644 --- a/lib/Screens/sign_in_screen.dart +++ b/lib/Screens/sign_in_screen.dart @@ -1,18 +1,48 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:connector/Screens/sign_in_button.dart'; import 'package:connector/Screens/my_text_field.dart'; import 'package:connector/Screens/sign_up_screen.dart'; +import 'home_screen.dart'; -class SignInScreen extends StatelessWidget { + +class SignInScreen extends StatefulWidget { SignInScreen({super.key}); - final usernameController = TextEditingController(); + @override + State createState() => _SignInScreenState(); +} + +class _SignInScreenState extends State { + final emailController = TextEditingController(); + final passwordController = TextEditingController(); - void signUserIn() {} + + + void signUserIn() async{ + User? user=FirebaseAuth.instance.currentUser; + await FirebaseAuth.instance.signInWithEmailAndPassword(email: emailController.text.trim(), + password: passwordController.text.trim()); + FirebaseAuth.instance + .authStateChanges() + .listen((User? user) async{ + if (user == null) { + print('User is currently signed out!'); + } else { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (BuildContext context) =>HomeScreen(), + ), + ); + } + }); + } @override Widget build(BuildContext context) { @@ -36,7 +66,7 @@ class SignInScreen extends StatelessWidget { MyTextField( - controller: usernameController, + controller: emailController, hintText: 'Username or Email', obscureText: false, ), diff --git a/lib/Screens/sign_out_button.dart b/lib/Screens/sign_out_button.dart new file mode 100644 index 0000000..accc20f --- /dev/null +++ b/lib/Screens/sign_out_button.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; + +class SignOutButton extends StatelessWidget { + final Function()? onTap; + + const SignOutButton({super.key, required this.onTap}); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.all(25), + margin: const EdgeInsets.symmetric(horizontal: 25), + decoration: BoxDecoration( + color: Colors.black87, + borderRadius: BorderRadius.circular(8), + ), + child: const Center( + child: Text( + "Sign Out", + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ), + ), + ); + } +} diff --git a/lib/Screens/sign_up_screen.dart b/lib/Screens/sign_up_screen.dart index 749998b..ee39ce6 100644 --- a/lib/Screens/sign_up_screen.dart +++ b/lib/Screens/sign_up_screen.dart @@ -1,21 +1,63 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:connector/Screens/sign_in_screen.dart'; +import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:connector/Screens/sign_in_button.dart'; import 'package:connector/Screens/my_text_field.dart'; -import 'package:connector/Screens/sign_in_screen.dart'; +import 'package:connector/Screens/sign_in_screen.dart' as globe; import 'package:connector/Screens/sign_up_button.dart'; -class SignUpScreen extends StatelessWidget { +import 'home_screen.dart'; + + +class SignUpScreen extends StatefulWidget { SignUpScreen({super.key}); + @override + State createState() => _SignUpScreenState(); +} +class _SignUpScreenState extends State { final usernameController = TextEditingController(); + final emailController=TextEditingController(); + final passwordController = TextEditingController(); - final confirmPasswordController=TextEditingController(); + final confirmPasswordController=TextEditingController(); - void signUserIn() {} + Future signUserUp() async { + UserCredential userCredential=await FirebaseAuth.instance.createUserWithEmailAndPassword(email: emailController.text.trim(), + password: passwordController.text.trim()); + + User? user =FirebaseAuth.instance.currentUser; + user?.updateDisplayName(usernameController.text.trim()); + user?.updatePhotoURL('https://firebasestorage.googleapis.com/v0/b/connector-71f47.appspot.com/o/contact-dummy_landscape_964x656.jpg?alt=media&token=0559da6a-1aea-4858-a8a1-281f4b286da6'); + await FirebaseFirestore.instance.collection('users').doc(userCredential.user?.uid).set( + { + 'uid':userCredential.user?.uid, + 'email':emailController.text, + 'username':usernameController.text, + 'photourl':'https://firebasestorage.googleapis.com/v0/b/connector-71f47.appspot.com/o/contact-dummy_landscape_964x656.jpg?alt=media&token=0559da6a-1aea-4858-a8a1-281f4b286da6', + }).then((value) => print("User data saved")) + .catchError((error) => print("Failed to save user data: $error")); + + FirebaseAuth.instance + .authStateChanges() + .listen((User? user) async{ + if (user == null) { + print('User is currently signed out!'); + } else { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (BuildContext context) =>HomeScreen(), + ), + ); + } + }); + } @override Widget build(BuildContext context) { @@ -87,7 +129,7 @@ class SignUpScreen extends StatelessWidget { SignUpButton( - onTap: signUserIn, + onTap: signUserUp, ), const SizedBox(height: 30), diff --git a/lib/Screens/splash_screen.dart b/lib/Screens/splash_screen.dart index f4efc53..75c908b 100644 --- a/lib/Screens/splash_screen.dart +++ b/lib/Screens/splash_screen.dart @@ -1,8 +1,12 @@ import 'dart:async'; +import 'package:connector/Screens/sign_in_screen.dart'; +import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; -import 'package:connector/Screens/sign_in_screen.dart'; +import 'package:connector/Screens/sign_in_screen.dart' as glob; + +import 'home_screen.dart'; class SplashScreen extends StatefulWidget{ @override @@ -14,13 +18,24 @@ class _SplashScreenState extends State { @override void initState(){ super.initState(); + Timer(Duration(seconds: 3),(){ - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (BuildContext context) =>SignInScreen(), - ), - ); + User? user = FirebaseAuth.instance.currentUser; + if(user != null){ + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (BuildContext context) =>HomeScreen(), + ), + ); + }else{ + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (BuildContext context) =>SignInScreen(), + ), + ); + } }); } diff --git a/lib/Screens/update_profile_screen.dart b/lib/Screens/update_profile_screen.dart new file mode 100644 index 0000000..a94eb3a --- /dev/null +++ b/lib/Screens/update_profile_screen.dart @@ -0,0 +1,130 @@ + + +import 'dart:io'; + +import 'package:connector/Widgets/save_button.dart'; +import 'package:connector/Widgets/update_profile_button.dart'; +import 'package:connector/Widgets/upload_profile_picture_button.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:firebase_storage/firebase_storage.dart'; +import 'package:permission_handler/permission_handler.dart'; + +import 'home_screen.dart'; +import 'my_text_field.dart'; + + +class UpdateProfileScreen extends StatelessWidget { + final usernameController = TextEditingController(); + final emailController = TextEditingController(); + final passwordController = TextEditingController(); + final passwordController2 = TextEditingController(); + final passwordController3 = TextEditingController(); + + @override + Widget build(BuildContext context) { + saveUserDetails() { + User? user = FirebaseAuth.instance.currentUser; + if (usernameController.text.toString() != null) { + user?.updateDisplayName(usernameController.text.toString()); + } + if (emailController.text.toString() != null) { + user?.updateEmail(emailController.text.toString()); + } + if (passwordController.text.toString() != null) { + user?.updatePassword(passwordController.text.toString()); + } + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("User profile updated"))); + } + return Scaffold( + resizeToAvoidBottomInset: false, + appBar: AppBar( + leading: BackButton( + onPressed: () { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (BuildContext context) => HomeScreen(), + ), + ); + }, + ), + title: Text("Update Profile"), + backgroundColor: Colors.blue, + + ), + body: SafeArea( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + MyTextField( + controller: usernameController, + hintText: 'Update username', + obscureText: false, + ), + const SizedBox(height: 10), + MyTextField( + controller: emailController, + hintText: 'Change email', + obscureText: false, + ), + const SizedBox(height: 10), + MyTextField( + controller: passwordController, + hintText: 'Enter current password', + obscureText: true, + ), + const SizedBox(height: 10), + MyTextField( + controller: passwordController2, + hintText: 'Enter new password', + obscureText: true, + ), + const SizedBox(height: 10), + MyTextField( + controller: passwordController3, + hintText: 'Confirm new password', + obscureText: true, + ), + const SizedBox(height: 20), + UploadProfilePictureButton(onTap: uploadProfilePic), + const SizedBox(height: 20), + SaveButton(onTap: saveUserDetails), + + ], + ), + ), + ) + ); + } + + + uploadProfilePic() async { + final _firebaseStorage = FirebaseStorage.instance; + final _imagePicker = ImagePicker(); + PickedFile? image; + User? user = FirebaseAuth.instance.currentUser; + //Check Permissions + var check=await Permission.photos.request(); + print(check); + + var permissionStatus = await Permission.photos.status; + + if (permissionStatus.isGranted) { + //Select Image + image = await _imagePicker.getImage(source: ImageSource.gallery); + var file = File(image!.path); + + if (image != null) { + //Upload to Firebase + var snapshot = await _firebaseStorage.ref() + .child('images/imageName') + .putFile(file); + String downloadUrl = await snapshot.ref.getDownloadURL(); + user?.updatePhotoURL(downloadUrl); + } + } + } +} \ No newline at end of file diff --git a/lib/Widgets/message_text_field.dart b/lib/Widgets/message_text_field.dart new file mode 100644 index 0000000..4f00e1b --- /dev/null +++ b/lib/Widgets/message_text_field.dart @@ -0,0 +1,81 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/material.dart'; + + +class MessageTextField extends StatefulWidget { + final String currentId; + final String friendId; + + MessageTextField(this.currentId,this.friendId); + + @override + _MessageTextFieldState createState() => _MessageTextFieldState(); +} + +class _MessageTextFieldState extends State { + TextEditingController _controller = TextEditingController(); + @override + Widget build(BuildContext context) { + return Container( + color: Colors.white, + padding: EdgeInsetsDirectional.all(8), + child: Row( + children: [ + Expanded(child: TextField( + controller: _controller, + decoration: InputDecoration( + labelText:"Type your Message", + fillColor: Colors.grey[100], + filled: true, + border: OutlineInputBorder( + borderSide: BorderSide(width: 0), + gapPadding: 10, + borderRadius: BorderRadius.circular(25) + ) + ), + )), + SizedBox(width: 20,), + GestureDetector( + onTap: ()async{ + String message = _controller.text; + _controller.clear(); + await FirebaseFirestore.instance.collection('users').doc(widget.currentId).collection('messages').doc(widget.friendId).collection('chats').add({ + "senderId":widget.currentId, + "receiverId":widget.friendId, + "message":message, + "type":"text", + "date":DateTime.now(), + }).then((value) { + FirebaseFirestore.instance.collection('users').doc(widget.currentId).collection('messages').doc(widget.friendId).set({ + 'last_msg':message, + }); + }); + + await FirebaseFirestore.instance.collection('users').doc(widget.friendId).collection('messages').doc(widget.currentId).collection("chats").add({ + "senderId":widget.currentId, + "receiverId":widget.friendId, + "message":message, + "type":"text", + "date":DateTime.now(), + + }).then((value){ + FirebaseFirestore.instance.collection('users').doc(widget.friendId).collection('messages').doc(widget.currentId).set({ + "last_msg":message + }); + }); + }, + child: Container( + padding: EdgeInsets.all(8), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.blue, + ), + child: Icon(Icons.send,color: Colors.white,), + ), + ) + ], + ), + + ); + } +} \ No newline at end of file diff --git a/lib/Widgets/my_search_textfield.dart b/lib/Widgets/my_search_textfield.dart new file mode 100644 index 0000000..7832a63 --- /dev/null +++ b/lib/Widgets/my_search_textfield.dart @@ -0,0 +1,39 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class MySearchTextField extends StatelessWidget { + final controller; + final String hintText; + final bool obscureText; + + const MySearchTextField({ + super.key, + required this.controller, + required this.hintText, + required this.obscureText, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 25.0), + child: TextField( + controller: controller, + obscureText: obscureText, + decoration: InputDecoration( + enabledBorder: const OutlineInputBorder( + //borderRadius:BorderRadius.all(2.0), + borderSide: BorderSide(color: Colors.white), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: BorderSide(color: Colors.grey.shade400), + ), + fillColor: Colors.grey.shade200, + filled: true, + hintText: hintText, + hintStyle: TextStyle(color: Colors.grey[500])), + ), + ); + } +} diff --git a/lib/Widgets/save_button.dart b/lib/Widgets/save_button.dart new file mode 100644 index 0000000..90302d9 --- /dev/null +++ b/lib/Widgets/save_button.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; + +class SaveButton extends StatelessWidget { + final Function()? onTap; + + const SaveButton({super.key, required this.onTap}); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.all(25), + margin: const EdgeInsets.symmetric(horizontal: 100), + decoration: BoxDecoration( + color: Colors.black87, + borderRadius: BorderRadius.circular(8), + ), + child: const Center( + child: Text( + "Save", + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ), + ), + ); + } +} diff --git a/lib/Widgets/single_message.dart b/lib/Widgets/single_message.dart new file mode 100644 index 0000000..dd3ca65 --- /dev/null +++ b/lib/Widgets/single_message.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + + +class SingleMessage extends StatelessWidget { + final String message; + final bool isMe; + SingleMessage({ + required this.message, + required this.isMe + }); + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: isMe ? MainAxisAlignment.end : MainAxisAlignment.start, + children: [ + Container( + padding: EdgeInsets.all(16), + margin: EdgeInsets.all(16), + constraints: BoxConstraints(maxWidth: 200), + decoration: BoxDecoration( + color: isMe ? Colors.black : Colors.orange, + borderRadius: BorderRadius.all(Radius.circular(12)) + ), + child: Text(message,style: TextStyle(color: Colors.white,),) + ), + ], + + ); + } +} \ No newline at end of file diff --git a/lib/Widgets/update_profile_button.dart b/lib/Widgets/update_profile_button.dart new file mode 100644 index 0000000..f330602 --- /dev/null +++ b/lib/Widgets/update_profile_button.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; + +class UpdateProfileButton extends StatelessWidget { + final Function()? onTap; + + const UpdateProfileButton({super.key, required this.onTap}); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.all(25), + margin: const EdgeInsets.symmetric(horizontal: 25), + decoration: BoxDecoration( + border: Border.all( + color:Colors.black87, + ), + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: const Center( + child: Text( + "Update profile", + style: TextStyle( + color: Colors.black87, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ), + ), + ); + } +} diff --git a/lib/Widgets/upload_profile_picture_button.dart b/lib/Widgets/upload_profile_picture_button.dart new file mode 100644 index 0000000..c3f612c --- /dev/null +++ b/lib/Widgets/upload_profile_picture_button.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; + +class UploadProfilePictureButton extends StatelessWidget { + final Function()? onTap; + + const UploadProfilePictureButton({super.key, required this.onTap}); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.all(25), + margin: const EdgeInsets.symmetric(horizontal: 25), + decoration: BoxDecoration( + border: Border.all( + color:Colors.black87, + ), + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: const Center( + child: Text( + "Upload profile picture", + style: TextStyle( + color: Colors.black87, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ), + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 1ac2fcb..e3095db 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,15 +1,22 @@ //it is for test purpose +import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; import 'package:connector/Screens/sign_in_screen.dart'; import 'package:connector/Screens/splash_screen.dart'; -void main() { +import 'Screens/home_screen.dart'; + +void main()async{ + + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); + // This widget is the root of your application. @override Widget build(BuildContext context) { diff --git a/pubspec.lock b/pubspec.lock index e9b9197..d15136f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,13 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.17" async: dependency: transitive description: @@ -15,6 +22,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.3" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" characters: dependency: transitive description: @@ -29,6 +57,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.1" + cloud_firestore: + dependency: "direct main" + description: + name: cloud_firestore + url: "https://pub.dartlang.org" + source: hosted + version: "4.4.4" + cloud_firestore_platform_interface: + dependency: transitive + description: + name: cloud_firestore_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "5.11.4" + cloud_firestore_web: + dependency: transitive + description: + name: cloud_firestore_web + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.4" collection: dependency: transitive description: @@ -36,6 +85,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.16.0" + cross_file: + dependency: transitive + description: + name: cross_file + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.3+4" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" cupertino_icons: dependency: "direct main" description: @@ -50,11 +113,102 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + file: + dependency: "direct main" + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.4" + firebase_auth: + dependency: "direct main" + description: + name: firebase_auth + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.10" + firebase_auth_platform_interface: + dependency: transitive + description: + name: firebase_auth_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "6.11.12" + firebase_auth_web: + dependency: transitive + description: + name: firebase_auth_web + url: "https://pub.dartlang.org" + source: hosted + version: "5.2.9" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + url: "https://pub.dartlang.org" + source: hosted + version: "2.7.1" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "4.5.3" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.2" + firebase_storage: + dependency: "direct main" + description: + name: firebase_storage + url: "https://pub.dartlang.org" + source: hosted + version: "11.0.15" + firebase_storage_platform_interface: + dependency: transitive + description: + name: firebase_storage_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.31" + firebase_storage_web: + dependency: transitive + description: + name: firebase_storage_web + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.24" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_blurhash: + dependency: transitive + description: + name: flutter_blurhash + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.0" flutter_lints: dependency: "direct dev" description: @@ -62,11 +216,86 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.1" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + http: + dependency: transitive + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.5" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.2" + image_picker: + dependency: "direct main" + description: + name: image_picker + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.7" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.6+1" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.12" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.7+1" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.6.3" + intl: + dependency: transitive + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.0" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.4" lints: dependency: transitive description: @@ -95,6 +324,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + octo_image: + dependency: transitive + description: + name: octo_image + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" path: dependency: transitive description: @@ -102,6 +338,118 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.2" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.13" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.24" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.10" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.11.1" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + url: "https://pub.dartlang.org" + source: hosted + version: "10.2.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + url: "https://pub.dartlang.org" + source: hosted + version: "10.2.0" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.7" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "3.9.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" + rxdart: + dependency: transitive + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.27.7" sky_engine: dependency: transitive description: flutter @@ -114,6 +462,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.9.0" + sqflite: + dependency: transitive + description: + name: sqflite + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.6" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.3" stack_trace: dependency: transitive description: @@ -135,6 +497,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.1" + synchronized: + dependency: transitive + description: + name: synchronized + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" term_glyph: dependency: transitive description: @@ -149,6 +518,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.12" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.7" vector_math: dependency: transitive description: @@ -156,5 +539,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.3" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" sdks: dart: ">=2.18.2 <3.0.0" + flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index 1b551f6..3bb74b8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,7 +36,14 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 - + firebase_auth: ^4.2.9 + firebase_core: ^2.7.0 + cloud_firestore: ^4.4.3 + firebase_storage: ^11.0.15 + image_picker: ^0.8.7 + file: ^6.1.4 + permission_handler: ^10.2.0 + cached_network_image: ^3.2.3 dev_dependencies: flutter_test: sdk: flutter