save nd filters for pinhole profile

This commit is contained in:
Vadim 2025-09-03 21:11:04 +02:00
parent 76a6754a3b
commit 488d30e28e
14 changed files with 113 additions and 68 deletions

View file

@ -35,6 +35,7 @@ class EquipmentProfilesProvider extends StatefulWidget {
class EquipmentProfilesProviderState extends State<EquipmentProfilesProvider> { class EquipmentProfilesProviderState extends State<EquipmentProfilesProvider> {
final TogglableMap<EquipmentProfile> _customProfiles = {}; final TogglableMap<EquipmentProfile> _customProfiles = {};
final TogglableMap<PinholeEquipmentProfile> _pinholeCustomProfiles = {};
String _selectedId = ''; String _selectedId = '';
EquipmentProfile get _selectedProfile => EquipmentProfile get _selectedProfile =>
@ -50,6 +51,7 @@ class EquipmentProfilesProviderState extends State<EquipmentProfilesProvider> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return EquipmentProfiles( return EquipmentProfiles(
profiles: context.isPro ? _customProfiles : {}, profiles: context.isPro ? _customProfiles : {},
pinholeProfiles: context.isPro ? _pinholeCustomProfiles : {},
selected: context.isPro ? _selectedProfile : EquipmentProfilesProvider.defaultProfile, selected: context.isPro ? _selectedProfile : EquipmentProfilesProvider.defaultProfile,
child: widget.child, child: widget.child,
); );
@ -58,18 +60,38 @@ class EquipmentProfilesProviderState extends State<EquipmentProfilesProvider> {
Future<void> _init() async { Future<void> _init() async {
_selectedId = widget.storageService.selectedEquipmentProfileId; _selectedId = widget.storageService.selectedEquipmentProfileId;
_customProfiles.addAll(await widget.storageService.getEquipmentProfiles()); _customProfiles.addAll(await widget.storageService.getEquipmentProfiles());
_pinholeCustomProfiles.addAll(await widget.storageService.getPinholeEquipmentProfiles());
_discardSelectedIfNotIncluded(); _discardSelectedIfNotIncluded();
if (mounted) setState(() {}); if (mounted) setState(() {});
widget.onInitialized?.call(); widget.onInitialized?.call();
} }
Future<void> addProfile(EquipmentProfile profile) async { Future<void> addProfile(IEquipmentProfile profile) async {
switch (profile) {
case final PinholeEquipmentProfile profile:
await widget.storageService.addPinholeEquipmentProfile(profile);
_pinholeCustomProfiles[profile.id] = (value: profile, isUsed: true);
case final EquipmentProfile profile:
await widget.storageService.addEquipmentProfile(profile); await widget.storageService.addEquipmentProfile(profile);
_customProfiles[profile.id] = (value: profile, isUsed: true); _customProfiles[profile.id] = (value: profile, isUsed: true);
}
setState(() {}); setState(() {});
} }
Future<void> updateProfile(EquipmentProfile profile) async { Future<void> updateProfile(IEquipmentProfile profile) async {
switch (profile) {
case final PinholeEquipmentProfile profile:
final oldProfile = _pinholeCustomProfiles[profile.id]!.value;
await widget.storageService.updatePinholeEquipmentProfile(
id: profile.id,
name: profile.name,
aperture: oldProfile.aperture.changedOrNull(profile.aperture),
isoValues: oldProfile.isoValues.changedOrNull(profile.isoValues),
lensZoom: oldProfile.lensZoom.changedOrNull(profile.lensZoom),
exposureOffset: oldProfile.exposureOffset.changedOrNull(profile.exposureOffset),
);
_pinholeCustomProfiles[profile.id] = (value: profile, isUsed: _pinholeCustomProfiles[profile.id]!.isUsed);
case final EquipmentProfile profile:
final oldProfile = _customProfiles[profile.id]!.value; final oldProfile = _customProfiles[profile.id]!.value;
await widget.storageService.updateEquipmentProfile( await widget.storageService.updateEquipmentProfile(
id: profile.id, id: profile.id,
@ -82,36 +104,46 @@ class EquipmentProfilesProviderState extends State<EquipmentProfilesProvider> {
exposureOffset: oldProfile.exposureOffset.changedOrNull(profile.exposureOffset), exposureOffset: oldProfile.exposureOffset.changedOrNull(profile.exposureOffset),
); );
_customProfiles[profile.id] = (value: profile, isUsed: _customProfiles[profile.id]!.isUsed); _customProfiles[profile.id] = (value: profile, isUsed: _customProfiles[profile.id]!.isUsed);
}
setState(() {}); setState(() {});
} }
Future<void> deleteProfile(EquipmentProfile profile) async { Future<void> deleteProfile(IEquipmentProfile profile) async {
await widget.storageService.deleteEquipmentProfile(profile.id);
if (profile.id == _selectedId) { if (profile.id == _selectedId) {
_selectedId = EquipmentProfilesProvider.defaultProfile.id; _selectedId = EquipmentProfilesProvider.defaultProfile.id;
widget.storageService.selectedEquipmentProfileId = EquipmentProfilesProvider.defaultProfile.id; widget.storageService.selectedEquipmentProfileId = EquipmentProfilesProvider.defaultProfile.id;
} }
switch (profile) {
case final PinholeEquipmentProfile profile:
await widget.storageService.deletePinholeEquipmentProfile(profile.id);
_pinholeCustomProfiles.remove(profile.id);
case final EquipmentProfile profile:
await widget.storageService.deleteEquipmentProfile(profile.id);
_customProfiles.remove(profile.id); _customProfiles.remove(profile.id);
}
_discardSelectedIfNotIncluded(); _discardSelectedIfNotIncluded();
setState(() {}); setState(() {});
} }
void selectProfile(EquipmentProfile data) { void selectProfile(String id) {
if (_selectedId != data.id) { if (_selectedId != id) {
setState(() { setState(() {
_selectedId = data.id; _selectedId = id;
}); });
widget.storageService.selectedEquipmentProfileId = _selectedProfile.id; widget.storageService.selectedEquipmentProfileId = _selectedProfile.id;
} }
} }
Future<void> toggleProfile(EquipmentProfile profile, bool enabled) async { Future<void> toggleProfile(String id, bool enabled) async {
if (_customProfiles.containsKey(profile.id)) { if (_customProfiles.containsKey(id)) {
_customProfiles[profile.id] = (value: profile, isUsed: enabled); _customProfiles[id] = (value: _customProfiles[id]!.value, isUsed: enabled);
await widget.storageService.updateEquipmentProfile(id: id, isUsed: enabled);
} else if (_pinholeCustomProfiles.containsKey(id)) {
_pinholeCustomProfiles[id] = (value: _pinholeCustomProfiles[id]!.value, isUsed: enabled);
await widget.storageService.updatePinholeEquipmentProfile(id: id, isUsed: enabled);
} else { } else {
return; return;
} }
await widget.storageService.updateEquipmentProfile(id: profile.id, isUsed: enabled);
_discardSelectedIfNotIncluded(); _discardSelectedIfNotIncluded();
setState(() {}); setState(() {});
} }
@ -120,7 +152,7 @@ class EquipmentProfilesProviderState extends State<EquipmentProfilesProvider> {
if (_selectedId == EquipmentProfilesProvider.defaultProfile.id) { if (_selectedId == EquipmentProfilesProvider.defaultProfile.id) {
return; return;
} }
final isSelectedUsed = _customProfiles[_selectedId]?.isUsed ?? false; final isSelectedUsed = _customProfiles[_selectedId]?.isUsed ?? _pinholeCustomProfiles[_selectedId]?.isUsed ?? false;
if (!isSelectedUsed) { if (!isSelectedUsed) {
_selectedId = EquipmentProfilesProvider.defaultProfile.id; _selectedId = EquipmentProfilesProvider.defaultProfile.id;
widget.storageService.selectedEquipmentProfileId = _selectedId; widget.storageService.selectedEquipmentProfileId = _selectedId;
@ -136,38 +168,42 @@ enum _EquipmentProfilesModelAspect {
class EquipmentProfiles extends InheritedModel<_EquipmentProfilesModelAspect> { class EquipmentProfiles extends InheritedModel<_EquipmentProfilesModelAspect> {
final TogglableMap<EquipmentProfile> profiles; final TogglableMap<EquipmentProfile> profiles;
final EquipmentProfile selected; final TogglableMap<PinholeEquipmentProfile> pinholeProfiles;
final IEquipmentProfile selected;
const EquipmentProfiles({ const EquipmentProfiles({
required this.profiles, required this.profiles,
required this.pinholeProfiles,
required this.selected, required this.selected,
required super.child, required super.child,
}); });
/// _default + profiles create by the user /// _default + profiles create by the user
static List<EquipmentProfile> of(BuildContext context) { static List<IEquipmentProfile> of(BuildContext context) {
final model = final model =
InheritedModel.inheritFrom<EquipmentProfiles>(context, aspect: _EquipmentProfilesModelAspect.profiles)!; InheritedModel.inheritFrom<EquipmentProfiles>(context, aspect: _EquipmentProfilesModelAspect.profiles)!;
return List<EquipmentProfile>.from( return List<IEquipmentProfile>.from(
[ [
EquipmentProfilesProvider.defaultProfile, EquipmentProfilesProvider.defaultProfile,
...model.profiles.values.map((p) => p.value), ...model.profiles.values.map((p) => p.value),
...model.pinholeProfiles.values.map((p) => p.value),
], ],
); );
} }
static List<EquipmentProfile> inUseOf(BuildContext context) { static List<IEquipmentProfile> inUseOf(BuildContext context) {
final model = final model =
InheritedModel.inheritFrom<EquipmentProfiles>(context, aspect: _EquipmentProfilesModelAspect.profilesInUse)!; InheritedModel.inheritFrom<EquipmentProfiles>(context, aspect: _EquipmentProfilesModelAspect.profilesInUse)!;
return List<EquipmentProfile>.from( return List<IEquipmentProfile>.from(
[ [
EquipmentProfilesProvider.defaultProfile, EquipmentProfilesProvider.defaultProfile,
...model.profiles.values.where((p) => p.isUsed).map((p) => p.value), ...model.profiles.values.where((p) => p.isUsed).map((p) => p.value),
...model.pinholeProfiles.values.where((p) => p.isUsed).map((p) => p.value),
], ],
); );
} }
static EquipmentProfile selectedOf(BuildContext context) { static IEquipmentProfile selectedOf(BuildContext context) {
return InheritedModel.inheritFrom<EquipmentProfiles>(context, aspect: _EquipmentProfilesModelAspect.selected)! return InheritedModel.inheritFrom<EquipmentProfiles>(context, aspect: _EquipmentProfilesModelAspect.selected)!
.selected; .selected;
} }
@ -180,7 +216,8 @@ class EquipmentProfiles extends InheritedModel<_EquipmentProfilesModelAspect> {
return (dependencies.contains(_EquipmentProfilesModelAspect.selected) && oldWidget.selected != selected) || return (dependencies.contains(_EquipmentProfilesModelAspect.selected) && oldWidget.selected != selected) ||
((dependencies.contains(_EquipmentProfilesModelAspect.profiles) || ((dependencies.contains(_EquipmentProfilesModelAspect.profiles) ||
dependencies.contains(_EquipmentProfilesModelAspect.profilesInUse)) && dependencies.contains(_EquipmentProfilesModelAspect.profilesInUse)) &&
const DeepCollectionEquality().equals(oldWidget.profiles, profiles)); (const DeepCollectionEquality().equals(oldWidget.profiles, profiles) ||
const DeepCollectionEquality().equals(oldWidget.pinholeProfiles, pinholeProfiles)));
} }
} }

View file

@ -165,6 +165,10 @@ class PinholeEquipmentProfileEditBloc extends IEquipmentProfileEditBloc<PinholeE
await _onNameChanged(e, emit); await _onNameChanged(e, emit);
case final EquipmentProfileApertureValuesChangedEvent e: case final EquipmentProfileApertureValuesChangedEvent e:
await _onApertureValuesChanged(e, emit); await _onApertureValuesChanged(e, emit);
case final EquipmentProfileIsoValuesChangedEvent e:
await _onIsoValuesChanged(e, emit);
case final EquipmentProfileNdValuesChangedEvent e:
await _onNdValuesChanged(e, emit);
case final EquipmentProfileLensZoomChangedEvent e: case final EquipmentProfileLensZoomChangedEvent e:
await _onLensZoomChanged(e, emit); await _onLensZoomChanged(e, emit);
case final EquipmentProfileExposureOffsetChangedEvent e: case final EquipmentProfileExposureOffsetChangedEvent e:
@ -181,6 +185,7 @@ class PinholeEquipmentProfileEditBloc extends IEquipmentProfileEditBloc<PinholeE
name: state.profile.name, name: state.profile.name,
aperture: state.profile.aperture, aperture: state.profile.aperture,
isoValues: state.profile.isoValues, isoValues: state.profile.isoValues,
ndValues: state.profile.ndValues,
lensZoom: state.profile.lensZoom, lensZoom: state.profile.lensZoom,
exposureOffset: state.profile.exposureOffset, exposureOffset: state.profile.exposureOffset,
); );
@ -193,6 +198,13 @@ class PinholeEquipmentProfileEditBloc extends IEquipmentProfileEditBloc<PinholeE
Future<void> _onApertureValuesChanged(EquipmentProfileApertureValuesChangedEvent event, Emitter emit) async { Future<void> _onApertureValuesChanged(EquipmentProfileApertureValuesChangedEvent event, Emitter emit) async {
//emitProfile(state.profile.copyWith(apertureValues: event.apertureValues), emit); //emitProfile(state.profile.copyWith(apertureValues: event.apertureValues), emit);
} }
Future<void> _onIsoValuesChanged(EquipmentProfileIsoValuesChangedEvent event, Emitter emit) async {
emitProfile(state.profile.copyWith(isoValues: event.isoValues), emit);
}
Future<void> _onNdValuesChanged(EquipmentProfileNdValuesChangedEvent event, Emitter emit) async {
emitProfile(state.profile.copyWith(ndValues: event.ndValues), emit);
}
Future<void> _onLensZoomChanged(EquipmentProfileLensZoomChangedEvent event, Emitter emit) async { Future<void> _onLensZoomChanged(EquipmentProfileLensZoomChangedEvent event, Emitter emit) async {
emitProfile(state.profile.copyWith(lensZoom: event.lensZoom), emit); emitProfile(state.profile.copyWith(lensZoom: event.lensZoom), emit);

View file

@ -16,7 +16,7 @@ class EquipmentProfileIsoValuesChangedEvent<T extends IEquipmentProfile> extends
const EquipmentProfileIsoValuesChangedEvent(this.isoValues); const EquipmentProfileIsoValuesChangedEvent(this.isoValues);
} }
class EquipmentProfileNdValuesChangedEvent extends IEquipmentProfileEditEvent<EquipmentProfile> { class EquipmentProfileNdValuesChangedEvent<T extends IEquipmentProfile> extends IEquipmentProfileEditEvent<T> {
final List<NdValue> ndValues; final List<NdValue> ndValues;
const EquipmentProfileNdValuesChangedEvent(this.ndValues); const EquipmentProfileNdValuesChangedEvent(this.ndValues);

View file

@ -103,24 +103,19 @@ class _EquipmentProfileEditScreenState<T extends IEquipmentProfile> extends Stat
child: Opacity( child: Opacity(
opacity: state.isLoading ? Dimens.disabledOpacity : Dimens.enabledOpacity, opacity: state.isLoading ? Dimens.disabledOpacity : Dimens.enabledOpacity,
child: Column( child: Column(
children: switch (state.profile) { children: [
EquipmentProfile() => [
_NameFieldBuilder<T>(), _NameFieldBuilder<T>(),
_ApertureValuesListTileBuilder(), if (state.profile is PinholeEquipmentProfile)
_ShutterSpeedValuesListTileBuilder(), const SizedBox.shrink()
else ...[
const _ApertureValuesListTileBuilder(),
const _ShutterSpeedValuesListTileBuilder(),
],
_IsoValuesListTileBuilder<T>(), _IsoValuesListTileBuilder<T>(),
_NdValuesListTileBuilder(), _NdValuesListTileBuilder<T>(),
_LensZoomListTileBuilder<T>(), _LensZoomListTileBuilder<T>(),
_ExposureOffsetListTileBuilder<T>(), _ExposureOffsetListTileBuilder<T>(),
], ],
PinholeEquipmentProfile() => [
_NameFieldBuilder<T>(),
// TODO: Add aperture value list tile for pinhole equipment profile
_IsoValuesListTileBuilder<T>(),
_LensZoomListTileBuilder<T>(),
_ExposureOffsetListTileBuilder<T>(),
],
},
), ),
), ),
), ),
@ -185,12 +180,12 @@ class _IsoValuesListTileBuilder<T extends IEquipmentProfile> extends StatelessWi
} }
} }
class _NdValuesListTileBuilder extends StatelessWidget { class _NdValuesListTileBuilder<T extends IEquipmentProfile> extends StatelessWidget {
const _NdValuesListTileBuilder(); const _NdValuesListTileBuilder();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<EquipmentProfileEditBloc, EquipmentProfileEditState<EquipmentProfile>>( return BlocBuilder<IEquipmentProfileEditBloc<T>, EquipmentProfileEditState<T>>(
builder: (context, state) => FilterListTile<NdValue>( builder: (context, state) => FilterListTile<NdValue>(
icon: Icons.filter_b_and_w_outlined, icon: Icons.filter_b_and_w_outlined,
title: S.of(context).ndFilters, title: S.of(context).ndFilters,
@ -198,7 +193,7 @@ class _NdValuesListTileBuilder extends StatelessWidget {
values: NdValue.values, values: NdValue.values,
selectedValues: state.profile.ndValues, selectedValues: state.profile.ndValues,
onChanged: (value) { onChanged: (value) {
context.read<EquipmentProfileEditBloc>().add(EquipmentProfileNdValuesChangedEvent(value)); context.read<IEquipmentProfileEditBloc<T>>().add(EquipmentProfileNdValuesChangedEvent<T>(value));
}, },
), ),
); );

View file

@ -76,6 +76,7 @@ class _EquipmentProfilesScreenState extends State<EquipmentProfilesScreen> with
name: EquipmentProfilesProvider.defaultProfile.name, name: EquipmentProfilesProvider.defaultProfile.name,
aperture: 22, aperture: 22,
isoValues: EquipmentProfilesProvider.defaultProfile.isoValues, isoValues: EquipmentProfilesProvider.defaultProfile.isoValues,
ndValues: EquipmentProfilesProvider.defaultProfile.ndValues,
), ),
), ),
}, },

View file

@ -74,10 +74,10 @@ class LogbookPhotoEditBloc extends Bloc<LogbookPhotoEditEvent, LogbookPhotoEditS
} }
Future<void> _onEquipmentProfileChanged(LogbookPhotoEquipmentProfileChangedEvent event, Emitter emit) async { Future<void> _onEquipmentProfileChanged(LogbookPhotoEquipmentProfileChangedEvent event, Emitter emit) async {
_newPhoto = _newPhoto.copyWith(equipmentProfileId: Optional(event.equipmentProfile?.id)); _newPhoto = _newPhoto.copyWith(equipmentProfileId: Optional(event.equipmentProfileId));
emit( emit(
state.copyWith( state.copyWith(
equipmentProfileId: Optional(event.equipmentProfile?.id), equipmentProfileId: Optional(event.equipmentProfileId),
canSave: _canSave(), canSave: _canSave(),
), ),
); );

View file

@ -23,9 +23,9 @@ class LogbookPhotoNoteChangedEvent extends LogbookPhotoEditEvent {
} }
class LogbookPhotoEquipmentProfileChangedEvent extends LogbookPhotoEditEvent { class LogbookPhotoEquipmentProfileChangedEvent extends LogbookPhotoEditEvent {
final EquipmentProfile? equipmentProfile; final String? equipmentProfileId;
const LogbookPhotoEquipmentProfileChangedEvent(this.equipmentProfile); const LogbookPhotoEquipmentProfileChangedEvent(this.equipmentProfileId);
} }
class LogbookPhotoFilmChangedEvent extends LogbookPhotoEditEvent { class LogbookPhotoFilmChangedEvent extends LogbookPhotoEditEvent {

View file

@ -273,7 +273,7 @@ class _EquipmentProfilePickerListTile extends StatelessWidget {
selectedValue: EquipmentProfiles.of(context).firstWhereOrNull((e) => e.id == state.equipmentProfileId), selectedValue: EquipmentProfiles.of(context).firstWhereOrNull((e) => e.id == state.equipmentProfileId),
titleAdapter: (value) => value.name, titleAdapter: (value) => value.name,
onChanged: (value) { onChanged: (value) {
context.read<LogbookPhotoEditBloc>().add(LogbookPhotoEquipmentProfileChangedEvent(value.value)); context.read<LogbookPhotoEditBloc>().add(LogbookPhotoEquipmentProfileChangedEvent(value.value?.id));
}, },
), ),
); );

View file

@ -19,7 +19,7 @@ class MeasureEvent extends ScreenEvent {
} }
class EquipmentProfileChangedEvent extends ScreenEvent { class EquipmentProfileChangedEvent extends ScreenEvent {
final EquipmentProfile profile; final IEquipmentProfile profile;
const EquipmentProfileChangedEvent(this.profile); const EquipmentProfileChangedEvent(this.profile);
} }

View file

@ -21,7 +21,7 @@ class MeasureState extends SourceState {
} }
class EquipmentProfileChangedState extends SourceState { class EquipmentProfileChangedState extends SourceState {
final EquipmentProfile profile; final IEquipmentProfile profile;
const EquipmentProfileChangedState(this.profile); const EquipmentProfileChangedState(this.profile);
} }

View file

@ -10,13 +10,13 @@ class EquipmentProfilePicker extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AnimatedDialogPicker<EquipmentProfile>( return AnimatedDialogPicker<IEquipmentProfile>(
icon: Icons.camera_alt_outlined, icon: Icons.camera_alt_outlined,
title: S.of(context).equipmentProfile, title: S.of(context).equipmentProfile,
selectedValue: EquipmentProfiles.selectedOf(context), selectedValue: EquipmentProfiles.selectedOf(context),
values: EquipmentProfiles.inUseOf(context), values: EquipmentProfiles.inUseOf(context),
itemTitleBuilder: (_, value) => Text(value.id.isEmpty ? S.of(context).none : value.name), itemTitleBuilder: (_, value) => Text(value.id.isEmpty ? S.of(context).none : value.name),
onChanged: EquipmentProfilesProvider.of(context).selectProfile, onChanged: (profile) => EquipmentProfilesProvider.of(context).selectProfile(profile.id),
closedChild: ReadingValueContainer.singleValue( closedChild: ReadingValueContainer.singleValue(
value: ReadingValue( value: ReadingValue(
label: S.of(context).equipmentProfile, label: S.of(context).equipmentProfile,

View file

@ -5,7 +5,7 @@ sealed class MeteringEvent {
} }
class EquipmentProfileChangedEvent extends MeteringEvent { class EquipmentProfileChangedEvent extends MeteringEvent {
final EquipmentProfile equipmentProfileData; final IEquipmentProfile equipmentProfileData;
const EquipmentProfileChangedEvent(this.equipmentProfileData); const EquipmentProfileChangedEvent(this.equipmentProfileData);
} }

View file

@ -3,7 +3,7 @@ import 'package:lightmeter/providers/equipment_profile_provider.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
class EquipmentProfileListener extends StatefulWidget { class EquipmentProfileListener extends StatefulWidget {
final ValueChanged<EquipmentProfile> onDidChangeDependencies; final ValueChanged<IEquipmentProfile> onDidChangeDependencies;
final Widget child; final Widget child;
const EquipmentProfileListener({ const EquipmentProfileListener({

View file

@ -34,7 +34,7 @@ class MeteringScreenLayoutListTile extends StatelessWidget {
.toList(growable: false), .toList(growable: false),
onSave: (value) { onSave: (value) {
if (!value[MeteringScreenLayoutFeature.equipmentProfiles]!) { if (!value[MeteringScreenLayoutFeature.equipmentProfiles]!) {
EquipmentProfilesProvider.of(context).selectProfile(EquipmentProfiles.of(context).first); EquipmentProfilesProvider.of(context).selectProfile(EquipmentProfiles.of(context).first.id);
} }
if (!value[MeteringScreenLayoutFeature.filmPicker]!) { if (!value[MeteringScreenLayoutFeature.filmPicker]!) {
FilmsProvider.of(context).selectFilm(const FilmStub()); FilmsProvider.of(context).selectFilm(const FilmStub());