Application Layer
The application layer contains the state management logic of the feature. The application layer is responsible for managing the state of the feature and handling the business logic.
Getting started
Create a new folder named application
inside the feature folder.
Feature Cubit
Use the Bloc
extension in VSCode to create a new cubit:
Right-click on the
application
folder.Select
Bloc: New Cubit
.Enter the name of the cubit. For example,
scratch_note_cubit
.
Writing the cubit
We'll start with the state class first and then write the cubit class. Extend the scratch_note_cubit.dart
file with the following code:
part of 'scratch_note_cubit.dart';
@freezed
class ScratchNoteState with _$ScratchNoteState {
const factory ScratchNoteState({
required List<ScratchNote> notes,
required bool isLoading,
required bool isSaving,
required bool isDeleting,
required Failure failure,
}) = _ScratchNoteState;
factory ScratchNoteState.initial() => _ScratchNoteState(
notes: [],
isLoading: false,
isSaving: false,
isDeleting: false,
failure: Failure.none(),
);
}
The state class contains the following properties:
notes
: A list ofScratchNote
objects.isLoading
: A boolean flag to indicate loading state.isSaving
: A boolean flag to indicate saving state.isDeleting
: A boolean flag to indicate deleting state.failure
: AFailure
object to handle errors.
It also contains the following factory constructor:
initial
: A factory constructor to create the initial state. It initializes the state with empty notes, and sets all flags tofalse
. This will be the default state of the cubit when user opens the feature.
Next, extend the scratch_note_cubit.dart
file with the following code:
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fpdart/fpdart.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:techshoi_app/features/core/domain/entities/failure.dart';
import 'package:techshoi_app/features/scratch_notes/domain/i_scratch_note_repository.dart';
import 'package:techshoi_app/features/scratch_notes/domain/scratch_note.dart';
part 'scratch_note_state.dart';
part 'scratch_note_cubit.freezed.dart';
class ScratchNoteCubit extends Cubit<ScratchNoteState> {
ScratchNoteCubit(this._repo) : super(ScratchNoteState.initial());
final IScratchNoteRepository _repo;
StreamSubscription<Either<Failure, List<ScratchNote>>>? _notesSubscription;
Future<void> createScratchNote({required String title}) async {
emit(state.copyWith(isSaving: true));
final result = await _repo.addScratchNote(title);
result.fold(
(failure) => emit(
state.copyWith(
isSaving: false,
failure: failure,
),
),
(scratchNote) => emit(
state.copyWith(
isSaving: false,
failure: Failure.none(),
),
),
);
}
Future<void> updateScratchNote(ScratchNote updatedNote) async {
emit(state.copyWith(isSaving: true));
final result = await _repo.updateScratchNote(updatedNote);
result.fold(
(failure) => emit(
state.copyWith(
isSaving: false,
failure: failure,
),
),
(scratchNote) => emit(
state.copyWith(
isSaving: false,
failure: Failure.none(),
),
),
);
}
Future<void> deleteScratchNote(String noteId) async {
emit(state.copyWith(isDeleting: true));
final result = await _repo.deleteScratchNote(noteId);
result.fold(
(failure) => emit(
state.copyWith(
isDeleting: false,
failure: failure,
),
),
(scratchNote) => emit(
state.copyWith(
isDeleting: false,
failure: Failure.none(),
),
),
);
}
Future<void> getScratchNotes() async {
emit(state.copyWith(isLoading: true));
final result = await _repo.getScratchNotes();
result.fold(
(failure) => emit(
state.copyWith(
isLoading: false,
failure: failure,
),
),
(scratchNotes) => emit(
state.copyWith(
isLoading: false,
failure: Failure.none(),
notes: scratchNotes,
),
),
);
}
Future<void> listenForChanges() async {
emit(state.copyWith(isLoading: true));
_notesSubscription = _repo.listenToScratchNotes().listen(
(failureOrNotes) {
failureOrNotes.fold(
(failure) => emit(
state.copyWith(
isLoading: false,
failure: failure,
),
),
(scratchNotes) => emit(
state.copyWith(
isLoading: false,
failure: Failure.none(),
notes: scratchNotes,
),
),
);
},
);
}
void flush() {
emit(ScratchNoteState.initial());
}
@override
Future<void> close() {
_notesSubscription?.cancel();
return super.close();
}
}
The ScratchNoteCubit
class extends Cubit<ScratchNoteState>
. It contains the following methods:
createScratchNote
: Adds a new scratch note.updateScratchNote
: Updates an existing scratch note.deleteScratchNote
: Deletes a scratch note.getScratchNotes
: Retrieves a list of scratch notes.listenForChanges
: Listens to changes in the scratch notes.flush
: Resets the state to the initial state.close
: Cancels the subscription when the cubit is closed._notesSubscription
: A private subscription to listen for changes in the scratch notes._repo
: An instance of theIScratchNoteRepository
interface.
As you can see, the cubit takes an instance of the IScratchNoteRepository
interface in the constructor. This interface only contains the abstract methods for interacting with scratch notes. The actual implementation of these methods will be in the infrastructure layer.
Next, we'll move on to the infrastructure layer to implement the repository interface.