bloc
bloc copied to clipboard
feat: Add a `selectWhen` option to filter selections
Description
I am using union types as my State classes. I want to select a value only for one of the union types, since the value I want to select only exists for that union type. I don't want to add the same value into all other types, so they will all share the same values.
Desired Solution
A selectWhen option could filter out selections and only allow them to run when we return true. This way I can check of the state if of the correct type before the selection and build happen.
Hi @feinstein 👋 Thanks for opening an issue!
Can you please provide a bit more context regarding the use-case you have in mind? A sample or code snippet that illustrates what you’re trying to achieve would help me better understand the problem you’re facing, thanks!
Hi @felangel, thanks for the support.
I am a bit new to bloc and want to use it with freezed. My first approach was something like this:
import 'package:bloc/bloc.dart';
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'home_cubit.freezed.dart';
class HomeCubit extends Cubit<HomeState> {
HomeCubit({
required this.homeRepository,
}) : super(const HomeState.loading());
@visibleForTesting
final HomeRepository homeRepository;
@visibleForTesting
String pathToClone = '';
@visibleForTesting
String repoUrl = '';
@visibleForTesting
Set<String> emails = {};
bool get canLoadEmails => repoUrl.isNotEmpty && pathToClone.isNotEmpty;
Future<void> loadEmails() async {
try {
emit(const HomeState.loading());
final emails = await homeRepository.loadEmailsFromRepository(
repositoryPath: pathToClone,
repositoryUrl: repoUrl,
);
emit(HomeState.filling(
pathToClone: pathToClone,
repoUrl: repoUrl,
canLoadEmails: canLoadEmails,
emails: emails,
));
} catch (error, stacktrace) {
log.severe('Error while trying to load HomeCubit', error, stacktrace);
emit(const HomeState.error());
}
}
...
Future<void> openClonePathFilePicker() async {
final pathToClone = await getDirectoryPath();
if (pathToClone != null) {
repoPathChanged(pathToClone);
}
}
}
@freezed
sealed class HomeState with _$HomeState {
const factory HomeState.loading() = HomeStateLoading;
const factory HomeState.filling({
required String pathToClone,
required String repoUrl,
required bool canLoadEmails,
required Set<String> emails,
}) = HomeStateFilling;
const factory HomeState.error() = HomeStateError;
}
So as you can see there are different types of state, each with it's own properties. Since my Flutter code uses TextField, I want to select the rebuilds, otherwise I will be rebuilding everything at each text change from the TextFields (the changes are forwarded to the cubit, and the cubit emits new states with the new text).
Since only the filling state has the text for the TextFields, I was wondering if a selectWhen could be used to avoid rebuiding the TextField when the state is not filling. This way, if I emit an error state, the TextField widget will still hold the last text that was emitted, since it won't be rebuild.
To be fair, I am considering abandoning this state modeling, because having these different state types, with different variables is making development more complex, this generates many edge-cases, then we need to copy variables between state types, so I thinking on only using one state type with all possible variables, and this should remove the need for a selectWhen for me.... but still could be a good addition to the API, for others?
What do you think? Would love to hear your feedback.
@feinstein you should be able to use context.select to only trigger rebuilds when the selected property changes. Sounds like you restructured your bloc and this is no longer and issue so I prefer to close this for now but if this is still an issue or if you want to discuss further just let me know and I'm happy to continue the conversation 👍