- Introduction
- Project Setup
- Dependencies
- Service Locator Setup
- Cubit and Bloc
- Running the Project
- Testing
- Credentials
This document provides guidelines for setting up and testing the LoginCubit
and TodoBloc
in a Flutter project using the Bloc pattern, GetIt
for dependency injection, and mockito
for mocking dependencies. This is designed for a senior Flutter developer position.
Clone the repository and navigate to the project directory:
git clone https://github.com/dola99/maids.git
cd maids
Install the dependencies:
flutter pub get
flutter_bloc
: For state management using the Bloc pattern.equatable
: To make Dart objects comparable, useful for comparing Bloc states.get_it
: A service locator for dependency injection.http
: For making HTTP requests in theNetworkService
.dartz
: Provides functional programming tools for Dart.
flutter_test
: The testing framework for Flutter applications.mockito
: For creating mock objects in unit tests.build_runner
: For generating mock classes withmockito
.
- Centralized Dependency Management: Manage all dependencies from a single place.
- Lazy Initialization: Instances are created only when requested, improving startup time and resource usage.
- Separation of Concerns: Decouples dependency creation from business logic, leading to cleaner code.
import 'package:get_it/get_it.dart';
import 'package:maids_task/core/helpers/secure_sharedprefernce.dart';
import 'package:maids_task/core/network/network_layer.dart';
import 'package:maids_task/core/network/service_urls.dart';
import 'package:maids_task/features/todo/repos/todo_repo_imb.dart';
import 'package:maids_task/features/login/repo/login_repo_imb.dart';
final serviceLocator = GetIt.instance;
void setupServiceLocator() {
serviceLocator.registerLazySingleton(
() => NetworkService(baseUrl: ServicesUrl.baseUrl));
serviceLocator.registerLazySingleton(() => SharedPreferencesHelper()..init());
serviceLocator.registerLazySingleton(
() => LoginRepoImb(networkService: serviceLocator<NetworkService>()));
serviceLocator.registerLazySingleton(
() => TodoRepoImb(networkService: serviceLocator<NetworkService>()));
}
The LoginCubit
handles the login logic, managing states like loading, success, and failure.
The TodoBloc
handles various actions related to todos, such as adding, deleting, updating, and loading todos. It uses mixins to separate the logic for each action.
- Modularity: Mixins allow you to split complex logic into smaller, more manageable pieces. Each mixin can encapsulate related functionality, making the code more modular.
- Reusability: Mixins promote code reusability. By separating logic into mixins, you can easily reuse the same mixin in different Blocs or even in different projects.
- Maintainability: Separating logic into mixins makes the code easier to maintain. Each mixin has a single responsibility, adhering to the Single Responsibility Principle (SRP), which simplifies debugging and enhances readability.
- Testability: Isolating logic into mixins makes unit testing more straightforward. You can test each mixin independently, ensuring that the individual pieces of logic work correctly before integrating them into the Bloc.
Example Mixins:
mixin AddTodoMixin on Bloc<TodoEvent, TodoState> {
Future<void> onAddTodo(AddTodo event, Emitter<TodoState> emit,
TodoRepository todoRepository, int todosPerPage) async {
final result = await todoRepository.addTodo(event.todo);
if (emit.isDone) return;
await result.fold(
(failure) async => emit(TodoError(failure)),
(newTodo) async {
event.todoList.insert(0, newTodo);
emit(TodoLoaded(
event.todoList,
));
},
);
}
}
mixin DeleteTodoMixin on Bloc<TodoEvent, TodoState> {
Future<void> onDeleteTodo(DeleteTodo event, Emitter<TodoState> emit,
TodoRepository todoRepository, int todosPerPage) async {
final result = await todoRepository.deleteTodo(event.id);
if (emit.isDone) return;
await result.fold(
(failure) async => emit(TodoError(failure)),
(_) async {
event.todoList.removeAt(event.index);
emit(TodoLoaded(
event.todoList,
));
},
);
}
}
mixin UpdateTodoMixin on Bloc<TodoEvent, TodoState> {
Future<void> onUpdateTodo(UpdateTodo event, Emitter<TodoState> emit,
TodoRepository todoRepository, int todosPerPage) async {
final result = await todoRepository.updateTodo(event.todo);
if (emit.isDone) return;
await result.fold(
(failure) async => emit(TodoError(failure)),
(newTodo) async {
event.todoList[event.index] = newTodo;
emit(TodoLoaded(
event.todoList,
));
},
);
}
}
To run the project, ensure you have a Flutter development environment set up. Then, execute:
flutter run
- Ensure Correct State Transitions: Verify that the Cubit and Bloc emit the correct states.
- Verify Business Logic: Ensure the business logic works as expected.
- Mock Dependencies: Use
mockito
to mock dependencies and test the Cubit and Bloc in isolation.
Generate mock classes using build_runner
:
flutter pub run build_runner build
Create a test file named login_cubit_test.dart
and write tests to ensure the LoginCubit
emits the correct states.
Create a test file named todo_bloc_test.dart
and write tests to ensure the TodoBloc
handles events correctly and emits the expected states.
To run the tests, use the following command:
flutter test
To log in to the application, use the following credentials:
- Username:
emilys
- Password:
emilyspass