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 image
RegExp 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;
@override
void 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 exists
return profileImage(widget.user);
} else {
// New profile image
return 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 database
String? _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 update
DatabaseService.updateUser(user);
Navigator.pop(context);
}
}
@override
Widget 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

Edit profile Image Picker