bloc_lens 0.2.1
bloc_lens: ^0.2.1 copied to clipboard
Lenses and extensions based on lens_base, made for the bloc package.
Functional lenses for Dart & BLoC #
To read the basic documentation & the rationale, please visit the lens_base package.
This package provides a set of BLoC-specific classes and extensions.
Usage #
Let's say you have a BLoC with many properties, and you want an easy way to modify these values independently. Normally, you would have to create separate methods for each property, like this:
class SettingsCubit extends Cubit<SettingsState> {
SettingsCubit() : super(SettingsState());
void setScaling(double scaling) {
emit(state.copyWith(scaling: scaling));
}
void setHapticFeedback(bool hapticFeedback) {
emit(state.copyWith(hapticFeedback: hapticFeedback));
}
void setThemeMode(ThemeMode themeMode) {
emit(state.copyWith(themeMode: themeMode));
}
void setLocale(Locale locale) {
emit(state.copyWith(locale: locale));
}
void setFontSize(double fontSize) {
emit(state.copyWith(fontSize: fontSize));
}
}
class SettingsState {
const SettingsState({
required this.scaling,
required this.hapticFeedback,
required this.themeMode,
required this.locale,
required this.fontSize,
});
final double scaling;
final bool hapticFeedback;
final ThemeMode themeMode;
final Locale locale;
final double fontSize;
}
But that separates the getter
and setter
for each property, which forces you
to pass around the current value, the way to change it, and possibly some more
information (like the list of allowed values, or the allowed range). With lenses,
you have one object that fully manages the value, together with its constraints:
โ Without lenses | โ With lenses |
---|---|
|
|
To define lenses inside BLoCs, you can use the extensions provided by this package:
class SettingsCubit extends Cubit<SettingsState> {
SettingsCubit() : super(SettingsState());
late final scaling = numberLens(
get: () => state.scaling,
set: (value) => state.copyWith(scaling: value),
min: 0.5,
max: 2.0,
increment: 0.1,
);
late final themeMode = enumLens(
get: () => state.themeMode,
set: (value) => state.copyWith(themeMode: value),
values: ThemeMode.values,
);
...
}
You can then use these inside your widgets:
Widget build(BuildContext context) {
final cubit = context.watch<SettingsCubit>();
return MySlider(
lens: cubit.scaling,
);
}
If you're also using flutter_hooks
, you can include in your project a hook that
combines finding the bloc and listening to the specific changes:
@optionalTypeArgs
L useBlocLens<B extends BlocBase<S>, S, L extends BlocLens<S, T>, T>(
L Function(B cubit) lensGetter, {
bool listen = true,
}) {
final cubit = useContext().read<B>();
final lens = lensGetter(cubit);
useStream(
listen ? cubit.stream.map((_) => lens.get()).distinct() : null,
);
return lens;
}
Widget build(BuildContext context) {
final scaling = useBlocLens((SettingsCubit cubit) => cubit.scaling);
return MySlider(
lens: scaling,
);
}
Please note, that this hook is not provided out-of-the-box in the package to keep the dependencies Flutter-less.
For an example of how to use lenses in a sample Flutter app, check out the example app.
๐งช Experimental #
If you want to preview an even easier way of using lenses inside BLoCs without any boilerplate, check out the
bloc_lens_macros
package.
Built with โ๏ธ by LeanCode