Connect Day 6: Flutter BLoC Example for Beginners

Connect Day 6 Flutter BLoC Example for Beginners

Flutter BLoC Example for Beginners

Welcome back to python-hub.com. Where we are building

🔥Connect: A Photo Sharing App – 5 Steps at a Time 🔥

Today’s goal? Fetching & updating user profiles using Flutter BLoC.

👀 Ever wondered how apps like Instagram retrieve user data dynamically?
📌 How does a user’s profile update in real time when they change their name or bio?

We’ll walk through the process step by step, ensuring that the profile page dynamically displays user data from Firebase.

By the end of this, your app will:
Fetch user details (name, email, profile pic, bio, etc.)
Store & retrieve user data from Firestore
Use Flutter BLoC to manage UI state efficiently

I have tried to keep today’s article a little light.

GitHub Link: https://github.com/maitry4/Connect/

If you find this difficult I’ll add an infographic that better explains bloc.

Flutter BLoC Example for Beginners

Step 1: Get The User’s Name On The Profile Page

Navigate to firebase_auth_repo.dart, where the getCurrentUser method currently does not retrieve the user’s name from Firebase.

Modify the method now that we have Firebase data storage.

➡️Click To View Modified Code
 Future<CAppUser?> getCurrentUser() async {
    // Get the currently authenticated user from Firebase
    final firebaseUser = firebaseAuth.currentUser;

    // If no user is logged in, return null
    if (firebaseUser == null) {
      return null;
    } else {
      // Fetch user details from Firestore
      DocumentSnapshot userDoc = await firebaseFirestore
          .collection('users')
          .doc(firebaseUser.uid)
          .get();

      if (!userDoc.exists) {
        return null; // If user data doesn't exist in Firestore database
      }

      // Convert Firestore data to CAppUser
      return CAppUser.fromJson(userDoc.data() as Map<String, dynamic>);
    }
  }

1. Get the Auth Cubit

// cubits
late final authCubit = context.read<CAuthCubit>();

2. Get the current user from the auth cubit

// get the current user
late CAppUser? currentUser = authCubit.currentUser;

3. Now display its name in place of the dummy data we placed.

//mobile
Text(
  currentUser!.name,
  style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),

4. For desktop take parameter in the profile card widget.

//desktop
return Center(child: CProfileCardWidget(nm: currentUser!.name));

5. Profile card widget.

//profile card widget
class CProfileCardWidget extends StatefulWidget {
  final String nm;
  const CProfileCardWidget({super.key, required this.nm});
....  
//name instead of samantha.
Text(widget.nm,...

🎉 If the name appears correctly, we’re good to move on!

Step 2: Create An Entity For Further Data Of The User

Right now, CAppUser only holds name, email, and UID.
But for a proper profile page, we also need:
Bio
Profile Picture URL

📌 Solution? Create a new entity in features/profile/domain/entities/profile_user.dart:

import 'package:connect/features/auth/domain/entities/app_user.dart';

class CProfileUser extends CAppUser {
  final String bio;
  final String profileImageUrl;

  CProfileUser({
    required super.uid,
    required super.email,
    required super.name,
    required this.bio,
    required this.profileImageUrl,
  });

}

Now we need to add a method to update the user’s profile.

// Method to update profile user
  CProfileUser copyWith({String? newBio, String? newProfileImageUrl}) {
    return CProfileUser(
      uid: uid, 
      email: email, 
      name: name, 
      bio: newBio?? bio, 
      profileImageUrl: newProfileImageUrl?? profileImageUrl
    );
  }

Lastly, create methods for converting this profile user -> json and vice versa.

➡️Click To View The Complete Code CProfileUser
import 'package:connect/features/auth/domain/entities/app_user.dart';

class CProfileUser extends CAppUser {
  final String bio;
  final String profileImageUrl;

  CProfileUser({
    required super.uid,
    required super.email,
    required super.name,
    required this.bio,
    required this.profileImageUrl,
  });

  // Method to update profile user
  CProfileUser copyWith({String? newBio, String? newProfileImageUrl}) {
    return CProfileUser(
      uid: uid, 
      email: email, 
      name: name, 
      bio: newBio?? bio, 
      profileImageUrl: newProfileImageUrl?? profileImageUrl
    );
  }
  // convert profile user -> json
  Map<String, dynamic> toJson() {
    return {
      'uid':uid,
      'email':email,
      'name':name, 
      'bio':bio,
      'profileImageUrl':profileImageUrl,
    };
  }

  factory CProfileUser.fromJson(Map<String, dynamic> jsonUser) {
    return CProfileUser(
      uid: jsonUser['uid'],
      email: jsonUser['email'],
      name: jsonUser['name'],
      bio: jsonUser['bio'],
      profileImageUrl:jsonUser['profileImageUrl'],
    );
  }
}

Step 3: Create An Abstract Repo For Getting Data From Firebase

To ensure clean architecture, let’s create an abstract class inside:
📂 features/profile/domain/repos/profile_repo.dart

Here we will declare 2 methods:

✅ Fetching the user profile
✅ Updating the user profile

import 'package:connect/features/profile/domain/entities/profile_user.dart';

abstract class CProfileRepo {
  Future<CProfileUser?> fetchUserProfile(String uid);
  Future<void> updateProfile(CProfileUser updatedProfile);
}

Flutter BLoC Example for Beginners Flutter BLoC Example for Beginners Flutter BLoC Example for Beginners Flutter BLoC Example for Beginners

Step 4: Create A Firebase Repo To Fetch And Update The User Data

Now, let’s connect the abstract repository to Firebase by creating:

📂 features/profile/data/firebase_profile_repo.dart

➡️Click To View firebase_profile_repo.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:connect/features/profile/domain/entities/profile_user.dart';
import 'package:connect/features/profile/domain/repos/profile_repo.dart';

class CFirebaseProfileRepo implements CProfileRepo {
  // get firebase firestore instance
  final FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;

  @override
  Future<CProfileUser?> fetchUserProfile(String uid) async {
    try {
      final userDoc = await firebaseFirestore
          .collection('users')
          .doc(uid)
          .get();
      if(userDoc.exists) {
        final userData = userDoc.data();
        if(userData != null) {
          return CProfileUser(
            uid: uid,
            email: userData['email'],
            name: userData['name'],
            bio: userData['bio'] ?? '',
            profileImageUrl: userData['profileImageUrl'].toString(),
          );
        }
      }
      return null;
    } catch(e) {
      return null;
    }
  }
  
  @override
  Future<void> updateProfile(CProfileUser updatedProfile) async {
    try {
      await firebaseFirestore
      .collection('users')
      .doc(updatedProfile.uid)
      .update({
        'bio':updatedProfile.bio,
        'profileImageUrl':updatedProfile.profileImageUrl,
      });
    }
    catch(e) {
      throw Exception(e);
    }
  }
}

Flutter BLoC Example for Beginners Flutter BLoC Example for Beginners Flutter BLoC Example for Beginners Flutter BLoC Example for Beginners

Now we can fetch and update the user’s profile!

Step 5: Putting It All Together In A Cubit

Get back to the presentation directory of the profile feature.

Create a new directory cubit.

Inside that should be 2 files:

  1. profile_state.dart
  2. profile_cubit.dart

In the profile state, we will have 4 states: Initial, loading, loaded, and error.

Loaded will take the current profile user and error will take the error message in their constructors.

➡️Click To View profile_state.dart
import 'package:connect/features/profile/domain/entities/profile_user.dart';

abstract class CProfileState {}

// initial
class CProfileInitialState extends CProfileState {}
// loading
class CProfileLoadingState extends CProfileState {}
// loaded
class CProfileLoadedState extends CProfileState {
  final CProfileUser profileUser;
  CProfileLoadedState(this.profileUser);
}
// error
class CProfileErrorState extends CProfileState {
  final String message;
  CProfileErrorState(this.message);
}

In the profile cubit, we will define 2 methods one to fetch the user’s profile and another to update their profile.

We will use the Firebase profile repo’s instance for this with a bit of error handling.

➡️Click To View profile_cubit.dart
import 'package:connect/features/profile/data/firebase_profile_repo.dart';
import 'package:connect/features/profile/presentation/cubits/profile_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class CProfileCubit extends Cubit<CProfileState> {
  final CFirebaseProfileRepo profileRepo;
  CProfileCubit({required this.profileRepo}) : super(CProfileInitialState());

  // fetch profile using repo
  Future<void> fetchUserProfile(String uid) async {
    try {
      emit(CProfileLoadingState());
      final user = await profileRepo.fetchUserProfile(uid);
      if(user != null){
        emit(CProfileLoadedState(user));
      }
      else {
        emit(CProfileErrorState("User Not Found"));
      }
    } catch(e) {
        emit(CProfileErrorState(e.toString()));
    }
  }
  // update bio and or profile picture.
  Future<void> updateProfile({
    required String uid,
    String? newBio,
  }) async {
      emit(CProfileLoadingState());

      try {
        // fetch the current profile first
        final currentUser = await profileRepo.fetchUserProfile(uid);

        if(currentUser == null) {
          emit(CProfileErrorState("Failed to fetch the user details"));
          return;
        }

        // TODO:update profile picture

        // update new profile
        final updatedProfile = currentUser.copyWith(newBio: newBio ?? currentUser.bio);

        // update in the repostiory
        await profileRepo.updateProfile(updatedProfile);
        // re-fetch the updated profile
        await fetchUserProfile(uid);
      }
      catch(e) {
          emit(CProfileErrorState("Error updating the user details: $e"));
      }
  }
}

That’s it for today hope you got the bloc architecture in Flutter while building the profile page.

Tomorrow we will see how to show this all up on the UI and will design our Profile edit page for both platforms.

Flutter BLoC Example for Beginners Flutter BLoC Example for Beginners Flutter BLoC Example for Beginners Flutter BLoC Example for Beginners

Conclusion & What’s Next?

🔥 What We Built Today:
✅ Profile Page now fetches real user data
✅ Created a structured profile entity
✅ Stored & retrieved user data using Firebase
✅ Implemented BLoC to manage user state

🚀 Coming Up Next:
Tomorrow, we’ll design the UI and build the Profile Edit Page for both mobile & desktop!

If you’re enjoying this journey, drop a comment on what feature you’d love to see next! 🚀💬

Leave a Reply