Every app requires users to have their unique profile. As a developer, it's a common task to create a user profile page and integrate it with databases for storing and retrieving user data. This blog zeroed in on how to create a Flutter user profile page with Firebase. We will touch base on Firebase authentication, Firestore, and Firebase storage usage in the Flutter app, making sure as you read along, you'll have the necessary details to create your own user profile page in a Flutter app.
Let's look into the procedure of setting up the Flutter environment before we dive into Firebase.
To get started with Flutter, you need to set up your development environment first. Developers familiar with the setup can skip this part. If you are new to Flutter, follow the subsequent steps.
Flutter is Google's portable UI toolkit for building beautiful, natively-compiled applications for mobile, web, and desktop from a single codebase. Download the stable release of Flutter from the Flutter website. Extract the zipped file to the desired location. Add the Flutter and Dart SDK to the path of your machine.
To verify if Flutter is installed properly, open your terminal/command prompt and run:
1 flutter doctor 2
This command checks your configuration and displays a report of the status of your flutter installation.
There are several IDEs where you can set up the Flutter development environment such as Android Studio, VS Code, IntelliJ IDEA, etc. You need to install the Flutter and Dart plugins in your desired IDE.
After setting up Flutter, the next step is to put Firebase into action in your Flutter project.
As a flexible and robust solution, Firebase is a Google toolkit that allows you to enhance your apps without managing the infrastructure. It offers numerous services like Firestore database, Firebase authentication, Firebase storage, Google Analytics, and much more. We'll be primarily focusing on Firebase Authentication and Firestore for user profile management.
Firebase streamlines the process of app development, provides secure software solutions, enhances the app's quality, and fosters user engagement. By integrating the Firebase project with the Flutter project, one can utilize the various services provided by Firebase within the Flutter application.
1 dependencies: 2 flutter: 3 sdk: flutter 4 5 firebase_core: ^2.15.1 6 firebase_auth: ^4.7.3 7
Don't forget to run the flutter pub get command in the terminal to install these new packages.
In Flutter, everything is a Widget. From a button to a screen layout, all are built using Widgets. Hence, to create a user profile page in Flutter, we set up a tree of Widgets to render the layout. Let's conceptualize our user profile page and then integrate it with Firebase.
In Flutter, layout means arranging the widgets in a visually logical manner. For example, a column might consist of a photo, a name, and a few icons vertically aligned. The first step towards developing a visually compelling UI is understanding the structure and depth of widget trees.
To construct the profile page, we first create a new file called profile_page.dart in your project's lib directory.
Import Flutter Material Design: At the start of the file, import Flutter's Material package which provides visually rich widgets.
1 import 'package:flutter/material.dart'; 2
Create a new StatefulWidget: We create a new StatefulWidget named ProfilePage. This widget creates an instance of _ProfilePageState to maintain the mutable state for this widget subtree.
1 class ProfilePage extends StatefulWidget { 2 @override 3 _ProfilePageState createState() => _ProfilePageState(); 4 } 5 6 class _ProfilePageState extends State<ProfilePage> {} 7
Define the user profile layout: In the _ProfilePageState class, we override the build() function to define the user profile layout.
1 class _ProfilePageState extends State<ProfilePage> { 2 @override 3 Widget build(BuildContext context) { 4 return Scaffold( 5 appBar: AppBar( 6 title: Text('User Profile'), 7 ), 8 body: Center(child: Text('User Profile Page')), 9 ); 10 } 11 } 12
The user profile layout phase is done.
After designing a simple user profile page, it's time to integrate Firebase Firebase Authentication and Firestore into the project so users can sign in and manage their information.
Firebase Authentication offers multiple methods to authenticate users such as Google Sign-In, Facebook Login, Sign-in with Apple, etc. For brevity, we will focus on user registration and authentication using email and passwords.
Import firebase_auth and define the FirebaseAuth instance:
Initialize Firebase and create a new instance of FirebaseAuth which will be used to authenticate our users.
1 import 'package:firebase_auth/firebase_auth.dart'; 2 3 FirebaseAuth _auth = FirebaseAuth.instance; 4
Register a new user:
The Firebase Auth instance allows you to register new users using their email and password. Here's an example method to create a new user.
1 Future registerNewUser(String email, String password) async { 2 try { 3 UserCredential userCredential = await _auth.createUserWithEmailAndPassword( 4 email: email.trim(), 5 password: password, 6 ); 7 return userCredential.user; 8 } on FirebaseAuthException catch (e) { 9 // handle error 10 } 11 } 12
Upon successful user account creation, we want to store user profile information like username and profile picture, which are not directly managed by Firebase Authentication. Firestore, a NoSQL database, is a great tool for managing user profile data.
You need to import the cloud_firestore package and create an instance of FirebaseFirestore.
1 import 'package:cloud_firestore/cloud_firestore.dart'; 2 3 final FirebaseFirestore _firestore = FirebaseFirestore.instance; 4
After user creation with Firebase Authentication, we can save additional user information in Firestore. For example:
1 Future createUserInFirestore(User user, String username) async { 2 DocumentSnapshot doc = await _firestore.collection('users').doc(user.uid).get(); 3 4 if (!doc.exists) { 5 _firestore.collection('users').doc(user.uid).set({ 6 'username': username, 7 'email': user.email, 8 'createdOn': DateTime.now(), 9 }); 10 } 11 } 12
Here, we check whether a document for the user already exists. If not, we create a new document with the user's ID as the document ID and set some initial data for the user.
Once you've created your user and have some initial data set, it's common that you'll want to allow users to update their profile information. Here's how to handle user profile updates and reflect them in real time on the profile page.
To update the Firestore document, we'll use Firebase's update method. Below is an example updateUserProfile method where a user updates their username.
1 Future updateUserProfile(User user, String newUsername) async { 2 await _firestore.collection('users').doc(user.uid).update({ 3 'username': newUsername, 4 }); 5 } 6
Firebase's Firestore SDKs provide real-time updates for data changes. This means when a user's profile document changes, you can reflect these changes in real-time on the user's profile page.
Here's an example of how you can implement this.
1 @override 2 Widget build(BuildContext context) { 3 final User user = _auth.currentUser; 4 5 return StreamBuilder<DocumentSnapshot>( 6 stream: _firestore.collection('users').doc(user.uid).snapshots(), 7 builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) { 8 if (snapshot.hasError) { 9 return Text('Error: ${snapshot.error}'); 10 } else if (!snapshot.hasData) { 11 return CircularProgressIndicator(); 12 } else if (snapshot.hasData) { 13 Map<String, dynamic> data = snapshot.data.data(); 14 return ListTile( 15 title: Text('Username: ${data['username']}', 16 subtitle: Text('Email: ${data['email']}'), 17 ); 18 } 19 return CircularProgressIndicator(); 20 }, 21 ); 22 } 23
In this code snippet, we're using a StreamBuilder to listen for updates on a Firestore document. As updates come in, we rebuild our widget with the updated information.
This allows us to reflect the updates on the user's profile page as soon as they happen. This brings in updates to the user profile with Firebase.
Most user profile pages need a feature that lets users select a profile picture and update it over time. Firebase Storage can come in handy for these tasks.
Start by adding the firebase_storage plugin to your pubspec.yaml:
1 dependencies: 2 flutter: 3 sdk: flutter 4 firebase_storage: ^11.2.6 5
Create a new instance of FirebaseStorage to interact with Firebase Storage in your Flutter app:
1 final FirebaseStorage _storage = FirebaseStorage.instance; 2
Assuming you have a file profilePicture ready to be uploaded (you can get this file using packages like image_picker), we can upload it to Firebase Storage as follows:
1 Future uploadProfilePicture(User user, File profilePicture) async { 2 UploadTask uploadTask = _storage 3 .ref() 4 .child('userProfilePictures/${user.uid}/profilePicture.jpg') 5 .putFile(profilePicture); 6 7 TaskSnapshot taskSnapshot = await uploadTask.whenComplete(() => null); 8 String downloadURL = await taskSnapshot.ref.getDownloadURL(); 9 10 return downloadURL; 11 } 12
Notice that we use the putFile method which is useful for uploading local files. We also get the download URL at the end, which can be stored in our Firestore database and used to retrieve this image later on.
You can display this profile picture in your app by using the download URL we stored in the user's Firestore document.
1 // using Image.network to load an image from the network 2 Image.network('${data['profilePictureURL']}') 3
That's it! You learned how to add a profile picture using Firebase Storage.
Security is a big consideration when dealing with user data. Firebase provides various ways to secure your data with security rules.
Firebase Security Rules allow you to control who has access to your Firebase project: Authentication, Firestore, and Storage. These rules can restrict read/write operations, validate incoming data, and much more.
You can navigate to the Firebase Console > Firestore > Rules to implement Firestore security rules for your application. Here is a sample rule that allows users to read/write their own document in the 'users' collection:
1 rules_version = '2'; 2 service cloud.firestore { 3 match /databases/{database}/documents { 4 match /users/{userId} { 5 allow read, write: if request.auth != null && request.auth.uid == userId; 6 } 7 } 8 } 9
Similarly, in Firebase Console > Storage > Rules, you can implement security rules for Firebase Storage. Here is a sample rule that allows users to upload/download their own profile pictures:
1 rules_version = '2'; 2 service firebase.storage { 3 match /b/{bucket}/o { 4 match /userProfilePictures/{userId}/{allPaths=**} { 5 allow read, write: if request.auth != null && request.auth.uid == userId; 6 } 7 } 8 } 9
Firebase Security Rules are a powerful tool for securing your application data. Given their importance, make sure to spend a good amount of time understanding and implementing them correctly.
Testing is important as it ensures the code behaves as intended. The assurance of quality and reliability of the Flutter application is crucial before it goes into production.
Flutter provides a rich set of testing features to test apps at unit, widget, and integration levels. Unit tests can verify that the Firestore calls work as expected by using a package (cloud_firestore_mocks) to mock the Firestore instance. Here's an example of testing the createUserInFirestore function.
1 void main() { 2 final instance = MockFirestoreInstance(); 3 final user = User(uid: 'uid', email: 'email'); 4 5 test('createUserInFirestore creates a new user document', () async { 6 await createUserInFirestore(user, instance, 'username'); 7 DocumentSnapshot snapshot = await instance.collection('users').doc(user.uid).get(); 8 expect(snapshot.exists, true); 9 }); 10 } 11
Simulating the user registration process to ensure that the communication between the Firebase Authentication, Firestore, and Firebase Storage squads goes unerringly is crucial. Integration tests help us accomplish such requirements.
Though the cloud services offered by Firebase don't support a local emulator completely yet, there are several strategies you can use. You can use a separate Firebase project with test-specific data, mock the interaction, or use Firebase's local emulators for these tests.
The creation of a user profile page using Flutter and Firebase is characteristic of most modern applications. This blog post explained the step-by-step process of creating a Flutter user profile page with Firebase. We discussed various Firebase services such as Firebase Authentication for user sign-ups, Firestore for storing user data, Firebase Storage for storing and retrieving user data, as well as applying security rules to our Firebase project.
As I walked you through this blog, I hope you got a comprehensive understanding of the steps involved. Remember, practice is key. Challenge yourself to build a Flutter app with user authentication and a profile page, perhaps with extra features I didn't mention here.
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.