diff --git a/lib/screens/metering/bloc_metering.dart b/lib/screens/metering/bloc_metering.dart index cac55fc..48086af 100644 --- a/lib/screens/metering/bloc_metering.dart +++ b/lib/screens/metering/bloc_metering.dart @@ -1,10 +1,8 @@ import 'dart:async'; -import 'dart:math'; import 'package:bloc_concurrency/bloc_concurrency.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:lightmeter/data/models/exposure_pair.dart'; import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/interactors/metering_interactor.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; @@ -21,39 +19,16 @@ class MeteringBloc extends Bloc { final MeteringInteractor _meteringInteractor; late final StreamSubscription _communicationSubscription; - List get _apertureValues => - _equipmentProfileData.apertureValues.whereStopType(stopType); - List get _shutterSpeedValues => - _equipmentProfileData.shutterSpeedValues.whereStopType(stopType); - - EquipmentProfileData _equipmentProfileData; - StopType stopType; - - @visibleForTesting - late IsoValue iso = _meteringInteractor.iso; - - @visibleForTesting - late NdValue nd = _meteringInteractor.ndFilter; - - @visibleForTesting - late Film film = _meteringInteractor.film; - - @visibleForTesting - double? ev100; - MeteringBloc( this._communicationBloc, this._meteringInteractor, - this._equipmentProfileData, - this.stopType, ) : super( MeteringDataState( - ev: null, + ev100: null, film: _meteringInteractor.film, iso: _meteringInteractor.iso, nd: _meteringInteractor.ndFilter, - exposurePairs: const [], - continuousMetering: false, + isMetering: false, ), ) { _communicationSubscription = _communicationBloc.stream @@ -62,7 +37,6 @@ class MeteringBloc extends Bloc { .listen(onCommunicationState); on(_onEquipmentProfileChanged); - on(_onStopTypeChanged); on(_onFilmChanged); on(_onIsoChanged); on(_onNdChanged); @@ -71,6 +45,23 @@ class MeteringBloc extends Bloc { on(_onMeasureError); } + @override + void onTransition(Transition transition) { + super.onTransition(transition); + if (transition.nextState is MeteringDataState) { + final nextState = transition.nextState as MeteringDataState; + if (transition.currentState is LoadingState || + transition.currentState is MeteringDataState && + (transition.currentState as MeteringDataState).ev != nextState.ev) { + if (nextState.hasError) { + _meteringInteractor.errorVibration(); + } else { + _meteringInteractor.responseVibration(); + } + } + } + } + @override Future close() async { await _communicationSubscription.cancel(); @@ -82,60 +73,72 @@ class MeteringBloc extends Bloc { if (communicationState is communication_states.MeasuredState) { _handleEv100( communicationState.ev100, - continuousMetering: communicationState is communication_states.MeteringInProgressState, + isMetering: communicationState is communication_states.MeteringInProgressState, ); } } - void _onStopTypeChanged(StopTypeChangedEvent event, Emitter emit) { - if (stopType != event.stopType) { - stopType = event.stopType; - _updateMeasurements(); - } - } - void _onEquipmentProfileChanged(EquipmentProfileChangedEvent event, Emitter emit) { - _equipmentProfileData = event.equipmentProfileData; bool willUpdateMeasurements = false; - /// Update selected ISO value, if selected equipment profile + /// Update selected ISO value and discard selected film, if selected equipment profile /// doesn't contain currently selected value - if (!event.equipmentProfileData.isoValues.any((v) => iso.value == v.value)) { + IsoValue iso = state.iso; + Film film = state.film; + if (!event.equipmentProfileData.isoValues.any((v) => state.iso.value == v.value)) { _meteringInteractor.iso = event.equipmentProfileData.isoValues.first; iso = event.equipmentProfileData.isoValues.first; + _meteringInteractor.film = Film.values.first; + film = Film.values.first; willUpdateMeasurements &= true; } /// The same for ND filter - if (!event.equipmentProfileData.ndValues.any((v) => nd.value == v.value)) { + NdValue nd = state.nd; + if (!event.equipmentProfileData.ndValues.any((v) => state.nd.value == v.value)) { _meteringInteractor.ndFilter = event.equipmentProfileData.ndValues.first; nd = event.equipmentProfileData.ndValues.first; willUpdateMeasurements &= true; } if (willUpdateMeasurements) { - _updateMeasurements(); + emit( + MeteringDataState( + ev100: state.ev100, + film: film, + iso: iso, + nd: nd, + isMetering: state.isMetering, + ), + ); } } void _onFilmChanged(FilmChangedEvent event, Emitter emit) { - if (film.name != event.data.name) { - film = event.data; - _meteringInteractor.film = event.data; - _film = event.data; + if (state.film.name != event.film.name) { + _meteringInteractor.film = event.film; + + /// Find `IsoValue` with matching value + IsoValue iso = state.iso; + if (state.iso.value != event.film.iso && event.film != const Film.other()) { + iso = IsoValue.values.firstWhere( + (e) => e.value == event.film.iso, + orElse: () => state.iso, + ); + _meteringInteractor.iso = iso; + } /// If user selects 'Other' film we preserve currently selected ISO /// and therefore only discard reciprocity formula - if (iso.value != event.data.iso && event.data != const Film.other()) { - final newIso = IsoValue.values.firstWhere( - (e) => e.value == event.data.iso, - orElse: () => iso, - ); - _meteringInteractor.iso = newIso; - iso = newIso; - } - - _updateMeasurements(); + emit( + MeteringDataState( + ev100: state.ev100, + film: event.film, + iso: iso, + nd: state.nd, + isMetering: state.isMetering, + ), + ); } } @@ -144,20 +147,33 @@ class MeteringBloc extends Bloc { /// because, for example, Fomapan 400 and any Ilford 400 /// have different reciprocity formulas _meteringInteractor.film = Film.values.first; - _film = Film.values.first; - if (iso != event.isoValue) { + if (state.iso != event.isoValue) { _meteringInteractor.iso = event.isoValue; - iso = event.isoValue; - _updateMeasurements(); + emit( + MeteringDataState( + ev100: state.ev100, + film: Film.values.first, + iso: event.isoValue, + nd: state.nd, + isMetering: state.isMetering, + ), + ); } } void _onNdChanged(NdChangedEvent event, Emitter emit) { - if (nd != event.ndValue) { + if (state.nd != event.ndValue) { _meteringInteractor.ndFilter = event.ndValue; - nd = event.ndValue; - _updateMeasurements(); + emit( + MeteringDataState( + ev100: state.ev100, + film: state.film, + iso: state.iso, + nd: event.ndValue, + isMetering: state.isMetering, + ), + ); } } @@ -166,109 +182,42 @@ class MeteringBloc extends Bloc { _communicationBloc.add(const communication_events.MeasureEvent()); emit( LoadingState( - film: film, - iso: iso, - nd: nd, + film: state.film, + iso: state.iso, + nd: state.nd, ), ); } - void _updateMeasurements() => _handleEv100(ev100, continuousMetering: false); - - void _handleEv100(double? ev100, {required bool continuousMetering}) { + void _handleEv100(double? ev100, {required bool isMetering}) { if (ev100 == null || ev100.isNaN || ev100.isInfinite) { - add(MeasureErrorEvent(continuousMetering: continuousMetering)); + add(MeasureErrorEvent(isMetering: isMetering)); } else { - add(MeasuredEvent(ev100, continuousMetering: continuousMetering)); + add(MeasuredEvent(ev100, isMetering: isMetering)); } } void _onMeasured(MeasuredEvent event, Emitter emit) { - _meteringInteractor.responseVibration(); - ev100 = event.ev100; - final ev = event.ev100 + log2(iso.value / 100) - nd.stopReduction; emit( MeteringDataState( - ev: ev, - film: film, - iso: iso, - nd: nd, - exposurePairs: buildExposureValues(ev), - continuousMetering: event.continuousMetering, + ev100: event.ev100, + film: state.film, + iso: state.iso, + nd: state.nd, + isMetering: event.isMetering, ), ); } void _onMeasureError(MeasureErrorEvent event, Emitter emit) { - _meteringInteractor.errorVibration(); - ev100 = null; emit( MeteringDataState( - ev: null, - film: film, - iso: iso, - nd: nd, - exposurePairs: const [], - continuousMetering: event.continuousMetering, + ev100: null, + film: state.film, + iso: state.iso, + nd: state.nd, + isMetering: event.isMetering, ), ); } - - @visibleForTesting - List buildExposureValues(double ev) { - if (ev.isNaN || ev.isInfinite) { - return List.empty(); - } - - /// Depending on the `stopType` the exposure pairs list length is multiplied by 1,2 or 3 - final int evSteps = (ev * (stopType.index + 1)).round(); - - /// Basically we use 1" shutter speed as an anchor point for building the exposure pairs list. - /// But user can exclude this value from the list using custom equipment profile. - /// So we have to restore the index of the anchor value. - const ShutterSpeedValue anchorShutterSpeed = ShutterSpeedValue(1, false, StopType.full); - int anchorIndex = _shutterSpeedValues.indexOf(anchorShutterSpeed); - if (anchorIndex < 0) { - final filteredFullList = ShutterSpeedValue.values.whereStopType(stopType); - final customListStartIndex = filteredFullList.indexOf(_shutterSpeedValues.first); - final fullListAnchor = filteredFullList.indexOf(anchorShutterSpeed); - if (customListStartIndex < fullListAnchor) { - /// This means, that user excluded anchor value at the end, - /// i.e. all shutter speed values are shorter than 1". - anchorIndex = fullListAnchor - customListStartIndex; - } else { - /// In case user excludes anchor value at the start, - /// we can do no adjustment. - } - } - final int evOffset = anchorIndex - evSteps; - - late final int apertureOffset; - late final int shutterSpeedOffset; - if (evOffset >= 0) { - apertureOffset = 0; - shutterSpeedOffset = evOffset; - } else { - apertureOffset = -evOffset; - shutterSpeedOffset = 0; - } - - final int itemsCount = min( - _apertureValues.length + shutterSpeedOffset, - _shutterSpeedValues.length + apertureOffset, - ) - - max(apertureOffset, shutterSpeedOffset); - - if (itemsCount < 0) { - return List.empty(); - } - return List.generate( - itemsCount, - (index) => ExposurePair( - _apertureValues[index + apertureOffset], - film.reciprocityFailure(_shutterSpeedValues[index + shutterSpeedOffset]), - ), - growable: false, - ); - } } diff --git a/lib/screens/metering/components/bottom_controls/components/measure_button/widget_button_measure.dart b/lib/screens/metering/components/bottom_controls/components/measure_button/widget_button_measure.dart index 80819ca..99bd2ae 100644 --- a/lib/screens/metering/components/bottom_controls/components/measure_button/widget_button_measure.dart +++ b/lib/screens/metering/components/bottom_controls/components/measure_button/widget_button_measure.dart @@ -6,13 +6,11 @@ import 'package:lightmeter/screens/shared/filled_circle/widget_circle_filled.dar class MeteringMeasureButton extends StatefulWidget { final double? ev; final bool isMetering; - final bool hasError; final VoidCallback onTap; const MeteringMeasureButton({ required this.ev, required this.isMetering, - required this.hasError, required this.onTap, super.key, }); @@ -34,58 +32,49 @@ class _MeteringMeasureButtonState extends State { @override Widget build(BuildContext context) { - return IgnorePointer( - ignoring: widget.isMetering && widget.ev == null && !widget.hasError, - child: GestureDetector( - onTap: widget.onTap, - onTapDown: (_) { - setState(() { - _isPressed = true; - }); - }, - onTapUp: (_) { - setState(() { - _isPressed = false; - }); - }, - onTapCancel: () { - setState(() { - _isPressed = false; - }); - }, - child: SizedBox.fromSize( - size: const Size.square(Dimens.grid72), - child: Stack( - children: [ - Center( - child: AnimatedScale( - duration: Dimens.durationS, - scale: _isPressed ? 0.9 : 1.0, - child: FilledCircle( - color: Theme.of(context).colorScheme.onSurface, - size: Dimens.grid72 - Dimens.grid8, - child: Center( - child: widget.hasError - ? Icon( - Icons.error_outline, - color: Theme.of(context).colorScheme.surface, - size: Dimens.grid24, - ) - : (widget.ev != null ? _EvValueText(ev: widget.ev!) : null), - ), + return GestureDetector( + onTap: widget.onTap, + onTapDown: (_) { + setState(() { + _isPressed = true; + }); + }, + onTapUp: (_) { + setState(() { + _isPressed = false; + }); + }, + onTapCancel: () { + setState(() { + _isPressed = false; + }); + }, + child: SizedBox.fromSize( + size: const Size.square(Dimens.grid72), + child: Stack( + children: [ + Center( + child: AnimatedScale( + duration: Dimens.durationS, + scale: _isPressed ? 0.9 : 1.0, + child: FilledCircle( + color: Theme.of(context).colorScheme.onSurface, + size: Dimens.grid72 - Dimens.grid8, + child: Center( + child: widget.ev != null ? _EvValueText(ev: widget.ev!) : null, ), ), ), - Positioned.fill( - child: CircularProgressIndicator( - /// This key is needed to make indicator start from the same point every time - key: ValueKey(widget.isMetering), - color: Theme.of(context).colorScheme.onSurface, - value: widget.isMetering ? null : 1, - ), + ), + Positioned.fill( + child: CircularProgressIndicator( + /// This key is needed to make indicator start from the same point every time + key: ValueKey(widget.isMetering), + color: Theme.of(context).colorScheme.onSurface, + value: widget.isMetering ? null : 1, ), - ], - ), + ), + ], ), ), ); diff --git a/lib/screens/metering/components/bottom_controls/provider_bottom_controls.dart b/lib/screens/metering/components/bottom_controls/provider_bottom_controls.dart index 4d07889..dd4a9be 100644 --- a/lib/screens/metering/components/bottom_controls/provider_bottom_controls.dart +++ b/lib/screens/metering/components/bottom_controls/provider_bottom_controls.dart @@ -6,7 +6,6 @@ import 'package:lightmeter/screens/metering/components/bottom_controls/widget_bo class MeteringBottomControlsProvider extends StatelessWidget { final double? ev; final bool isMetering; - final bool hasError; final VoidCallback? onSwitchEvSourceType; final VoidCallback onMeasure; final VoidCallback onSettings; @@ -14,7 +13,6 @@ class MeteringBottomControlsProvider extends StatelessWidget { const MeteringBottomControlsProvider({ required this.ev, required this.isMetering, - required this.hasError, required this.onSwitchEvSourceType, required this.onMeasure, required this.onSettings, @@ -38,7 +36,6 @@ class MeteringBottomControlsProvider extends StatelessWidget { child: MeteringBottomControls( ev: ev, isMetering: isMetering, - hasError: hasError, onSwitchEvSourceType: onSwitchEvSourceType, onMeasure: onMeasure, onSettings: onSettings, diff --git a/lib/screens/metering/components/bottom_controls/widget_bottom_controls.dart b/lib/screens/metering/components/bottom_controls/widget_bottom_controls.dart index 96cd7b6..9989494 100644 --- a/lib/screens/metering/components/bottom_controls/widget_bottom_controls.dart +++ b/lib/screens/metering/components/bottom_controls/widget_bottom_controls.dart @@ -7,7 +7,6 @@ import 'package:lightmeter/utils/inherited_generics.dart'; class MeteringBottomControls extends StatelessWidget { final double? ev; final bool isMetering; - final bool hasError; final VoidCallback? onSwitchEvSourceType; final VoidCallback onMeasure; final VoidCallback onSettings; @@ -15,7 +14,6 @@ class MeteringBottomControls extends StatelessWidget { const MeteringBottomControls({ required this.ev, required this.isMetering, - required this.hasError, required this.onSwitchEvSourceType, required this.onMeasure, required this.onSettings, @@ -56,7 +54,6 @@ class MeteringBottomControls extends StatelessWidget { MeteringMeasureButton( ev: ev, isMetering: isMetering, - hasError: hasError, onTap: onMeasure, ), Expanded( diff --git a/lib/screens/metering/event_metering.dart b/lib/screens/metering/event_metering.dart index b1435f4..e12ba35 100644 --- a/lib/screens/metering/event_metering.dart +++ b/lib/screens/metering/event_metering.dart @@ -1,16 +1,10 @@ import 'package:lightmeter/data/models/film.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; -abstract class MeteringEvent { +sealed class MeteringEvent { const MeteringEvent(); } -class StopTypeChangedEvent extends MeteringEvent { - final StopType stopType; - - const StopTypeChangedEvent(this.stopType); -} - class EquipmentProfileChangedEvent extends MeteringEvent { final EquipmentProfileData equipmentProfileData; @@ -18,9 +12,9 @@ class EquipmentProfileChangedEvent extends MeteringEvent { } class FilmChangedEvent extends MeteringEvent { - final Film data; + final Film film; - const FilmChangedEvent(this.data); + const FilmChangedEvent(this.film); } class IsoChangedEvent extends MeteringEvent { @@ -41,13 +35,13 @@ class MeasureEvent extends MeteringEvent { class MeasuredEvent extends MeteringEvent { final double ev100; - final bool continuousMetering; + final bool isMetering; - const MeasuredEvent(this.ev100, {required this.continuousMetering}); + const MeasuredEvent(this.ev100, {required this.isMetering}); } class MeasureErrorEvent extends MeteringEvent { - final bool continuousMetering; + final bool isMetering; - const MeasureErrorEvent({required this.continuousMetering}); + const MeasureErrorEvent({required this.isMetering}); } diff --git a/lib/screens/metering/flow_metering.dart b/lib/screens/metering/flow_metering.dart index 3cd2f33..ebbef96 100644 --- a/lib/screens/metering/flow_metering.dart +++ b/lib/screens/metering/flow_metering.dart @@ -6,12 +6,10 @@ import 'package:lightmeter/data/light_sensor_service.dart'; import 'package:lightmeter/data/permissions_service.dart'; import 'package:lightmeter/data/shared_prefs_service.dart'; import 'package:lightmeter/interactors/metering_interactor.dart'; -import 'package:lightmeter/providers/equipment_profile_provider.dart'; import 'package:lightmeter/screens/metering/bloc_metering.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:lightmeter/screens/metering/screen_metering.dart'; import 'package:lightmeter/utils/inherited_generics.dart'; -import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class MeteringFlow extends StatefulWidget { const MeteringFlow({super.key}); @@ -38,8 +36,6 @@ class _MeteringFlowState extends State { create: (context) => MeteringBloc( context.read(), context.get(), - context.get(), - context.get(), ), ), ], diff --git a/lib/screens/metering/screen_metering.dart b/lib/screens/metering/screen_metering.dart index 29c7813..f05ca9c 100644 --- a/lib/screens/metering/screen_metering.dart +++ b/lib/screens/metering/screen_metering.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/ev_source_type.dart'; @@ -28,26 +30,30 @@ class MeteringScreen extends StatelessWidget { children: [ Expanded( child: BlocBuilder( - builder: (_, state) => _MeteringContainerBuidler( - fastest: state is MeteringDataState ? state.fastest : null, - slowest: state is MeteringDataState ? state.slowest : null, - exposurePairs: state is MeteringDataState ? state.exposurePairs : [], - film: state.film, - iso: state.iso, - nd: state.nd, - onFilmChanged: (value) => - context.read().add(FilmChangedEvent(value)), - onIsoChanged: (value) => context.read().add(IsoChangedEvent(value)), - onNdChanged: (value) => context.read().add(NdChangedEvent(value)), - ), + builder: (_, state) { + final exposurePairs = state is MeteringDataState && state.ev != null + ? buildExposureValues(context, state.ev!, state.film) + : []; + return _MeteringContainerBuidler( + fastest: exposurePairs.isNotEmpty ? exposurePairs.first : null, + slowest: exposurePairs.isNotEmpty ? exposurePairs.last : null, + exposurePairs: exposurePairs, + film: state.film, + iso: state.iso, + nd: state.nd, + onFilmChanged: (value) => + context.read().add(FilmChangedEvent(value)), + onIsoChanged: (value) => + context.read().add(IsoChangedEvent(value)), + onNdChanged: (value) => context.read().add(NdChangedEvent(value)), + ); + }, ), ), BlocBuilder( builder: (context, state) => MeteringBottomControlsProvider( ev: state is MeteringDataState ? state.ev : null, - isMetering: - state is LoadingState || state is MeteringDataState && state.continuousMetering, - hasError: state is MeteringDataState && state.hasError, + isMetering: state.isMetering, onSwitchEvSourceType: context.get().hasLightSensor ? EvSourceTypeProvider.of(context).toggleType : null, @@ -60,6 +66,70 @@ class MeteringScreen extends StatelessWidget { ), ); } + + List buildExposureValues(BuildContext context, double ev, Film film) { + if (ev.isNaN || ev.isInfinite) { + return List.empty(); + } + + /// Depending on the `stopType` the exposure pairs list length is multiplied by 1,2 or 3 + final StopType stopType = context.listen(); + final int evSteps = (ev * (stopType.index + 1)).round(); + + final EquipmentProfile equipmentProfile = context.listen(); + final List apertureValues = + equipmentProfile.apertureValues.whereStopType(stopType); + final List shutterSpeedValues = + equipmentProfile.shutterSpeedValues.whereStopType(stopType); + + /// Basically we use 1" shutter speed as an anchor point for building the exposure pairs list. + /// But user can exclude this value from the list using custom equipment profile. + /// So we have to restore the index of the anchor value. + const ShutterSpeedValue anchorShutterSpeed = ShutterSpeedValue(1, false, StopType.full); + int anchorIndex = shutterSpeedValues.indexOf(anchorShutterSpeed); + if (anchorIndex < 0) { + final filteredFullList = ShutterSpeedValue.values.whereStopType(stopType); + final customListStartIndex = filteredFullList.indexOf(shutterSpeedValues.first); + final fullListAnchor = filteredFullList.indexOf(anchorShutterSpeed); + if (customListStartIndex < fullListAnchor) { + /// This means, that user excluded anchor value at the end, + /// i.e. all shutter speed values are shorter than 1". + anchorIndex = fullListAnchor - customListStartIndex; + } else { + /// In case user excludes anchor value at the start, + /// we can do no adjustment. + } + } + final int evOffset = anchorIndex - evSteps; + + late final int apertureOffset; + late final int shutterSpeedOffset; + if (evOffset >= 0) { + apertureOffset = 0; + shutterSpeedOffset = evOffset; + } else { + apertureOffset = -evOffset; + shutterSpeedOffset = 0; + } + + final int itemsCount = min( + apertureValues.length + shutterSpeedOffset, + shutterSpeedValues.length + apertureOffset, + ) - + max(apertureOffset, shutterSpeedOffset); + + if (itemsCount < 0) { + return List.empty(); + } + return List.generate( + itemsCount, + (index) => ExposurePair( + apertureValues[index + apertureOffset], + film.reciprocityFailure(shutterSpeedValues[index + shutterSpeedOffset]), + ), + growable: false, + ); + } } class _InheritedListeners extends StatelessWidget { @@ -73,17 +143,12 @@ class _InheritedListeners extends StatelessWidget { onDidChangeDependencies: (value) { context.read().add(EquipmentProfileChangedEvent(value)); }, - child: InheritedWidgetListener( + child: InheritedModelAspectListener( + aspect: MeteringScreenLayoutFeature.filmPicker, onDidChangeDependencies: (value) { - context.read().add(StopTypeChangedEvent(value)); + if (!value) context.read().add(const FilmChangedEvent(Film.other())); }, - child: InheritedModelAspectListener( - aspect: MeteringScreenLayoutFeature.filmPicker, - onDidChangeDependencies: (value) { - if (!value) context.read().add(const FilmChangedEvent(Film.other())); - }, - child: child, - ), + child: child, ), ); } diff --git a/lib/screens/metering/state_metering.dart b/lib/screens/metering/state_metering.dart index 52dcf7a..909895d 100644 --- a/lib/screens/metering/state_metering.dart +++ b/lib/screens/metering/state_metering.dart @@ -1,18 +1,21 @@ import 'package:flutter/material.dart'; -import 'package:lightmeter/data/models/exposure_pair.dart'; import 'package:lightmeter/data/models/film.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; @immutable abstract class MeteringState { + final double? ev100; final Film film; final IsoValue iso; final NdValue nd; + final bool isMetering; const MeteringState({ + this.ev100, required this.film, required this.iso, required this.nd, + required this.isMetering, }); } @@ -21,24 +24,18 @@ class LoadingState extends MeteringState { required super.film, required super.iso, required super.nd, - }); + }) : super(isMetering: true); } class MeteringDataState extends MeteringState { - final double? ev; - final List exposurePairs; - final bool continuousMetering; - const MeteringDataState({ - required this.ev, + required super.ev100, required super.film, required super.iso, required super.nd, - required this.exposurePairs, - required this.continuousMetering, + required super.isMetering, }); - ExposurePair? get fastest => exposurePairs.isEmpty ? null : exposurePairs.first; - ExposurePair? get slowest => exposurePairs.isEmpty ? null : exposurePairs.last; + double? get ev => ev100 != null ? ev100! + log2(iso.value / 100) - nd.stopReduction : null; bool get hasError => ev == null; }