mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-24 00:10:47 +00:00
wip
This commit is contained in:
parent
31ef42c4c0
commit
ab5b747831
24 changed files with 298 additions and 88 deletions
|
@ -12,7 +12,7 @@ import 'environment.dart';
|
||||||
import 'generated/l10n.dart';
|
import 'generated/l10n.dart';
|
||||||
import 'res/theme.dart';
|
import 'res/theme.dart';
|
||||||
import 'screens/metering/flow_metering.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';
|
import 'utils/stop_type_provider.dart';
|
||||||
|
|
||||||
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
|
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
|
||||||
|
@ -66,7 +66,7 @@ class Application extends StatelessWidget {
|
||||||
initialRoute: "metering",
|
initialRoute: "metering",
|
||||||
routes: {
|
routes: {
|
||||||
"metering": (context) => const MeteringFlow(),
|
"metering": (context) => const MeteringFlow(),
|
||||||
"settings": (context) => const SettingsScreen(),
|
"settings": (context) => const SettingsFlow(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,6 +8,8 @@ class UserPreferencesService {
|
||||||
static const _isoKey = "iso";
|
static const _isoKey = "iso";
|
||||||
static const _ndFilterKey = "nd";
|
static const _ndFilterKey = "nd";
|
||||||
|
|
||||||
|
static const _cameraEvCalibrationKey = "cameraEvCalibration";
|
||||||
|
|
||||||
static const _hapticsKey = "haptics";
|
static const _hapticsKey = "haptics";
|
||||||
static const _themeTypeKey = "themeType";
|
static const _themeTypeKey = "themeType";
|
||||||
static const _dynamicColorKey = "dynamicColor";
|
static const _dynamicColorKey = "dynamicColor";
|
||||||
|
@ -25,6 +27,9 @@ class UserPreferencesService {
|
||||||
bool get haptics => _sharedPreferences.getBool(_hapticsKey) ?? false;
|
bool get haptics => _sharedPreferences.getBool(_hapticsKey) ?? false;
|
||||||
set haptics(bool value) => _sharedPreferences.setBool(_hapticsKey, value);
|
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];
|
ThemeType get themeType => ThemeType.values[_sharedPreferences.getInt(_themeTypeKey) ?? 0];
|
||||||
set themeType(ThemeType value) => _sharedPreferences.setInt(_themeTypeKey, value.index);
|
set themeType(ThemeType value) => _sharedPreferences.setInt(_themeTypeKey, value.index);
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import 'package:lightmeter/data/haptics_service.dart';
|
import 'package:lightmeter/data/haptics_service.dart';
|
||||||
import 'package:lightmeter/data/shared_prefs_service.dart';
|
import 'package:lightmeter/data/shared_prefs_service.dart';
|
||||||
|
|
||||||
class HapticsInteractor {
|
class MeteringInteractor {
|
||||||
final UserPreferencesService _userPreferencesService;
|
final UserPreferencesService _userPreferencesService;
|
||||||
final HapticsService _hapticsService;
|
final HapticsService _hapticsService;
|
||||||
|
|
||||||
const HapticsInteractor(
|
const MeteringInteractor(
|
||||||
this._userPreferencesService,
|
this._userPreferencesService,
|
||||||
this._hapticsService,
|
this._hapticsService,
|
||||||
);
|
);
|
||||||
|
|
||||||
bool get isEnabled => _userPreferencesService.haptics;
|
bool get isHapticsEnabled => _userPreferencesService.haptics;
|
||||||
|
|
||||||
/// Executes vibration if haptics are enabled in settings
|
/// Executes vibration if haptics are enabled in settings
|
||||||
void quickVibration() {
|
void quickVibration() {
|
29
lib/interactors/settings_interactor.dart
Normal file
29
lib/interactors/settings_interactor.dart
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -5,6 +5,14 @@
|
||||||
"openSettings": "Open settings",
|
"openSettings": "Open settings",
|
||||||
"fastestExposurePair": "Fastest",
|
"fastestExposurePair": "Fastest",
|
||||||
"slowestExposurePair": "Slowest",
|
"slowestExposurePair": "Slowest",
|
||||||
|
"ev": "{value} EV",
|
||||||
|
"@ev": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"iso": "ISO",
|
"iso": "ISO",
|
||||||
"filmSpeed": "Film speed",
|
"filmSpeed": "Film speed",
|
||||||
"nd": "ND",
|
"nd": "ND",
|
||||||
|
@ -12,12 +20,16 @@
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"select": "Select",
|
"select": "Select",
|
||||||
|
"save": "Save",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"metering": "Metering",
|
"metering": "Metering",
|
||||||
"fractionalStops": "Fractional stops",
|
"fractionalStops": "Fractional stops",
|
||||||
"showFractionalStops": "Show fractional stops",
|
"showFractionalStops": "Show fractional stops",
|
||||||
"halfStops": "1/2",
|
"halfStops": "1/2",
|
||||||
"thirdStops": "1/3",
|
"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",
|
"general": "General",
|
||||||
"haptics": "Haptics",
|
"haptics": "Haptics",
|
||||||
"theme": "Theme",
|
"theme": "Theme",
|
||||||
|
|
|
@ -21,7 +21,7 @@ class Dimens {
|
||||||
static const Duration durationML = Duration(milliseconds: 250);
|
static const Duration durationML = Duration(milliseconds: 250);
|
||||||
static const Duration durationL = Duration(milliseconds: 300);
|
static const Duration durationL = Duration(milliseconds: 300);
|
||||||
|
|
||||||
// `CameraSlider`
|
// `CenteredSlider`
|
||||||
static const double cameraSliderTrackHeight = grid4;
|
static const double cameraSliderTrackHeight = grid4;
|
||||||
static const double cameraSliderTrackRadius = cameraSliderTrackHeight / 2;
|
static const double cameraSliderTrackRadius = cameraSliderTrackHeight / 2;
|
||||||
static const double cameraSliderHandleSize = 32;
|
static const double cameraSliderHandleSize = 32;
|
||||||
|
|
|
@ -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/photography_value.dart';
|
||||||
import 'package:lightmeter/data/models/photography_values/shutter_speed_value.dart';
|
import 'package:lightmeter/data/models/photography_values/shutter_speed_value.dart';
|
||||||
import 'package:lightmeter/data/shared_prefs_service.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/event_communication_metering.dart' as communication_events;
|
||||||
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states;
|
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states;
|
||||||
import 'package:lightmeter/utils/log_2.dart';
|
import 'package:lightmeter/utils/log_2.dart';
|
||||||
|
@ -19,7 +19,7 @@ import 'state_metering.dart';
|
||||||
class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
final MeteringCommunicationBloc _communicationBloc;
|
final MeteringCommunicationBloc _communicationBloc;
|
||||||
final UserPreferencesService _userPreferencesService;
|
final UserPreferencesService _userPreferencesService;
|
||||||
final HapticsInteractor _hapticsInteractor;
|
final MeteringInteractor _meteringInteractor;
|
||||||
late final StreamSubscription<communication_states.ScreenState> _communicationSubscription;
|
late final StreamSubscription<communication_states.ScreenState> _communicationSubscription;
|
||||||
|
|
||||||
List<ApertureValue> get _apertureValues => apertureValues.whereStopType(stopType);
|
List<ApertureValue> get _apertureValues => apertureValues.whereStopType(stopType);
|
||||||
|
@ -30,7 +30,7 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
MeteringBloc(
|
MeteringBloc(
|
||||||
this._communicationBloc,
|
this._communicationBloc,
|
||||||
this._userPreferencesService,
|
this._userPreferencesService,
|
||||||
this._hapticsInteractor,
|
this._meteringInteractor,
|
||||||
this.stopType,
|
this.stopType,
|
||||||
) : super(
|
) : super(
|
||||||
MeteringState(
|
MeteringState(
|
||||||
|
@ -103,12 +103,12 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onMeasure(_, __) {
|
void _onMeasure(_, __) {
|
||||||
_hapticsInteractor.quickVibration();
|
_meteringInteractor.quickVibration();
|
||||||
_communicationBloc.add(const communication_events.MeasureEvent());
|
_communicationBloc.add(const communication_events.MeasureEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onMeasured(MeasuredEvent event, Emitter emit) {
|
void _onMeasured(MeasuredEvent event, Emitter emit) {
|
||||||
_hapticsInteractor.responseVibration();
|
_meteringInteractor.responseVibration();
|
||||||
final ev = event.ev100 + log2(state.iso.value / 100);
|
final ev = event.ev100 + log2(state.iso.value / 100);
|
||||||
emit(MeteringState(
|
emit(MeteringState(
|
||||||
iso: state.iso,
|
iso: state.iso,
|
||||||
|
|
|
@ -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/bloc_camera.dart';
|
||||||
import 'package:lightmeter/screens/metering/ev_source/camera/event_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/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 'package:lightmeter/utils/to_string_signed.dart';
|
||||||
|
|
||||||
import 'shared/widget_slider_camera.dart';
|
|
||||||
|
|
||||||
class CameraExposureSlider extends StatelessWidget {
|
class CameraExposureSlider extends StatelessWidget {
|
||||||
const CameraExposureSlider({super.key});
|
const CameraExposureSlider({super.key});
|
||||||
|
|
||||||
|
@ -21,7 +20,7 @@ class CameraExposureSlider extends StatelessWidget {
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.sync),
|
icon: const Icon(Icons.sync),
|
||||||
onPressed: state.currentExposureOffset != 0.0
|
onPressed: state.currentExposureOffset != 0.0
|
||||||
? () => context.read<CameraBloc>().add(const ExposureOffsetResetEvent())
|
? () => context.read<CameraBloc>().add(const ExposureOffsetChangedEvent(0.0))
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -32,7 +31,7 @@ class CameraExposureSlider extends StatelessWidget {
|
||||||
padding: const EdgeInsets.symmetric(vertical: Dimens.grid8),
|
padding: const EdgeInsets.symmetric(vertical: Dimens.grid8),
|
||||||
child: _Ruler(state.minExposureOffset, state.maxExposureOffset),
|
child: _Ruler(state.minExposureOffset, state.maxExposureOffset),
|
||||||
),
|
),
|
||||||
CameraSlider(
|
CenteredSlider(
|
||||||
isVertical: true,
|
isVertical: true,
|
||||||
icon: const Icon(Icons.light_mode),
|
icon: const Icon(Icons.light_mode),
|
||||||
value: state.currentExposureOffset,
|
value: state.currentExposureOffset,
|
||||||
|
|
|
@ -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/event_camera.dart';
|
||||||
import 'package:lightmeter/screens/metering/ev_source/camera/state_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 {
|
class CameraZoomSlider extends StatelessWidget {
|
||||||
const CameraZoomSlider({super.key});
|
const CameraZoomSlider({super.key});
|
||||||
|
@ -14,7 +14,7 @@ class CameraZoomSlider extends StatelessWidget {
|
||||||
return BlocBuilder<CameraBloc, CameraState>(
|
return BlocBuilder<CameraBloc, CameraState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state is CameraActiveState) {
|
if (state is CameraActiveState) {
|
||||||
return CameraSlider(
|
return CenteredSlider(
|
||||||
icon: const Icon(Icons.search),
|
icon: const Icon(Icons.search),
|
||||||
value: state.currentZoom,
|
value: state.currentZoom,
|
||||||
min: state.minZoom,
|
min: state.minZoom,
|
||||||
|
|
|
@ -101,7 +101,7 @@ class _MeteringScreenDialogPickerState<T extends PhotographyValue> extends State
|
||||||
child: widget.itemTitleBuilder(context, widget.values[index]),
|
child: widget.itemTitleBuilder(context, widget.values[index]),
|
||||||
),
|
),
|
||||||
secondary: widget.values[index].value != _selectedValue.value
|
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,
|
: null,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import 'package:camera/camera.dart';
|
||||||
import 'package:exif/exif.dart';
|
import 'package:exif/exif.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.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/ev_source/ev_source_bloc.dart';
|
||||||
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||||
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' as communication_event;
|
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';
|
import 'state_camera.dart';
|
||||||
|
|
||||||
class CameraBloc extends EvSourceBloc<CameraEvent, CameraState> {
|
class CameraBloc extends EvSourceBloc<CameraEvent, CameraState> {
|
||||||
final HapticsInteractor _hapticsInteractor;
|
final MeteringInteractor _meteringInteractor;
|
||||||
late final _WidgetsBindingObserver _observer;
|
late final _WidgetsBindingObserver _observer;
|
||||||
CameraController? _cameraController;
|
CameraController? _cameraController;
|
||||||
CameraController? get cameraController => _cameraController;
|
CameraController? get cameraController => _cameraController;
|
||||||
|
@ -33,7 +33,7 @@ class CameraBloc extends EvSourceBloc<CameraEvent, CameraState> {
|
||||||
|
|
||||||
CameraBloc(
|
CameraBloc(
|
||||||
MeteringCommunicationBloc communicationBloc,
|
MeteringCommunicationBloc communicationBloc,
|
||||||
this._hapticsInteractor,
|
this._meteringInteractor,
|
||||||
) : super(
|
) : super(
|
||||||
communicationBloc,
|
communicationBloc,
|
||||||
const CameraInitState(),
|
const CameraInitState(),
|
||||||
|
@ -44,7 +44,6 @@ class CameraBloc extends EvSourceBloc<CameraEvent, CameraState> {
|
||||||
on<InitializeEvent>(_onInitialize);
|
on<InitializeEvent>(_onInitialize);
|
||||||
on<ZoomChangedEvent>(_onZoomChanged);
|
on<ZoomChangedEvent>(_onZoomChanged);
|
||||||
on<ExposureOffsetChangedEvent>(_onExposureOffsetChanged);
|
on<ExposureOffsetChangedEvent>(_onExposureOffsetChanged);
|
||||||
on<ExposureOffsetResetEvent>(_onExposureOffsetResetEvent);
|
|
||||||
|
|
||||||
add(const InitializeEvent());
|
add(const InitializeEvent());
|
||||||
}
|
}
|
||||||
|
@ -120,14 +119,10 @@ class CameraBloc extends EvSourceBloc<CameraEvent, CameraState> {
|
||||||
Future<void> _onExposureOffsetChanged(ExposureOffsetChangedEvent event, Emitter emit) async {
|
Future<void> _onExposureOffsetChanged(ExposureOffsetChangedEvent event, Emitter emit) async {
|
||||||
_cameraController!.setExposureOffset(event.value);
|
_cameraController!.setExposureOffset(event.value);
|
||||||
_currentExposureOffset = event.value;
|
_currentExposureOffset = event.value;
|
||||||
|
if (event.value == 0.0) _meteringInteractor.quickVibration();
|
||||||
_emitActiveState(emit);
|
_emitActiveState(emit);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onExposureOffsetResetEvent(ExposureOffsetResetEvent event, Emitter emit) async {
|
|
||||||
_hapticsInteractor.quickVibration();
|
|
||||||
add(const ExposureOffsetChangedEvent(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _emitActiveState(Emitter emit) {
|
void _emitActiveState(Emitter emit) {
|
||||||
emit(CameraActiveState(
|
emit(CameraActiveState(
|
||||||
minZoom: _zoomRange!.start,
|
minZoom: _zoomRange!.start,
|
||||||
|
|
|
@ -17,7 +17,3 @@ class ExposureOffsetChangedEvent extends CameraEvent {
|
||||||
|
|
||||||
const ExposureOffsetChangedEvent(this.value);
|
const ExposureOffsetChangedEvent(this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExposureOffsetResetEvent extends CameraEvent {
|
|
||||||
const ExposureOffsetResetEvent();
|
|
||||||
}
|
|
||||||
|
|
|
@ -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/ev_source_type.dart';
|
||||||
import 'package:lightmeter/data/models/photography_values/photography_value.dart';
|
import 'package:lightmeter/data/models/photography_values/photography_value.dart';
|
||||||
import 'package:lightmeter/data/shared_prefs_service.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 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'ev_source/camera/bloc_camera.dart';
|
import 'ev_source/camera/bloc_camera.dart';
|
||||||
|
@ -19,7 +19,7 @@ class MeteringFlow extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Provider(
|
return Provider(
|
||||||
create: (context) => HapticsInteractor(
|
create: (context) => MeteringInteractor(
|
||||||
context.read<UserPreferencesService>(),
|
context.read<UserPreferencesService>(),
|
||||||
context.read<HapticsService>(),
|
context.read<HapticsService>(),
|
||||||
),
|
),
|
||||||
|
@ -30,14 +30,14 @@ class MeteringFlow extends StatelessWidget {
|
||||||
create: (context) => MeteringBloc(
|
create: (context) => MeteringBloc(
|
||||||
context.read<MeteringCommunicationBloc>(),
|
context.read<MeteringCommunicationBloc>(),
|
||||||
context.read<UserPreferencesService>(),
|
context.read<UserPreferencesService>(),
|
||||||
context.read<HapticsInteractor>(),
|
context.read<MeteringInteractor>(),
|
||||||
context.read<StopType>(),
|
context.read<StopType>(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => CameraBloc(
|
create: (context) => CameraBloc(
|
||||||
context.read<MeteringCommunicationBloc>(),
|
context.read<MeteringCommunicationBloc>(),
|
||||||
context.read<HapticsInteractor>(),
|
context.read<MeteringInteractor>(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (context.read<EvSourceType>() == EvSourceType.sensor)
|
if (context.read<EvSourceType>() == EvSourceType.sensor)
|
||||||
|
|
|
@ -88,7 +88,7 @@ class _MeteringScreenState extends State<MeteringScreen> {
|
||||||
MeteringBottomControls(
|
MeteringBottomControls(
|
||||||
onMeasure: () => context.read<MeteringBloc>().add(const MeasureEvent()),
|
onMeasure: () => context.read<MeteringBloc>().add(const MeasureEvent()),
|
||||||
onSettings: () {
|
onSettings: () {
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (context) => const SettingsScreen()));
|
Navigator.pushNamed(context, 'settings');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -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<CalibrationDialog> createState() => _CalibrationDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CalibrationDialogState extends State<CalibrationDialog> {
|
||||||
|
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<double> 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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
abstract class CalibrationEvent {
|
||||||
|
const CalibrationEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class CameraEvCalibrationChangedEvent extends CalibrationEvent {
|
||||||
|
final double value;
|
||||||
|
|
||||||
|
const CameraEvCalibrationChangedEvent(this.value);
|
||||||
|
}
|
|
@ -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<double>(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => CalibrationDialog(
|
||||||
|
cameraEvCalibration: 0.0,
|
||||||
|
),
|
||||||
|
).then((value) {
|
||||||
|
if (value != null) {}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,17 @@
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
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<bool> {
|
class HapticsListTileBloc extends Cubit<bool> {
|
||||||
final HapticsInteractor _hapticsInteractor;
|
final SettingsInteractor _settingsInteractor;
|
||||||
|
|
||||||
HapticsListTileBloc(
|
HapticsListTileBloc(
|
||||||
this._hapticsInteractor,
|
this._settingsInteractor,
|
||||||
) : super(_hapticsInteractor.isEnabled);
|
) : super(_settingsInteractor.isHapticsEnabled);
|
||||||
|
|
||||||
void onHapticsChange(bool value) {
|
void onHapticsChange(bool value) {
|
||||||
_hapticsInteractor.enableHaptics(value);
|
_settingsInteractor.enableHaptics(value);
|
||||||
if (value) {
|
if (value) {
|
||||||
_hapticsInteractor.quickVibration();
|
_settingsInteractor.quickVibration();
|
||||||
}
|
}
|
||||||
emit(value);
|
emit(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:lightmeter/data/haptics_service.dart';
|
import 'package:lightmeter/interactors/settings_interactor.dart';
|
||||||
import 'package:lightmeter/data/shared_prefs_service.dart';
|
|
||||||
import 'package:lightmeter/interactors/haptics_interactor.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
|
|
||||||
import 'bloc_list_tile_haptics.dart';
|
import 'bloc_list_tile_haptics.dart';
|
||||||
import 'widget_list_tile_haptics.dart';
|
import 'widget_list_tile_haptics.dart';
|
||||||
|
@ -13,17 +10,9 @@ class HapticsListTileProvider extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Provider(
|
return BlocProvider(
|
||||||
create: (context) => HapticsInteractor(
|
create: (context) => HapticsListTileBloc(context.read<SettingsInteractor>()),
|
||||||
context.read<UserPreferencesService>(),
|
|
||||||
context.read<HapticsService>(),
|
|
||||||
),
|
|
||||||
child: BlocProvider(
|
|
||||||
create: (context) => HapticsListTileBloc(
|
|
||||||
context.read<HapticsInteractor>()
|
|
||||||
),
|
|
||||||
child: const HapticsListTile(),
|
child: const HapticsListTile(),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
22
lib/screens/settings/flow_settings.dart
Normal file
22
lib/screens/settings/flow_settings.dart
Normal file
|
@ -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<UserPreferencesService>(),
|
||||||
|
context.read<HapticsService>(),
|
||||||
|
),
|
||||||
|
child: const SettingsScreen(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/res/dimens.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/haptics/provider_list_tile_haptics.dart';
|
||||||
import 'components/report_issue/widget_list_tile_report_issue.dart';
|
import 'components/report_issue/widget_list_tile_report_issue.dart';
|
||||||
import 'components/shared/settings_section/widget_settings_section.dart';
|
import 'components/shared/settings_section/widget_settings_section.dart';
|
||||||
|
@ -48,6 +49,7 @@ class SettingsScreen extends StatelessWidget {
|
||||||
title: S.of(context).metering,
|
title: S.of(context).metering,
|
||||||
children: const [
|
children: const [
|
||||||
StopTypeListTile(),
|
StopTypeListTile(),
|
||||||
|
CalibrationListTile(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SettingsSection(
|
SettingsSection(
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
|
|
||||||
class CameraSlider extends StatefulWidget {
|
class CenteredSlider extends StatefulWidget {
|
||||||
final Icon icon;
|
final Icon? icon;
|
||||||
final double value;
|
final double value;
|
||||||
final double min;
|
final double min;
|
||||||
final double max;
|
final double max;
|
||||||
final ValueChanged<double> onChanged;
|
final ValueChanged<double> onChanged;
|
||||||
final bool isVertical;
|
final bool isVertical;
|
||||||
|
|
||||||
const CameraSlider({
|
const CenteredSlider({
|
||||||
required this.icon,
|
this.icon,
|
||||||
required this.value,
|
required this.value,
|
||||||
required this.min,
|
required this.min,
|
||||||
required this.max,
|
required this.max,
|
||||||
|
@ -20,10 +20,10 @@ class CameraSlider extends StatefulWidget {
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CameraSlider> createState() => _CameraSliderState();
|
State<CenteredSlider> createState() => _CenteredSliderState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CameraSliderState extends State<CameraSlider> {
|
class _CenteredSliderState extends State<CenteredSlider> {
|
||||||
double relativeValue = 0.0;
|
double relativeValue = 0.0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -33,14 +33,17 @@ class _CameraSliderState extends State<CameraSlider> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(CameraSlider oldWidget) {
|
void didUpdateWidget(CenteredSlider oldWidget) {
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
relativeValue = (widget.value - widget.min) / (widget.max - widget.min);
|
relativeValue = (widget.value - widget.min) / (widget.max - widget.min);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return LayoutBuilder(
|
return SizedBox(
|
||||||
|
height: widget.isVertical ? double.maxFinite : Dimens.cameraSliderHandleSize,
|
||||||
|
width: !widget.isVertical ? double.maxFinite : Dimens.cameraSliderHandleSize,
|
||||||
|
child: LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
final biggestSize = widget.isVertical ? constraints.maxHeight : constraints.maxWidth;
|
final biggestSize = widget.isVertical ? constraints.maxHeight : constraints.maxWidth;
|
||||||
final handleDistance = biggestSize - Dimens.cameraSliderHandleSize;
|
final handleDistance = biggestSize - Dimens.cameraSliderHandleSize;
|
||||||
|
@ -67,6 +70,7 @@ class _CameraSliderState extends State<CameraSlider> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue