diff --git a/lib/providers/metering_screen_layout_provider.dart b/lib/providers/metering_screen_layout_provider.dart index 83e3773..3c0cc14 100644 --- a/lib/providers/metering_screen_layout_provider.dart +++ b/lib/providers/metering_screen_layout_provider.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/data/models/metering_screen_layout_config.dart'; import 'package:lightmeter/data/shared_prefs_service.dart'; +import 'package:lightmeter/utils/inherited_generics.dart'; import 'package:provider/provider.dart'; +typedef MeteringScreenLayout = InheritedModelBase; + class MeteringScreenLayoutProvider extends StatefulWidget { final Widget child; @@ -22,8 +25,8 @@ class MeteringScreenLayoutProviderState extends State( + data: MeteringScreenLayoutConfig.from(_config), child: widget.child, ); } @@ -41,42 +44,3 @@ class MeteringScreenLayoutProviderState extends State().meteringScreenLayout = _config; } } - -class MeteringScreenLayout extends InheritedModel { - final MeteringScreenLayoutConfig config; - - const MeteringScreenLayout({ - required this.config, - required super.child, - super.key, - }); - - static MeteringScreenLayoutConfig of(BuildContext context, {bool listen = true}) { - if (listen) { - return context.dependOnInheritedWidgetOfExactType()!.config; - } else { - return context.findAncestorWidgetOfExactType()!.config; - } - } - - static bool featureStatusOf(BuildContext context, MeteringScreenLayoutFeature feature) { - return InheritedModel.inheritFrom(context, aspect: feature)! - .config[feature]!; - } - - @override - bool updateShouldNotify(MeteringScreenLayout oldWidget) => true; - - @override - bool updateShouldNotifyDependent( - MeteringScreenLayout oldWidget, - Set dependencies, - ) { - for (final dependecy in dependencies) { - if (oldWidget.config[dependecy] != config[dependecy]) { - return true; - } - } - return false; - } -} diff --git a/lib/screens/metering/components/camera_container/widget_container_camera.dart b/lib/screens/metering/components/camera_container/widget_container_camera.dart index d107ebd..2f8ef45 100644 --- a/lib/screens/metering/components/camera_container/widget_container_camera.dart +++ b/lib/screens/metering/components/camera_container/widget_container_camera.dart @@ -56,14 +56,14 @@ class CameraContainer extends StatelessWidget { topBarOverflow += Dimens.readingContainerSingleValueHeight; topBarOverflow += Dimens.paddingS; } - if (MeteringScreenLayout.featureStatusOf( + if (MeteringScreenLayout.featureOf( context, MeteringScreenLayoutFeature.extremeExposurePairs, )) { topBarOverflow += Dimens.readingContainerDoubleValueHeight; topBarOverflow += Dimens.paddingS; } - if (MeteringScreenLayout.featureStatusOf( + if (MeteringScreenLayout.featureOf( context, MeteringScreenLayoutFeature.filmPicker, )) { diff --git a/lib/screens/metering/components/shared/readings_container/widget_container_readings.dart b/lib/screens/metering/components/shared/readings_container/widget_container_readings.dart index 9edf2e7..688d932 100644 --- a/lib/screens/metering/components/shared/readings_container/widget_container_readings.dart +++ b/lib/screens/metering/components/shared/readings_container/widget_container_readings.dart @@ -42,7 +42,7 @@ class ReadingsContainer extends StatelessWidget { const _EquipmentProfilePicker(), const _InnerPadding(), ], - if (MeteringScreenLayout.featureStatusOf( + if (MeteringScreenLayout.featureOf( context, MeteringScreenLayoutFeature.extremeExposurePairs, )) ...[ @@ -60,7 +60,7 @@ class ReadingsContainer extends StatelessWidget { ), const _InnerPadding(), ], - if (MeteringScreenLayout.featureStatusOf( + if (MeteringScreenLayout.featureOf( context, MeteringScreenLayoutFeature.filmPicker, )) ...[ diff --git a/lib/screens/metering/screen_metering.dart b/lib/screens/metering/screen_metering.dart index 99ca16c..51d33b9 100644 --- a/lib/screens/metering/screen_metering.dart +++ b/lib/screens/metering/screen_metering.dart @@ -7,7 +7,6 @@ import 'package:lightmeter/data/models/metering_screen_layout_config.dart'; import 'package:lightmeter/environment.dart'; import 'package:lightmeter/providers/equipment_profile_provider.dart'; import 'package:lightmeter/providers/ev_source_type_provider.dart'; -import 'package:lightmeter/providers/metering_screen_layout_provider.dart'; import 'package:lightmeter/screens/metering/bloc_metering.dart'; import 'package:lightmeter/screens/metering/components/bottom_controls/provider_bottom_controls.dart'; import 'package:lightmeter/screens/metering/components/camera_container/provider_container_camera.dart'; @@ -31,17 +30,11 @@ class _MeteringScreenState extends State { void didChangeDependencies() { super.didChangeDependencies(); _bloc.add(EquipmentProfileChangedEvent(EquipmentProfile.of(context))); - if (!MeteringScreenLayout.featureStatusOf(context, MeteringScreenLayoutFeature.filmPicker)) { - _bloc.add(const FilmChangedEvent(Film.other())); - } } @override Widget build(BuildContext context) { - return InheritedWidgetListener( - onDidChangeDependencies: (value) { - context.read().add(StopTypeChangedEvent(value)); - }, + return _InheritedListeners( child: Scaffold( backgroundColor: Theme.of(context).colorScheme.background, body: Column( @@ -64,8 +57,8 @@ class _MeteringScreenState extends State { BlocBuilder( builder: (context, state) => MeteringBottomControlsProvider( ev: state is MeteringDataState ? state.ev : null, - isMetering: - state is LoadingState || state is MeteringDataState && state.continuousMetering, + isMetering: state is LoadingState || + state is MeteringDataState && state.continuousMetering, hasError: state is MeteringDataState && state.hasError, onSwitchEvSourceType: context.read().hasLightSensor ? EvSourceTypeProvider.of(context).toggleType @@ -81,6 +74,28 @@ class _MeteringScreenState extends State { } } +class _InheritedListeners extends StatelessWidget { + final Widget child; + + const _InheritedListeners({required this.child}); + + @override + Widget build(BuildContext context) { + return InheritedWidgetListener( + onDidChangeDependencies: (value) { + context.read().add(StopTypeChangedEvent(value)); + }, + child: InheritedModelAspectListener( + aspect: MeteringScreenLayoutFeature.filmPicker, + onDidChangeDependencies: (value) { + if (!value) context.read().add(const FilmChangedEvent(Film.other())); + }, + child: child, + ), + ); + } +} + class _MeteringContainerBuidler extends StatelessWidget { final ExposurePair? fastest; final ExposurePair? slowest; diff --git a/lib/utils/inherited_generics.dart b/lib/utils/inherited_generics.dart index 026f564..5f71ec6 100644 --- a/lib/utils/inherited_generics.dart +++ b/lib/utils/inherited_generics.dart @@ -62,45 +62,6 @@ class InheritedWidgetBase extends InheritedWidget { bool updateShouldNotify(InheritedWidgetBase oldWidget) => true; } -// class InheritedModelBase extends InheritedModel { -// final Map data; - -// const InheritedModelBase({ -// required this.data, -// required super.child, -// super.key, -// }); - -// static Map of(BuildContext context, {bool listen = true}) { -// if (listen) { -// return context.dependOnInheritedWidgetOfExactType>()!.data; -// } else { -// return context.findAncestorWidgetOfExactType>()!.data; -// } -// } - -// static R featureStatusOf(BuildContext context, A aspect) { -// return InheritedModel.inheritFrom>(context, aspect: aspect)! -// .data[aspect]!; -// } - -// @override -// bool updateShouldNotify(InheritedModelBase oldWidget) => true; - -// @override -// bool updateShouldNotifyDependent( -// InheritedModelBase oldWidget, -// Set dependencies, -// ) { -// for (final dependecy in dependencies) { -// if (oldWidget.data[dependecy] != data[dependecy]) { -// return true; -// } -// } -// return false; -// } -// } - extension InheritedWidgetBaseContext on BuildContext { T get() { return InheritedWidgetBase.of(this, listen: false); @@ -110,3 +71,101 @@ extension InheritedWidgetBaseContext on BuildContext { return InheritedWidgetBase.of(this); } } + +/// Listening to multiple dependencies at the same time causes firing an event for all dependencies +/// even though some of them didn't change: +/// ```dart +/// @override +/// void didChangeDependencies() { +/// super.didChangeDependencies(); +/// _bloc.add(EquipmentProfileChangedEvent(EquipmentProfile.of(context))); +/// if (!MeteringScreenLayout.featureStatusOf(context, MeteringScreenLayoutFeature.filmPicker)) { +/// _bloc.add(const FilmChangedEvent(Film.other())); +/// } +/// } +/// ``` +/// To overcome this issue I've decided to create a generic listener, +/// that will listen to each dependency separately. +class InheritedModelAspectListener extends StatefulWidget { + final A aspect; + final ValueChanged onDidChangeDependencies; + final Widget child; + + const InheritedModelAspectListener({ + required this.aspect, + required this.onDidChangeDependencies, + required this.child, + super.key, + }); + + @override + State> createState() => + _InheritedModelAspectListenerState(); +} + +class _InheritedModelAspectListenerState + extends State> { + @override + void didChangeDependencies() { + super.didChangeDependencies(); + widget.onDidChangeDependencies(context.listenModelFeature(widget.aspect)); + } + + @override + Widget build(BuildContext context) { + return widget.child; + } +} + +class InheritedModelBase extends InheritedModel { + final Map data; + + const InheritedModelBase({ + required this.data, + required super.child, + super.key, + }); + + static Map of(BuildContext context, {bool listen = true}) { + if (listen) { + return context.dependOnInheritedWidgetOfExactType>()!.data; + } else { + return context.findAncestorWidgetOfExactType>()!.data; + } + } + + static T featureOf(BuildContext context, A aspect) { + return InheritedModel.inheritFrom>(context, aspect: aspect)! + .data[aspect]!; + } + + @override + bool updateShouldNotify(InheritedModelBase oldWidget) => true; + + @override + bool updateShouldNotifyDependent( + InheritedModelBase oldWidget, + Set dependencies, + ) { + for (final dependecy in dependencies) { + if (oldWidget.data[dependecy] != data[dependecy]) { + return true; + } + } + return false; + } +} + +extension InheritedModelBaseContext on BuildContext { + Map getModel() { + return InheritedModelBase.of(this, listen: false); + } + + Map listenModel() { + return InheritedModelBase.of(this); + } + + T listenModelFeature(A aspect) { + return InheritedModelBase.featureOf(this, aspect); + } +}