I have a page that needs two different API calls.
I am applying the clean architecture to write the code and Riverpod as State Management. Then I am using the Freezed package to map the different states.
How can I combine the different states?
What I would like to achieve is to emit a success state only if both states give me data or emit an error state if one of them is an error, otherwise loading state.
Thanks in advance.
These are the two State classes:
import 'package:freezed_annotation/freezed_annotation.dart';
import '...eatures/profile/domain/entities/user_profile_entity.dart';
part 'user_profile_details_state.freezed.dart';
@freezed
class UserProfileDetailsState with _$UserProfileDetailsState {
///Initial
const factory UserProfileDetailsState.initial() =
_UserProfileDetailsStateInitial;
///Loading
const factory UserProfileDetailsState.loading() =
_UserProfileDetailsStateLoading;
///Data
const factory UserProfileDetailsState.data(
{required ProfileEntity profileEntity}) = _UserProfileDetailsStateData;
///Error
const factory UserProfileDetailsState.error([String? error]) =
_UserProfileDetailsStateError;
}
import 'package:freezed_annotation/freezed_annotation.dart';
import '....features/profile/domain/entities/user_properties_entity.dart';
part 'user_properties_state.freezed.dart';
@freezed
class UserPropertiesState with _$UserPropertiesState {
///Initial
const factory UserPropertiesState.initial() = _UserPropertiesStateInitial;
///Loading
const factory UserPropertiesState.loading() = _UserPropertiesStateLoading;
///Data
const factory UserPropertiesState.data(
{required UserPropertiesEntity userPropertiesEntity}) =
_UserPropertiesStateData;
///Error
const factory UserPropertiesState.error([String? error]) =
_UserPropertiesStateError;
}
And these are the two notifiers:
import '...core/di/dependency_injection.dart';
import '...core/errors/failures.dart';
import '...core/presentation/riverpod/check_token_notifier.dart';
import '...features/profile/presentation/riverpod/user_profile_details_state.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'user_profile_details_notifier.g.dart';
@riverpod
class UserProfileDetailsNotifier extends _$UserProfileDetailsNotifier {
@override
UserProfileDetailsState build() {
getUserProfileDetailsData();
return const UserProfileDetailsState.initial();
}
Future<void> getUserProfileDetailsData() async {
state = const UserProfileDetailsState.loading();
final userProfileDetailsOrFailure = await ref
.read(userProfileDetailsUseCaseProvider)
.getUserProfileDetailsData();
userProfileDetailsOrFailure.fold((error) {
if (error is TokenFailure) {
ref.read(checkTokenNotifierProvider.notifier).deAuthUser();
return;
}
state = UserProfileDetailsState.error(error.errorMessage);
}, (userProfileDetailsEntity) {
state =
UserProfileDetailsState.data(profileEntity: userProfileDetailsEntity);
});
}
}
import '...core/di/dependency_injection.dart';
import '...core/errors/failures.dart';
import '...core/presentation/riverpod/check_token_notifier.dart';
import '...features/profile/presentation/riverpod/user_properties_state.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'user_properties_notifier.g.dart';
@riverpod
class UserPropertiesNotifier extends _$UserPropertiesNotifier {
@override
UserPropertiesState build() {
getUserPropertiesData();
return const UserPropertiesState.initial();
}
Future<void> getUserPropertiesData() async {
state = const UserPropertiesState.loading();
final userPropertiesOrFailure =
await ref.read(userPropertiesUseCaseProvider).getUserPropertiesData();
userPropertiesOrFailure.fold((error) {
if (error is TokenFailure) {
ref.read(checkTokenNotifierProvider.notifier).deAuthUser();
return;
}
state = UserPropertiesState.error(error.errorMessage);
}, (userPropertiesEntity) {
state =
UserPropertiesState.data(userPropertiesEntity: userPropertiesEntity);
});
}
}
2
Answers
You can create a separate @freezed class where the corresponding new fields will be collected based on your two states. In code, you can do this:
You can use the required fields in
whenOrNull
, or split into ifsTo combine the states of two different notifiers and emit a success state only if both states have data or emit an error state if one of them is an error, you can use ProviderContainer to listen to both states and update a new state accordingly. Here’s an example of how you can achieve this: First, create a new state class that represents the combined state:
Next, create a new notifier that combines the states of both UserProfileDetailsNotifier and UserPropertiesNotifier:
Finally, you can use the CombinedNotifier in your UI to access the combined state: