Instagram Clone Clean Architecture – Part 8

Every developer in this universe write code, but it sounds really nice when someone says this developer writes clean code. As flutter framework got only Dart language to write UI code and also business logic, so everything become messy when both UI code and business logic mix up.

Question: How do we can stop our code from being messy?

Note: We’re already using Clean Architecture for separation of concern but this situation is different.

Answer: There comes our Bloc library but we will use its lighter version Cubit which removes bunch of boilerplate code, Cubit not required any kind of package to add but it comes with the package flutter_bloc of version 6.0.0 or above.

Don’t know what is Bloc?

Bloc is well-known and established state management library for flutter, which promotes immutability and has one of the best ecosystems of supporting packages and documentation built around it.

Don’t know what is Cubit?

Cubit is lightweight state management solution. It is subset of the bloc package that does not rely on events and instead uses methods to emit new states. Every Cubit requires an initial state which will be the state of the Cubit before emit has been called.

Bloc

If you’ve used the bloc in past you may be familiar with this diagram.

This Bloc architecture self-explained. Let’s understand it, In Bloc we’ve Events and States and Bloc itself.

  • Event: Events tell bloc to do something, Triggered button click, method call etc.
  • State: States represent the info to be processed by any widget, Initial, Loading, Loaded and Failure State etc.
  • Bloc: All the business logic take place inside the Bloc, It accept events, perform the logic and output states.

In General: We’ve events (click, trigger, call) and states (initial, loading, loaded etc.) Bloc accept events perform the logic and output a specific state.

Cubit

If you’ve used the cubit in past you may be familiar with this diagram.

This Cubit architecture is also self-explained. Let’s understand it, in cubit we’ve States and Cubit itself.

  • State: States represent the info to be processed by any widget, Initial, Loading, Loaded and Failure State etc.
  • Cubit: Cubit exposes direct functions and outputs appropriate states.

In General: We’ve States (Initial, Loading, Loaded and Failure), Cubit directly take functions and emits an appropriate state.

Create Directories:

Go to > presentation > cubit and create 3 directories, user, credential and auth. Now create cubit class inside all of them with specific names.

If you’re using Android Studio install a plugin of Bloc.

Go to File > Settings > Plugins > Market Place install Bloc.

 

If you’re using VS Code install and extension of Bloc.

After creating Cubit Classes your directories will look something like this:

Auth Cubit:

auth_state.dart

 

				
					part of 'auth_cubit.dart';

abstract class AuthState extends Equatable {
  const AuthState();
}

class AuthInitial extends AuthState {
  @override
  List<Object> get props => [];
}

class Authenticated extends AuthState {
  final String uid;

  Authenticated({required this.uid});
  @override
  List<Object> get props => [uid];
}

class UnAuthenticated extends AuthState {
  @override
  List<Object> get props => [];
}

				
			

auth_cubit.dart

				
					import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
import 'package:instagram_clone_app/features/domain/usecases/firebase_usecases/user/get_current_uid_usecase.dart';
import 'package:instagram_clone_app/features/domain/usecases/firebase_usecases/user/is_sign_in_usecase.dart';
import 'package:instagram_clone_app/features/domain/usecases/firebase_usecases/user/sign_out_usecase.dart';

part 'auth_state.dart';

class AuthCubit extends Cubit<AuthState> {
  final SignOutUseCase signOutUseCase;
  final IsSignInUseCase isSignInUseCase;
  final GetCurrentUidUseCase getCurrentUidUseCase;
  AuthCubit({required this.signOutUseCase, required this.isSignInUseCase, required this.getCurrentUidUseCase}) : super(AuthInitial());

  Future<void> appStarted(BuildContext context) async {
    try {
      bool isSignIn = await isSignInUseCase.call();
      if (isSignIn == true) {

        final uid = await getCurrentUidUseCase.call();
        emit(Authenticated(uid: uid));
      } else {
        emit(UnAuthenticated());
      }
    } catch(_) {
      emit(UnAuthenticated());
    }
  }

  Future<void> loggedIn() async {
    try {
      final uid = await getCurrentUidUseCase.call();
      emit(Authenticated(uid: uid));
    } catch(_) {
      emit(UnAuthenticated());
    }
  }

  Future<void> loggedOut()async {
    try {
      await signOutUseCase.call();
      emit(UnAuthenticated());
    } catch (_) {
      emit(UnAuthenticated());
    }
  }
}

				
			

Credential Cubit:

credential_state.dart

				
					part of 'credential_cubit.dart';

abstract class CredentialState extends Equatable {
  const CredentialState();
}

class CredentialInitial extends CredentialState {
  @override
  List<Object> get props => [];
}

class CredentialLoading extends CredentialState {
  @override
  List<Object> get props => [];
}


class CredentialSuccess extends CredentialState {
  @override
  List<Object> get props => [];
}


class CredentialFailure extends CredentialState {
  @override
  List<Object> get props => [];
}

				
			

credential_cubit.dart

				
					import 'dart:io';

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:instagram_clone_app/features/domain/entities/user/user_entity.dart';
import 'package:instagram_clone_app/features/domain/usecases/firebase_usecases/user/sign_in_user_usecase.dart';
import 'package:instagram_clone_app/features/domain/usecases/firebase_usecases/user/sign_up_user_usecase.dart';

part 'credential_state.dart';

class CredentialCubit extends Cubit<CredentialState> {
  final SignInUserUseCase signInUserUseCase;
  final SignUpUseCase signUpUseCase;
  CredentialCubit({required this.signInUserUseCase, required this.signUpUseCase}) : super(CredentialInitial());

  Future<void> signInUser({required String email, required String password}) async {
    emit(CredentialLoading());
    try {
      await signInUserUseCase.call(UserEntity(email: email, password: password));
      emit(CredentialSuccess());
    } on SocketException catch(_) {
      emit(CredentialFailure());
    } catch (_) {
      emit(CredentialFailure());
    }
  }

  Future<void> signUpUser({required UserEntity user}) async {
    emit(CredentialLoading());
    try {
      await signUpUseCase.call(user);
      emit(CredentialSuccess());
    } on SocketException catch(_) {
      emit(CredentialFailure());
    } catch (_) {
      emit(CredentialFailure());
    }
  }
}

				
			

User Cubit:

Credential_state.dart

				
					part of 'user_cubit.dart';

abstract class UserState extends Equatable {
  const UserState();
}

class UserInitial extends UserState {
  @override
  List<Object> get props => [];
}

class UserLoading extends UserState {
  @override
  List<Object> get props => [];
}


class UserLoaded extends UserState {
  final List<UserEntity> users;

  UserLoaded({required this.users});
  @override
  List<Object> get props => [users];
}


class UserFailure extends UserState {
  @override
  List<Object> get props => [];
}

				
			

credential_cubit.dart

				
					import 'dart:io';

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:instagram_clone_app/features/domain/entities/user/user_entity.dart';
import 'package:instagram_clone_app/features/domain/usecases/firebase_usecases/user/get_users_usecase.dart';
import 'package:instagram_clone_app/features/domain/usecases/firebase_usecases/user/update_user_usecase.dart';

part 'user_state.dart';

class UserCubit extends Cubit<UserState> {
  final UpdateUserUseCase updateUserUseCase;
  final GetUsersUseCase getUsersUseCase;
  UserCubit({required this.updateUserUseCase, required this.getUsersUseCase}) : super(UserInitial());

  Future<void> getUsers({required UserEntity user}) async {
    emit(UserLoading());
    try {
      final streamResponse = getUsersUseCase.call(user);
      streamResponse.listen((users) {
        emit(UserLoaded(users: users));
      });
    } on SocketException catch(_) {
      emit(UserFailure());
    } catch (_) {
      emit(UserFailure());
    }
  }

  Future<void> updateUser({required UserEntity user}) async {
    try {
      await updateUserUseCase.call(user);
    } on SocketException catch(_) {
      emit(UserFailure());
    } catch (_) {
      emit(UserFailure());
    }
  }
}


				
			

Conclusion:

We’ve successfully created Cubits for our User and in the next article we will explore dependency injection and also will call these methods. In order to not miss next video be sure to subscribe to the channel and hit the bell icon to make sure you get notified whenever the next video is uploaded.

Website:
Have any Questions? Find me on

Leave a Reply

You may like