diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 1af2befef7..7805d4c420 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,6 +8,8 @@ diff --git a/android/settings_aar.gradle b/android/settings_aar.gradle new file mode 100644 index 0000000000..e7b4def49c --- /dev/null +++ b/android/settings_aar.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index a508656d4a..ee2ba91b95 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -2,6 +2,10 @@ + + NSPhotoLibraryUsageDescription + Access gallery to pick image + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable diff --git a/lib/screens/profile_screen.dart b/lib/screens/profile_screen.dart index 84f911654b..f51f0190c4 100644 --- a/lib/screens/profile_screen.dart +++ b/lib/screens/profile_screen.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:class_manager/constants.dart'; import 'package:class_manager/models/users.dart'; import 'package:class_manager/screens/onboarding_page.dart'; @@ -6,8 +8,11 @@ import 'package:class_manager/services/facebookAuthentication.dart'; import 'package:class_manager/services/googleAuthentication.dart'; import 'package:class_manager/services/user_info_services.dart'; import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:path/path.dart' as Path; class ProfileScreen extends StatefulWidget { @override @@ -15,16 +20,51 @@ class ProfileScreen extends StatefulWidget { } class _ProfileScreenState extends State { + ImagePicker _imagePicker = ImagePicker(); + StorageReference _storageReference = FirebaseStorage.instance.ref(); + + void getImage() async { + PickedFile image = await _imagePicker.getImage(source: ImageSource.gallery); + + if (image == null) { + return; + } + + String imagePath = image.path; + + File file = File(imagePath); + + uploadPictures(file); + } + + uploadPictures(File image) async { + // uploads picture(s) to storage and return it's URL + final StorageReference ref = + _storageReference.child('${Path.basename(image.path)}}'); + + final StorageUploadTask uploadTask = ref.putFile(image); + + UploadTaskSnapshot uploadTaskSnapshot = await uploadTask.future; + String pictureUrl = uploadTaskSnapshot.downloadUrl.toString(); + + final userInfoProvider = + Provider.of(context, listen: false); + + Users currentUser = userInfoProvider.user; + currentUser.profilePictureUrl = pictureUrl; + + userInfoProvider.setUser(currentUser); + + userInfoProvider.upateProfilePictureUrl(); + } + @override Widget build(BuildContext context) { final FirebaseAuth firebaseAuth = FirebaseAuth.instance; final User userData = firebaseAuth.currentUser; return Scaffold( - backgroundColor: Theme - .of(context) - .backgroundColor - .withOpacity(0.8), + backgroundColor: Theme.of(context).backgroundColor.withOpacity(0.8), body: SingleChildScrollView( physics: AlwaysScrollableScrollPhysics(parent: BouncingScrollPhysics()), child: Consumer( @@ -36,17 +76,12 @@ class _ProfileScreenState extends State { children: [ Column( children: [ - SizedBox(height: 0.15 * MediaQuery - .of(context) - .size - .height), + SizedBox(height: 0.15 * MediaQuery.of(context).size.height), Container( margin: EdgeInsets.fromLTRB(15, 15, 15, 60), padding: EdgeInsets.all(30), decoration: BoxDecoration( - color: Theme - .of(context) - .primaryColor, + color: Theme.of(context).primaryColor, borderRadius: BorderRadius.only( topLeft: Radius.circular(40), topRight: Radius.circular(40), @@ -139,8 +174,9 @@ class _ProfileScreenState extends State { // For Sign Out from Google Auth or Facebook Auth, Back to the On Boarding Page Navigator.pushAndRemoveUntil( context, - MaterialPageRoute(builder: (_) => OnboardingPage()), - (Route route) => false, + MaterialPageRoute( + builder: (_) => OnboardingPage()), + (Route route) => false, ); } } @@ -151,18 +187,10 @@ class _ProfileScreenState extends State { horizontal: 20, vertical: 10), shape: StadiumBorder(), color: Colors.transparent, - hoverColor: Theme - .of(context) - .primaryColor, - splashColor: Theme - .of(context) - .primaryColor, - focusColor: Theme - .of(context) - .primaryColor, - highlightColor: Theme - .of(context) - .primaryColor, + hoverColor: Theme.of(context).primaryColor, + splashColor: Theme.of(context).primaryColor, + focusColor: Theme.of(context).primaryColor, + highlightColor: Theme.of(context).primaryColor, child: Text( "Log out", style: TextStyle( @@ -178,35 +206,28 @@ class _ProfileScreenState extends State { ], ), Positioned( - top: 0.1 * MediaQuery - .of(context) - .size - .height, + top: 0.1 * MediaQuery.of(context).size.height, child: CircleAvatar( radius: profilePictureDiameter / 2, - backgroundImage: - AssetImage("assets/images/profile_pic.jpg"), + backgroundImage: _user.profilePictureUrl.isEmpty + ? AssetImage("assets/images/profile_pic.jpg") + : NetworkImage(_user.profilePictureUrl), backgroundColor: Colors.transparent, - foregroundColor: Theme - .of(context) - .backgroundColor, + foregroundColor: Theme.of(context).backgroundColor, ), ), Positioned( - top: 0.1 * MediaQuery - .of(context) - .size - .height + + top: 0.1 * MediaQuery.of(context).size.height + profilePictureDiameter - 35, - left: (MediaQuery - .of(context) - .size - .width / 2) + 25, - child: Icon( - Icons.camera_alt, - size: profilePictureDiameter * 0.25, - color: Colors.white, + left: (MediaQuery.of(context).size.width / 2) + 25, + child: IconButton( + icon: Icon( + Icons.camera_alt, + size: profilePictureDiameter * 0.25, + color: Colors.white, + ), + onPressed: getImage, ), ), ], diff --git a/lib/services/user_db_services.dart b/lib/services/user_db_services.dart index e238803775..55d05f24c0 100644 --- a/lib/services/user_db_services.dart +++ b/lib/services/user_db_services.dart @@ -23,6 +23,17 @@ class UserDBServices { .set(_user.toJson(), SetOptions(merge: true)); } + static Future updateProfilePictureUrl( + String uid, String profilePictureUrl) async { + FirebaseFirestore firestoreDB = FirebaseFirestore.instance; + + await firestoreDB.collection(usersCollection).doc(uid).update( + { + 'profilePictureUrl': profilePictureUrl, + }, + ); + } + // Fetches User Data from Collection static Future fetchUserData(BuildContext context) async { FirebaseFirestore firestoreDB = FirebaseFirestore.instance; diff --git a/lib/services/user_info_services.dart b/lib/services/user_info_services.dart index c2969e125b..6680da83f6 100644 --- a/lib/services/user_info_services.dart +++ b/lib/services/user_info_services.dart @@ -22,6 +22,12 @@ class UserInfoServices extends ChangeNotifier { notifyListeners(); } + Future upateProfilePictureUrl() async { + await UserDBServices.updateProfilePictureUrl( + _user.uid, _user.profilePictureUrl); + // notifyListeners(); + } + void setUser(Users _usr) { this._user = _usr; this.hasData = true; diff --git a/lib/widgets/header.dart b/lib/widgets/header.dart index 6b60379aa9..722ee916a5 100644 --- a/lib/widgets/header.dart +++ b/lib/widgets/header.dart @@ -8,37 +8,40 @@ class Header extends StatelessWidget { Widget build(BuildContext context) { return Padding( padding: EdgeInsets.fromLTRB(30.0, 50.0, 30.0, 30.0), - child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Flexible( - child:SvgPicture.asset( - "assets/icons/grad_cap.svg", - height: 70.0, - ), - ), - Flexible( - child:Consumer( - builder: (ctx, _userInfo, _) => FittedBox( - fit: BoxFit.fitWidth, - child: Text( - "Hello, " + - (_userInfo.hasData - ? _userInfo.user.name.split(" ")[0] - : "Sir"), - style: TextStyle( - color: Colors.white, - fontSize: 25.0, - fontWeight: FontWeight.bold, - ), - ), + child: SvgPicture.asset( + "assets/icons/grad_cap.svg", + height: 70.0, ), ), + Consumer( + builder: (ctx, _userInfo, _) => Row( + children: [ + FittedBox( + fit: BoxFit.fitWidth, + child: Text( + "Hello, " + + (_userInfo.hasData + ? _userInfo.user.name.split(" ")[0] + : "Sir"), + style: TextStyle( + color: Colors.white, + fontSize: 25.0, + fontWeight: FontWeight.bold, + ), + ), + ), + CircleAvatar( + radius: 25.0, + backgroundImage: _userInfo.user.profilePictureUrl.isEmpty + ? AssetImage("assets/images/profile_pic.jpg") + : NetworkImage(_userInfo.user.profilePictureUrl), + ), + ], ), - CircleAvatar( - radius: 25.0, - backgroundImage: AssetImage("assets/images/profile_pic.jpg"), ), ], ), diff --git a/pubspec.lock b/pubspec.lock index 6bf4d16ed9..0c77c3e1b0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -106,6 +106,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.0" firebase_auth: dependency: "direct main" description: @@ -148,6 +162,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.1+3" + firebase_storage: + dependency: "direct main" + description: + name: firebase_storage + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.2" flutter: dependency: "direct main" description: flutter @@ -174,6 +195,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.9.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.11" flutter_svg: dependency: "direct main" description: @@ -219,6 +247,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.9.2" + http: + dependency: transitive + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.2" http_parser: dependency: transitive description: @@ -233,6 +268,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.1" + image_picker: + dependency: "direct main" + description: + name: image_picker + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.7+22" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.6" intl: dependency: "direct main" description: @@ -289,6 +338,48 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.0-nullsafety.0" + path_provider: + dependency: "direct main" + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.11.0" petitparser: dependency: transitive description: @@ -296,6 +387,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.2" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" plugin_platform_interface: dependency: transitive description: @@ -303,6 +401,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.3" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" provider: dependency: "direct main" description: @@ -385,6 +490,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" xml: dependency: transitive description: @@ -400,5 +519,5 @@ packages: source: hosted version: "3.1.0" sdks: - dart: ">=2.12.0-0.0 <3.0.0" + dart: ">=2.12.0 <3.0.0" flutter: ">=1.24.0-7.0" diff --git a/pubspec.yaml b/pubspec.yaml index 63889c971c..6148d3d2cc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -24,6 +24,9 @@ dependencies: google_sign_in: uuid: ^3.0.1 flutter_facebook_login: + image_picker: + firebase_storage: + path_provider: dev_dependencies: flutter_test: