diff --git a/lib/application.dart b/lib/application.dart index 01b8f40..9c4c86b 100644 --- a/lib/application.dart +++ b/lib/application.dart @@ -12,7 +12,7 @@ import 'environment.dart'; import 'generated/l10n.dart'; import 'res/theme.dart'; import 'screens/metering/flow_metering.dart'; -import 'screens/settings/screen_settings.dart'; +import 'screens/settings/flow_settings.dart'; import 'utils/stop_type_provider.dart'; final RouteObserver routeObserver = RouteObserver(); @@ -66,7 +66,7 @@ class Application extends StatelessWidget { initialRoute: "metering", routes: { "metering": (context) => const MeteringFlow(), - "settings": (context) => const SettingsScreen(), + "settings": (context) => const SettingsFlow(), }, ), ); diff --git a/lib/data/shared_prefs_service.dart b/lib/data/shared_prefs_service.dart index d8fe310..686c0d1 100644 --- a/lib/data/shared_prefs_service.dart +++ b/lib/data/shared_prefs_service.dart @@ -8,6 +8,8 @@ class UserPreferencesService { static const _isoKey = "iso"; static const _ndFilterKey = "nd"; + static const _cameraEvCalibrationKey = "cameraEvCalibration"; + static const _hapticsKey = "haptics"; static const _themeTypeKey = "themeType"; static const _dynamicColorKey = "dynamicColor"; @@ -25,6 +27,9 @@ class UserPreferencesService { bool get haptics => _sharedPreferences.getBool(_hapticsKey) ?? false; set haptics(bool value) => _sharedPreferences.setBool(_hapticsKey, value); + double get cameraEvCalibration => _sharedPreferences.getDouble(_cameraEvCalibrationKey) ?? 0.0; + set cameraEvCalibration(double value) => _sharedPreferences.setDouble(_cameraEvCalibrationKey, value); + ThemeType get themeType => ThemeType.values[_sharedPreferences.getInt(_themeTypeKey) ?? 0]; set themeType(ThemeType value) => _sharedPreferences.setInt(_themeTypeKey, value.index); diff --git a/lib/interactors/haptics_interactor.dart b/lib/interactors/metering_interactor.dart similarity index 85% rename from lib/interactors/haptics_interactor.dart rename to lib/interactors/metering_interactor.dart index abb537a..0905a01 100644 --- a/lib/interactors/haptics_interactor.dart +++ b/lib/interactors/metering_interactor.dart @@ -1,16 +1,16 @@ import 'package:lightmeter/data/haptics_service.dart'; import 'package:lightmeter/data/shared_prefs_service.dart'; -class HapticsInteractor { +class MeteringInteractor { final UserPreferencesService _userPreferencesService; final HapticsService _hapticsService; - const HapticsInteractor( + const MeteringInteractor( this._userPreferencesService, this._hapticsService, ); - bool get isEnabled => _userPreferencesService.haptics; + bool get isHapticsEnabled => _userPreferencesService.haptics; /// Executes vibration if haptics are enabled in settings void quickVibration() { diff --git a/lib/interactors/settings_interactor.dart b/lib/interactors/settings_interactor.dart new file mode 100644 index 0000000..62c7d6d --- /dev/null +++ b/lib/interactors/settings_interactor.dart @@ -0,0 +1,29 @@ +import 'package:lightmeter/data/haptics_service.dart'; +import 'package:lightmeter/data/shared_prefs_service.dart'; + +class SettingsInteractor { + final UserPreferencesService _userPreferencesService; + final HapticsService _hapticsService; + + const SettingsInteractor( + this._userPreferencesService, + this._hapticsService, + ); + + double get cameraEvCalibration => _userPreferencesService.cameraEvCalibration; + void setCameraEvCalibration(double value) => _userPreferencesService.cameraEvCalibration = value; + + bool get isHapticsEnabled => _userPreferencesService.haptics; + + /// Executes vibration if haptics are enabled in settings + void quickVibration() { + if (_userPreferencesService.haptics) _hapticsService.quickVibration(); + } + + /// Executes vibration if haptics are enabled in settings + void responseVibration() { + if (_userPreferencesService.haptics) _hapticsService.responseVibration(); + } + + void enableHaptics(bool enable) => _userPreferencesService.haptics = enable; +} diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 0818a94..08d98ef 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -5,6 +5,14 @@ "openSettings": "Open settings", "fastestExposurePair": "Fastest", "slowestExposurePair": "Slowest", + "ev": "{value} EV", + "@ev": { + "placeholders": { + "value": { + "type": "String" + } + } + }, "iso": "ISO", "filmSpeed": "Film speed", "nd": "ND", @@ -12,12 +20,16 @@ "none": "None", "cancel": "Cancel", "select": "Select", + "save": "Save", "settings": "Settings", "metering": "Metering", "fractionalStops": "Fractional stops", "showFractionalStops": "Show fractional stops", "halfStops": "1/2", "thirdStops": "1/3", + "calibration": "Calibration", + "calibrationMessage": "The accuracy of the readings measured by this application depends entirely on the hardware of the device. Therefore, consider testing this application and setting up an EV calibration value that will give you the desired measurement results.", + "camera": "Camera", "general": "General", "haptics": "Haptics", "theme": "Theme", diff --git a/lib/res/dimens.dart b/lib/res/dimens.dart index 4ad38b3..cd80939 100644 --- a/lib/res/dimens.dart +++ b/lib/res/dimens.dart @@ -21,7 +21,7 @@ class Dimens { static const Duration durationML = Duration(milliseconds: 250); static const Duration durationL = Duration(milliseconds: 300); - // `CameraSlider` + // `CenteredSlider` static const double cameraSliderTrackHeight = grid4; static const double cameraSliderTrackRadius = cameraSliderTrackHeight / 2; static const double cameraSliderHandleSize = 32; diff --git a/lib/screens/metering/bloc_metering.dart b/lib/screens/metering/bloc_metering.dart index 7e4d357..722a1c6 100644 --- a/lib/screens/metering/bloc_metering.dart +++ b/lib/screens/metering/bloc_metering.dart @@ -7,7 +7,7 @@ import 'package:lightmeter/data/models/exposure_pair.dart'; import 'package:lightmeter/data/models/photography_values/photography_value.dart'; import 'package:lightmeter/data/models/photography_values/shutter_speed_value.dart'; import 'package:lightmeter/data/shared_prefs_service.dart'; -import 'package:lightmeter/interactors/haptics_interactor.dart'; +import 'package:lightmeter/interactors/metering_interactor.dart'; import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' as communication_events; import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states; import 'package:lightmeter/utils/log_2.dart'; @@ -19,7 +19,7 @@ import 'state_metering.dart'; class MeteringBloc extends Bloc { final MeteringCommunicationBloc _communicationBloc; final UserPreferencesService _userPreferencesService; - final HapticsInteractor _hapticsInteractor; + final MeteringInteractor _meteringInteractor; late final StreamSubscription _communicationSubscription; List get _apertureValues => apertureValues.whereStopType(stopType); @@ -30,7 +30,7 @@ class MeteringBloc extends Bloc { MeteringBloc( this._communicationBloc, this._userPreferencesService, - this._hapticsInteractor, + this._meteringInteractor, this.stopType, ) : super( MeteringState( @@ -103,12 +103,12 @@ class MeteringBloc extends Bloc { } void _onMeasure(_, __) { - _hapticsInteractor.quickVibration(); + _meteringInteractor.quickVibration(); _communicationBloc.add(const communication_events.MeasureEvent()); } void _onMeasured(MeasuredEvent event, Emitter emit) { - _hapticsInteractor.responseVibration(); + _meteringInteractor.responseVibration(); final ev = event.ev100 + log2(state.iso.value / 100); emit(MeteringState( iso: state.iso, diff --git a/lib/screens/metering/components/camera/widget_exposure_slider.dart b/lib/screens/metering/components/camera/widget_exposure_slider.dart index e0dc152..0f6a1ec 100644 --- a/lib/screens/metering/components/camera/widget_exposure_slider.dart +++ b/lib/screens/metering/components/camera/widget_exposure_slider.dart @@ -4,10 +4,9 @@ import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/screens/metering/ev_source/camera/bloc_camera.dart'; import 'package:lightmeter/screens/metering/ev_source/camera/event_camera.dart'; import 'package:lightmeter/screens/metering/ev_source/camera/state_camera.dart'; +import 'package:lightmeter/screens/shared/centered_slider/widget_slider_centered.dart'; import 'package:lightmeter/utils/to_string_signed.dart'; -import 'shared/widget_slider_camera.dart'; - class CameraExposureSlider extends StatelessWidget { const CameraExposureSlider({super.key}); @@ -21,7 +20,7 @@ class CameraExposureSlider extends StatelessWidget { IconButton( icon: const Icon(Icons.sync), onPressed: state.currentExposureOffset != 0.0 - ? () => context.read().add(const ExposureOffsetResetEvent()) + ? () => context.read().add(const ExposureOffsetChangedEvent(0.0)) : null, ), Expanded( @@ -32,7 +31,7 @@ class CameraExposureSlider extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: Dimens.grid8), child: _Ruler(state.minExposureOffset, state.maxExposureOffset), ), - CameraSlider( + CenteredSlider( isVertical: true, icon: const Icon(Icons.light_mode), value: state.currentExposureOffset, diff --git a/lib/screens/metering/components/camera/widget_zoom_camera.dart b/lib/screens/metering/components/camera/widget_zoom_camera.dart index 310c6ef..ba7b4bb 100644 --- a/lib/screens/metering/components/camera/widget_zoom_camera.dart +++ b/lib/screens/metering/components/camera/widget_zoom_camera.dart @@ -4,7 +4,7 @@ import 'package:lightmeter/screens/metering/ev_source/camera/bloc_camera.dart'; import 'package:lightmeter/screens/metering/ev_source/camera/event_camera.dart'; import 'package:lightmeter/screens/metering/ev_source/camera/state_camera.dart'; -import 'shared/widget_slider_camera.dart'; +import '../../../shared/centered_slider/widget_slider_centered.dart'; class CameraZoomSlider extends StatelessWidget { const CameraZoomSlider({super.key}); @@ -14,7 +14,7 @@ class CameraZoomSlider extends StatelessWidget { return BlocBuilder( builder: (context, state) { if (state is CameraActiveState) { - return CameraSlider( + return CenteredSlider( icon: const Icon(Icons.search), value: state.currentZoom, min: state.minZoom, diff --git a/lib/screens/metering/components/topbar/components/widget_dialog_picker.dart b/lib/screens/metering/components/topbar/components/widget_dialog_picker.dart index 1ba1f08..168e628 100644 --- a/lib/screens/metering/components/topbar/components/widget_dialog_picker.dart +++ b/lib/screens/metering/components/topbar/components/widget_dialog_picker.dart @@ -101,7 +101,7 @@ class _MeteringScreenDialogPickerState extends State child: widget.itemTitleBuilder(context, widget.values[index]), ), secondary: widget.values[index].value != _selectedValue.value - ? Text('${widget.evDifferenceBuilder.call(_selectedValue, widget.values[index])} EV') + ? Text(S.of(context).ev(widget.evDifferenceBuilder.call(_selectedValue, widget.values[index]))) : null, onChanged: (value) { if (value != null) { diff --git a/lib/screens/metering/ev_source/camera/bloc_camera.dart b/lib/screens/metering/ev_source/camera/bloc_camera.dart index 6824447..c651e4a 100644 --- a/lib/screens/metering/ev_source/camera/bloc_camera.dart +++ b/lib/screens/metering/ev_source/camera/bloc_camera.dart @@ -6,7 +6,7 @@ import 'package:camera/camera.dart'; import 'package:exif/exif.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:lightmeter/interactors/haptics_interactor.dart'; +import 'package:lightmeter/interactors/metering_interactor.dart'; import 'package:lightmeter/screens/metering/ev_source/ev_source_bloc.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' as communication_event; @@ -17,7 +17,7 @@ import 'event_camera.dart'; import 'state_camera.dart'; class CameraBloc extends EvSourceBloc { - final HapticsInteractor _hapticsInteractor; + final MeteringInteractor _meteringInteractor; late final _WidgetsBindingObserver _observer; CameraController? _cameraController; CameraController? get cameraController => _cameraController; @@ -33,7 +33,7 @@ class CameraBloc extends EvSourceBloc { CameraBloc( MeteringCommunicationBloc communicationBloc, - this._hapticsInteractor, + this._meteringInteractor, ) : super( communicationBloc, const CameraInitState(), @@ -44,7 +44,6 @@ class CameraBloc extends EvSourceBloc { on(_onInitialize); on(_onZoomChanged); on(_onExposureOffsetChanged); - on(_onExposureOffsetResetEvent); add(const InitializeEvent()); } @@ -120,14 +119,10 @@ class CameraBloc extends EvSourceBloc { Future _onExposureOffsetChanged(ExposureOffsetChangedEvent event, Emitter emit) async { _cameraController!.setExposureOffset(event.value); _currentExposureOffset = event.value; + if (event.value == 0.0) _meteringInteractor.quickVibration(); _emitActiveState(emit); } - Future _onExposureOffsetResetEvent(ExposureOffsetResetEvent event, Emitter emit) async { - _hapticsInteractor.quickVibration(); - add(const ExposureOffsetChangedEvent(0)); - } - void _emitActiveState(Emitter emit) { emit(CameraActiveState( minZoom: _zoomRange!.start, diff --git a/lib/screens/metering/ev_source/camera/event_camera.dart b/lib/screens/metering/ev_source/camera/event_camera.dart index 96de582..0a30d07 100644 --- a/lib/screens/metering/ev_source/camera/event_camera.dart +++ b/lib/screens/metering/ev_source/camera/event_camera.dart @@ -17,7 +17,3 @@ class ExposureOffsetChangedEvent extends CameraEvent { const ExposureOffsetChangedEvent(this.value); } - -class ExposureOffsetResetEvent extends CameraEvent { - const ExposureOffsetResetEvent(); -} diff --git a/lib/screens/metering/flow_metering.dart b/lib/screens/metering/flow_metering.dart index f56acf5..5204301 100644 --- a/lib/screens/metering/flow_metering.dart +++ b/lib/screens/metering/flow_metering.dart @@ -4,7 +4,7 @@ import 'package:lightmeter/data/haptics_service.dart'; import 'package:lightmeter/data/models/ev_source_type.dart'; import 'package:lightmeter/data/models/photography_values/photography_value.dart'; import 'package:lightmeter/data/shared_prefs_service.dart'; -import 'package:lightmeter/interactors/haptics_interactor.dart'; +import 'package:lightmeter/interactors/metering_interactor.dart'; import 'package:provider/provider.dart'; import 'ev_source/camera/bloc_camera.dart'; @@ -19,7 +19,7 @@ class MeteringFlow extends StatelessWidget { @override Widget build(BuildContext context) { return Provider( - create: (context) => HapticsInteractor( + create: (context) => MeteringInteractor( context.read(), context.read(), ), @@ -30,14 +30,14 @@ class MeteringFlow extends StatelessWidget { create: (context) => MeteringBloc( context.read(), context.read(), - context.read(), + context.read(), context.read(), ), ), BlocProvider( create: (context) => CameraBloc( context.read(), - context.read(), + context.read(), ), ), if (context.read() == EvSourceType.sensor) diff --git a/lib/screens/metering/screen_metering.dart b/lib/screens/metering/screen_metering.dart index d50cb6d..32d5d81 100644 --- a/lib/screens/metering/screen_metering.dart +++ b/lib/screens/metering/screen_metering.dart @@ -88,7 +88,7 @@ class _MeteringScreenState extends State { MeteringBottomControls( onMeasure: () => context.read().add(const MeasureEvent()), onSettings: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => const SettingsScreen())); + Navigator.pushNamed(context, 'settings'); }, ), ], diff --git a/lib/screens/settings/components/calibration/bloc_list_tile_calibration.dart b/lib/screens/settings/components/calibration/bloc_list_tile_calibration.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/screens/settings/components/calibration/components/calibration_dialog/widget_dialog_calibration.dart b/lib/screens/settings/components/calibration/components/calibration_dialog/widget_dialog_calibration.dart new file mode 100644 index 0000000..211f216 --- /dev/null +++ b/lib/screens/settings/components/calibration/components/calibration_dialog/widget_dialog_calibration.dart @@ -0,0 +1,112 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/res/dimens.dart'; +import 'package:lightmeter/screens/shared/centered_slider/widget_slider_centered.dart'; +import 'package:lightmeter/utils/to_string_signed.dart'; + +class CalibrationDialog extends StatefulWidget { + final double cameraEvCalibration; + + const CalibrationDialog({ + required this.cameraEvCalibration, + super.key, + }); + + @override + State createState() => _CalibrationDialogState(); +} + +class _CalibrationDialogState extends State { + late double _cameraEvCalibration = widget.cameraEvCalibration; + + @override + Widget build(BuildContext context) { + return AlertDialog( + titlePadding: const EdgeInsets.fromLTRB( + Dimens.paddingL, + Dimens.paddingL, + Dimens.paddingL, + Dimens.paddingM, + ), + title: Text(S.of(context).calibration), + contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingL), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(S.of(context).calibrationMessage), + const SizedBox(height: Dimens.grid16), + _CalibrationUnit( + title: S.of(context).camera, + value: _cameraEvCalibration, + onChanged: (value) { + setState(() { + _cameraEvCalibration = value; + }); + }, + ), + ], + ), + actionsPadding: const EdgeInsets.fromLTRB( + Dimens.paddingL, + Dimens.paddingM, + Dimens.paddingL, + Dimens.paddingL, + ), + actions: [ + TextButton( + onPressed: Navigator.of(context).pop, + child: Text(S.of(context).cancel), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(_cameraEvCalibration), + child: Text(S.of(context).save), + ), + ], + ); + } +} + +class _CalibrationUnit extends StatelessWidget { + final String title; + final double value; + final ValueChanged onChanged; + + const _CalibrationUnit({ + required this.title, + required this.value, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + contentPadding: EdgeInsets.zero, + title: Text(title), + trailing: Text(S.of(context).ev(value.toStringSignedAsFixed(1))), + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: CenteredSlider( + value: value, + min: -4, + max: 4, + onChanged: onChanged, + ), + ), + IconButton( + onPressed: () { + onChanged(0.0); + }, + icon: const Icon(Icons.sync), + ), + ], + ) + ], + ); + } +} diff --git a/lib/screens/settings/components/calibration/event_list_tile_calibration.dart b/lib/screens/settings/components/calibration/event_list_tile_calibration.dart new file mode 100644 index 0000000..eec5ebd --- /dev/null +++ b/lib/screens/settings/components/calibration/event_list_tile_calibration.dart @@ -0,0 +1,9 @@ +abstract class CalibrationEvent { + const CalibrationEvent(); +} + +class CameraEvCalibrationChangedEvent extends CalibrationEvent { + final double value; + + const CameraEvCalibrationChangedEvent(this.value); +} diff --git a/lib/screens/settings/components/calibration/widget_list_tile_calibration.dart b/lib/screens/settings/components/calibration/widget_list_tile_calibration.dart new file mode 100644 index 0000000..34bcf75 --- /dev/null +++ b/lib/screens/settings/components/calibration/widget_list_tile_calibration.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; + +import 'components/calibration_dialog/widget_dialog_calibration.dart'; + +class CalibrationListTile extends StatelessWidget { + const CalibrationListTile({super.key}); + + @override + Widget build(BuildContext context) { + return ListTile( + leading: const Icon(Icons.settings_brightness), + title: Text(S.of(context).calibration), + onTap: () { + showDialog( + context: context, + builder: (_) => CalibrationDialog( + cameraEvCalibration: 0.0, + ), + ).then((value) { + if (value != null) {} + }); + }, + ); + } +} diff --git a/lib/screens/settings/components/haptics/bloc_list_tile_haptics.dart b/lib/screens/settings/components/haptics/bloc_list_tile_haptics.dart index 134952a..11058da 100644 --- a/lib/screens/settings/components/haptics/bloc_list_tile_haptics.dart +++ b/lib/screens/settings/components/haptics/bloc_list_tile_haptics.dart @@ -1,17 +1,17 @@ import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:lightmeter/interactors/haptics_interactor.dart'; +import 'package:lightmeter/interactors/settings_interactor.dart'; class HapticsListTileBloc extends Cubit { - final HapticsInteractor _hapticsInteractor; + final SettingsInteractor _settingsInteractor; HapticsListTileBloc( - this._hapticsInteractor, - ) : super(_hapticsInteractor.isEnabled); + this._settingsInteractor, + ) : super(_settingsInteractor.isHapticsEnabled); void onHapticsChange(bool value) { - _hapticsInteractor.enableHaptics(value); + _settingsInteractor.enableHaptics(value); if (value) { - _hapticsInteractor.quickVibration(); + _settingsInteractor.quickVibration(); } emit(value); } diff --git a/lib/screens/settings/components/haptics/provider_list_tile_haptics.dart b/lib/screens/settings/components/haptics/provider_list_tile_haptics.dart index 2c431c1..97531c8 100644 --- a/lib/screens/settings/components/haptics/provider_list_tile_haptics.dart +++ b/lib/screens/settings/components/haptics/provider_list_tile_haptics.dart @@ -1,9 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:lightmeter/data/haptics_service.dart'; -import 'package:lightmeter/data/shared_prefs_service.dart'; -import 'package:lightmeter/interactors/haptics_interactor.dart'; -import 'package:provider/provider.dart'; +import 'package:lightmeter/interactors/settings_interactor.dart'; import 'bloc_list_tile_haptics.dart'; import 'widget_list_tile_haptics.dart'; @@ -13,17 +10,9 @@ class HapticsListTileProvider extends StatelessWidget { @override Widget build(BuildContext context) { - return Provider( - create: (context) => HapticsInteractor( - context.read(), - context.read(), - ), - child: BlocProvider( - create: (context) => HapticsListTileBloc( - context.read() - ), - child: const HapticsListTile(), - ), + return BlocProvider( + create: (context) => HapticsListTileBloc(context.read()), + child: const HapticsListTile(), ); } } diff --git a/lib/screens/settings/flow_settings.dart b/lib/screens/settings/flow_settings.dart new file mode 100644 index 0000000..0c4cc63 --- /dev/null +++ b/lib/screens/settings/flow_settings.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:lightmeter/data/haptics_service.dart'; +import 'package:lightmeter/data/shared_prefs_service.dart'; +import 'package:lightmeter/interactors/settings_interactor.dart'; +import 'package:lightmeter/screens/settings/screen_settings.dart'; +import 'package:provider/provider.dart'; + +class SettingsFlow extends StatelessWidget { + const SettingsFlow({super.key}); + + @override + Widget build(BuildContext context) { + return Provider( + create: (context) => SettingsInteractor( + context.read(), + context.read(), + ), + child: const SettingsScreen(), + ); + } +} diff --git a/lib/screens/settings/screen_settings.dart b/lib/screens/settings/screen_settings.dart index 43ae654..c41b1e7 100644 --- a/lib/screens/settings/screen_settings.dart +++ b/lib/screens/settings/screen_settings.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/res/dimens.dart'; +import 'components/calibration/widget_list_tile_calibration.dart'; import 'components/haptics/provider_list_tile_haptics.dart'; import 'components/report_issue/widget_list_tile_report_issue.dart'; import 'components/shared/settings_section/widget_settings_section.dart'; @@ -48,6 +49,7 @@ class SettingsScreen extends StatelessWidget { title: S.of(context).metering, children: const [ StopTypeListTile(), + CalibrationListTile(), ], ), SettingsSection( diff --git a/lib/screens/metering/components/camera/shared/widget_slider_camera.dart b/lib/screens/shared/centered_slider/widget_slider_centered.dart similarity index 67% rename from lib/screens/metering/components/camera/shared/widget_slider_camera.dart rename to lib/screens/shared/centered_slider/widget_slider_centered.dart index 643e502..8647dca 100644 --- a/lib/screens/metering/components/camera/shared/widget_slider_camera.dart +++ b/lib/screens/shared/centered_slider/widget_slider_centered.dart @@ -1,16 +1,16 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/res/dimens.dart'; -class CameraSlider extends StatefulWidget { - final Icon icon; +class CenteredSlider extends StatefulWidget { + final Icon? icon; final double value; final double min; final double max; final ValueChanged onChanged; final bool isVertical; - const CameraSlider({ - required this.icon, + const CenteredSlider({ + this.icon, required this.value, required this.min, required this.max, @@ -20,10 +20,10 @@ class CameraSlider extends StatefulWidget { }); @override - State createState() => _CameraSliderState(); + State createState() => _CenteredSliderState(); } -class _CameraSliderState extends State { +class _CenteredSliderState extends State { double relativeValue = 0.0; @override @@ -33,40 +33,44 @@ class _CameraSliderState extends State { } @override - void didUpdateWidget(CameraSlider oldWidget) { + void didUpdateWidget(CenteredSlider oldWidget) { super.didUpdateWidget(oldWidget); relativeValue = (widget.value - widget.min) / (widget.max - widget.min); } @override Widget build(BuildContext context) { - return LayoutBuilder( - builder: (context, constraints) { - final biggestSize = widget.isVertical ? constraints.maxHeight : constraints.maxWidth; - final handleDistance = biggestSize - Dimens.cameraSliderHandleSize; - return RotatedBox( - quarterTurns: widget.isVertical ? -1 : 0, - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTapUp: (details) => _updateHandlePosition(details.localPosition.dx, handleDistance), - onHorizontalDragUpdate: (details) => _updateHandlePosition(details.localPosition.dx, handleDistance), - child: SizedBox( - height: Dimens.cameraSliderHandleSize, - width: biggestSize, - child: _Slider( - handleDistance: handleDistance, - handleSize: Dimens.cameraSliderHandleSize, - trackThickness: Dimens.cameraSliderTrackHeight, - value: relativeValue, - icon: RotatedBox( - quarterTurns: widget.isVertical ? 1 : 0, - child: widget.icon, + return SizedBox( + height: widget.isVertical ? double.maxFinite : Dimens.cameraSliderHandleSize, + width: !widget.isVertical ? double.maxFinite : Dimens.cameraSliderHandleSize, + child: LayoutBuilder( + builder: (context, constraints) { + final biggestSize = widget.isVertical ? constraints.maxHeight : constraints.maxWidth; + final handleDistance = biggestSize - Dimens.cameraSliderHandleSize; + return RotatedBox( + quarterTurns: widget.isVertical ? -1 : 0, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTapUp: (details) => _updateHandlePosition(details.localPosition.dx, handleDistance), + onHorizontalDragUpdate: (details) => _updateHandlePosition(details.localPosition.dx, handleDistance), + child: SizedBox( + height: Dimens.cameraSliderHandleSize, + width: biggestSize, + child: _Slider( + handleDistance: handleDistance, + handleSize: Dimens.cameraSliderHandleSize, + trackThickness: Dimens.cameraSliderTrackHeight, + value: relativeValue, + icon: RotatedBox( + quarterTurns: widget.isVertical ? 1 : 0, + child: widget.icon, + ), ), ), ), - ), - ); - }, + ); + }, + ), ); } diff --git a/lib/utils/to_string_signed.dart b/lib/utils/to_string_signed.dart index 91936e7..48c285a 100644 --- a/lib/utils/to_string_signed.dart +++ b/lib/utils/to_string_signed.dart @@ -7,3 +7,13 @@ extension SignedString on num { } } } + +extension SignedStringDouble on double { + String toStringSignedAsFixed(fractionDigits) { + if (this > 0) { + return "+${toStringAsFixed(fractionDigits)}"; + } else { + return toStringAsFixed(fractionDigits); + } + } +}