Firebase Storage - Profile
🤳 Firebase Storage - Profile
Here we will look at firebase storage, we have images for the attendees 📇 profile Images we can store in firebase storage. For that we will create a Edit profile screen
Image Upload
Create a new 📄 file storage_service.dart
under services
📁 folder
Install a package flutter_image_compress , update pubspec.yaml 📄 file so that we compress images during storage.
flutter_image_compress: ^1.1.0
import 'dart:io';import 'package:firebase_storage/firebase_storage.dart';import 'package:flutter_image_compress/flutter_image_compress.dart';import 'package:devfest_flutter_firebase_chat/helpers/constants.dart';import 'package:path_provider/path_provider.dart';import 'package:uuid/uuid.dart';class StorageService {static Future<String> uploadUserProfileImage(String url, File imageFile) async {String? photoId = const Uuid().v4();File? image = await _compressImage(photoId, imageFile);if (url.isNotEmpty) {// Updating user profile imageRegExp exp = RegExp(r'userProfile_(.*).jpg');photoId = exp.firstMatch(url)![1];}String downloadUrl = await _uploadImage('images/users/userProfile_$photoId.jpg',image!,);return downloadUrl;}static Future<File?> _compressImage(String imageId, File image) async {final tempDir = await getTemporaryDirectory();final path = tempDir.path;File? compressedImageFile = await FlutterImageCompress.compressAndGetFile(image.path,'$path/img_$imageId.jpg',quality: 70,);return compressedImageFile;}static Future<String> _uploadImage(String path, File image) async {UploadTask uploadTask = storageRef.child(path).putFile(image);TaskSnapshot storageSnap = await uploadTask;String downloadUrl = await storageSnap.ref.getDownloadURL();return downloadUrl;}Future<String> uploadChatImage(String? url, File? imageFile) async {String? imageId = const Uuid().v4();File? image = await _compressImage(imageId, imageFile!);if (url != null) {RegExp exp = RegExp(r'chat_(.*).jpg');imageId = exp.firstMatch(url)![1];}String downloadUrl = await _uploadImage('images/chats/chat_$imageId.jpg',image!,);return downloadUrl;}Future<String> uploadMessageImage(File imageFile) async {String imageId = const Uuid().v4();File? image = await _compressImage(imageId, imageFile);String downloadUrl = await _uploadImage('images/messages/message_$imageId.jpg',image!,);return downloadUrl;}}
Also update database service to add a function to update user database_service.dart
static void updateUser(AppUser user) {usersRef.doc(user.id).update({'name': user.name,'profileImageUrl': user.profileImageUrl,'bio': user.bio,});}
Now backend is ready, lets work on the screen create a new file edit_profile_screen.dart
under screens folder,
EditProfile Screen
Create a new 📄 file edit_profile_screen.dart
under screens
📁 folder.
we need a image picker, install a new package image_picker: ^0.8.4+5
add this code to edit_profile_screen.dart
file
import 'dart:io';import 'package:devfest_flutter_firebase_chat/widgets/user_profile_image.dart';import 'package:flutter/material.dart';import 'package:devfest_flutter_firebase_chat/helpers/app_constants.dart';import 'package:devfest_flutter_firebase_chat/models/app_user.dart';import 'package:devfest_flutter_firebase_chat/services/storage_service.dart';import 'package:devfest_flutter_firebase_chat/services/database_service.dart';import 'package:image_picker/image_picker.dart';class EditProfileScreen extends StatefulWidget {static const String id = 'edit_profile_screen';final AppUser user;const EditProfileScreen({required this.user, Key? key}) : super(key: key);@override_EditProfileScreenState createState() => _EditProfileScreenState();}class _EditProfileScreenState extends State<EditProfileScreen> {final _formKey = GlobalKey<FormState>();File? _profileImage;String _name = '';String _bio = '';bool _isLoading = false;@overridevoid initState() {super.initState();_name = widget.user.name!;_bio = widget.user.bio!;}_handleImageFromGallery() async {XFile? imageFile =await ImagePicker().pickImage(source: ImageSource.gallery);if (imageFile != null) {setState(() {_profileImage = File(imageFile.path);});}}_displayProfileImage() {if (_profileImage == null) {// User profile image existsreturn profileImage(widget.user);} else {// New profile imagereturn CircleAvatar(radius: 60.0,backgroundColor:AppConstants.hexToColor(AppConstants.APP_BACKGROUND_COLOR_GRAY),backgroundImage: FileImage(_profileImage!),);}}_submit() async {if (_formKey.currentState!.validate() && !_isLoading) {_formKey.currentState!.save();setState(() {_isLoading = true;});// Update user in databaseString? _profileImageUrl = '';if (_profileImage == null) {_profileImageUrl = widget.user.profileImageUrl;} else {_profileImageUrl = await StorageService.uploadUserProfileImage(widget.user.profileImageUrl!,_profileImage!,);}AppUser user = AppUser(id: widget.user.id,name: _name,profileImageUrl: _profileImageUrl,bio: _bio,);// Database updateDatabaseService.updateUser(user);Navigator.pop(context);}}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(backgroundColor:AppConstants.hexToColor(AppConstants.APP_PRIMARY_COLOR),title: const Text('Edit Profile',style: TextStyle(fontSize: 28.0,fontWeight: FontWeight.bold,),),),body: GestureDetector(onTap: () => FocusScope.of(context).unfocus(),child: ListView(children: <Widget>[_isLoading? LinearProgressIndicator(backgroundColor:AppConstants.hexToColor(AppConstants.APP_PRIMARY_COLOR),valueColor: AlwaysStoppedAnimation(AppConstants.hexToColor(AppConstants.APP_PRIMARY_COLOR)),): const SizedBox.shrink(),Padding(padding: const EdgeInsets.all(30.0),child: Form(key: _formKey,child: Column(children: <Widget>[_displayProfileImage(),TextButton(onPressed: _handleImageFromGallery,child: Text('Change Profile Image',style: TextStyle(color: AppConstants.hexToColor(AppConstants.APP_PRIMARY_COLOR_ACTION),fontSize: 16.0),),),TextFormField(initialValue: _name,style: const TextStyle(fontSize: 18.0),decoration: const InputDecoration(icon: Icon(Icons.person,size: 30.0,),labelText: 'Name',),validator: (input) => input!.trim().isEmpty? 'Please enter a valid name': null,onSaved: (input) => _name = input!,),TextFormField(initialValue: _bio,style: const TextStyle(fontSize: 18.0),decoration: const InputDecoration(icon: Icon(Icons.book,size: 30.0,),labelText: 'Bio',),validator: (input) => input!.trim().length > 150? 'Please enter a bio less than 150 characters': null,onSaved: (input) => _bio = input!,),Container(margin: const EdgeInsets.all(40.0),height: 40.0,width: 250.0,child: FlatButton(onPressed: _submit,color: AppConstants.hexToColor(AppConstants.APP_PRIMARY_COLOR),textColor: AppConstants.hexToColor(AppConstants.APP_PRIMARY_FONT_COLOR_WHITE),child: const Text('Save Profile',style: TextStyle(fontSize: 18.0),),),),],),),),],),),);}}
to navigate to this screen modify onpressed for the Edit Profile icon button in app_drawer_widget.dart , fix the imports
onPressed: () => Navigator.push(context,MaterialPageRoute(builder: (_) => EditProfileScreen(user: user,),),),color:
Update commented ``profileImage(user, avatarRadius: 20),``attendees_screen.dart
Information and FYI
We used few new packages in the new code make sure they exist , or add them to pubspec.yaml and run pub get
flutter_image_compress: ^0.6.5+1
path_provider: ^1.6.5
uuid: ^2.0.4
image_picker: ^0.6.3+4
Image picker for IOS needs some special setting https://pub.dev/packages/image_picker
NSPhotoLibraryUsageDescription
NSCameraUsageDescription
if you get an error
MissingPluginException (MissingPluginException(No implementation found for method pickImage on channel plugins.flutter.io/image_picker))
run flutter clean
andrestart the app