mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2025-05-19 22:50:40 +00:00
Merge branch 'main' of https://github.com/vodemn/m3_lightmeter into cd
This commit is contained in:
commit
03dd9864bf
50 changed files with 421 additions and 206 deletions
25
.github/workflows/cd_dev.yml
vendored
25
.github/workflows/cd_dev.yml
vendored
|
@ -3,13 +3,19 @@
|
||||||
# separate terms of service, privacy policy, and support
|
# separate terms of service, privacy policy, and support
|
||||||
# documentation.
|
# documentation.
|
||||||
|
|
||||||
name: Build Dev APK
|
name: Build APK
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v*.*.*"
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
flavor:
|
||||||
|
description: 'Flavor'
|
||||||
|
type: choice
|
||||||
|
required: true
|
||||||
|
options:
|
||||||
|
- dev
|
||||||
|
- prod
|
||||||
|
default: 'dev'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -47,9 +53,12 @@ jobs:
|
||||||
flutter pub run intl_utils:generate
|
flutter pub run intl_utils:generate
|
||||||
|
|
||||||
- name: Build Apk
|
- name: Build Apk
|
||||||
run: flutter build apk --release --flavor dev --dart-define cameraPreviewAspectRatio=2/3 -t lib/main_dev.dart
|
env:
|
||||||
|
FLAVOR: ${{ github.event.inputs.flavor }}
|
||||||
|
run: flutter build apk --release --flavor $FLAVOR --dart-define cameraPreviewAspectRatio=2/3 -t lib/main_$FLAVOR.dart
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: m3_lightmeter.apk
|
name: m3_lightmeter_${{ github.event.inputs.flavor }}
|
||||||
path: build/app/outputs/flutter-apk/app-dev-release.apk
|
path: build/app/outputs/flutter-apk/app-${{ github.event.inputs.flavor }}-release.apk
|
||||||
|
|
2
.github/workflows/cd_prod.yml
vendored
2
.github/workflows/cd_prod.yml
vendored
|
@ -43,7 +43,7 @@ jobs:
|
||||||
flutter pub get
|
flutter pub get
|
||||||
flutter pub run intl_utils:generate
|
flutter pub run intl_utils:generate
|
||||||
|
|
||||||
- name: Build Apk
|
- name: Build appbundle
|
||||||
run: flutter build appbundle --release --flavor prod --dart-define cameraPreviewAspectRatio=2/3 -t lib/main_prod.dart
|
run: flutter build appbundle --release --flavor prod --dart-define cameraPreviewAspectRatio=2/3 -t lib/main_prod.dart
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
|
|
2
.github/workflows/crowdin_push.yml
vendored
2
.github/workflows/crowdin_push.yml
vendored
|
@ -1,6 +1,8 @@
|
||||||
name: Crowdin push
|
name: Crowdin push
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
paths:
|
||||||
|
- lib/l10n/**
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- crowdin
|
- crowdin
|
||||||
|
|
|
@ -20,12 +20,15 @@ const List<NdValue> ndValues = [
|
||||||
NdValue(16),
|
NdValue(16),
|
||||||
NdValue(32),
|
NdValue(32),
|
||||||
NdValue(64),
|
NdValue(64),
|
||||||
|
NdValue(100),
|
||||||
NdValue(128),
|
NdValue(128),
|
||||||
NdValue(256),
|
NdValue(256),
|
||||||
|
NdValue(400),
|
||||||
NdValue(512),
|
NdValue(512),
|
||||||
NdValue(1024),
|
NdValue(1024),
|
||||||
NdValue(2048),
|
NdValue(2048),
|
||||||
NdValue(4096),
|
NdValue(4096),
|
||||||
|
NdValue(6310),
|
||||||
NdValue(8192),
|
NdValue(8192),
|
||||||
NdValue(10000),
|
NdValue(10000),
|
||||||
];
|
];
|
||||||
|
|
|
@ -25,7 +25,44 @@ class UserPreferencesService {
|
||||||
|
|
||||||
final SharedPreferences _sharedPreferences;
|
final SharedPreferences _sharedPreferences;
|
||||||
|
|
||||||
UserPreferencesService(this._sharedPreferences);
|
UserPreferencesService(this._sharedPreferences) {
|
||||||
|
_migrateOldKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _migrateOldKeys() async {
|
||||||
|
final legacyIsoIndex = _sharedPreferences.getInt("curIsoIndex");
|
||||||
|
if (legacyIsoIndex != null) {
|
||||||
|
iso = isoValues[legacyIsoIndex];
|
||||||
|
await _sharedPreferences.remove("curIsoIndex");
|
||||||
|
}
|
||||||
|
|
||||||
|
final legacyNdIndex = _sharedPreferences.getInt("curndIndex");
|
||||||
|
if (legacyNdIndex != null) {
|
||||||
|
/// Legacy ND list has 1 extra value at the end, so this check is needed
|
||||||
|
if (legacyNdIndex < ndValues.length) {
|
||||||
|
ndFilter = ndValues[legacyNdIndex];
|
||||||
|
}
|
||||||
|
await _sharedPreferences.remove("curndIndex");
|
||||||
|
}
|
||||||
|
|
||||||
|
final legacyCameraCalibration = _sharedPreferences.getDouble("cameraCalibr");
|
||||||
|
if (legacyCameraCalibration != null) {
|
||||||
|
cameraEvCalibration = legacyCameraCalibration;
|
||||||
|
await _sharedPreferences.remove("cameraCalibr");
|
||||||
|
}
|
||||||
|
|
||||||
|
final legacyLightSensorCalibration = _sharedPreferences.getDouble("sensorCalibr");
|
||||||
|
if (legacyLightSensorCalibration != null) {
|
||||||
|
lightSensorEvCalibration = legacyLightSensorCalibration;
|
||||||
|
await _sharedPreferences.remove("sensorCalibr");
|
||||||
|
}
|
||||||
|
|
||||||
|
final legacyHaptics = _sharedPreferences.getBool("vibrate");
|
||||||
|
if (legacyHaptics != null) {
|
||||||
|
haptics = legacyHaptics;
|
||||||
|
await _sharedPreferences.remove("vibrate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IsoValue get iso => isoValues.firstWhere((v) => v.value == (_sharedPreferences.getInt(_isoKey) ?? 100));
|
IsoValue get iso => isoValues.firstWhere((v) => v.value == (_sharedPreferences.getInt(_isoKey) ?? 100));
|
||||||
set iso(IsoValue value) => _sharedPreferences.setInt(_isoKey, value.value);
|
set iso(IsoValue value) => _sharedPreferences.setInt(_isoKey, value.value);
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
enum BuildType { dev, prod }
|
||||||
|
|
||||||
class Environment {
|
class Environment {
|
||||||
|
final BuildType buildType;
|
||||||
final String sourceCodeUrl;
|
final String sourceCodeUrl;
|
||||||
final String issuesReportUrl;
|
final String issuesReportUrl;
|
||||||
final String contactEmail;
|
final String contactEmail;
|
||||||
|
@ -6,6 +9,7 @@ class Environment {
|
||||||
final bool hasLightSensor;
|
final bool hasLightSensor;
|
||||||
|
|
||||||
const Environment({
|
const Environment({
|
||||||
|
required this.buildType,
|
||||||
required this.sourceCodeUrl,
|
required this.sourceCodeUrl,
|
||||||
required this.issuesReportUrl,
|
required this.issuesReportUrl,
|
||||||
required this.contactEmail,
|
required this.contactEmail,
|
||||||
|
@ -13,18 +17,21 @@ class Environment {
|
||||||
});
|
});
|
||||||
|
|
||||||
const Environment.dev()
|
const Environment.dev()
|
||||||
: sourceCodeUrl = 'https://github.com/vodemn/m3_lightmeter',
|
: buildType = BuildType.dev,
|
||||||
|
sourceCodeUrl = 'https://github.com/vodemn/m3_lightmeter',
|
||||||
issuesReportUrl = 'https://github.com/vodemn/m3_lightmeter/issues',
|
issuesReportUrl = 'https://github.com/vodemn/m3_lightmeter/issues',
|
||||||
contactEmail = 'contact.vodemn@gmail.com',
|
contactEmail = 'contact.vodemn@gmail.com',
|
||||||
hasLightSensor = false;
|
hasLightSensor = false;
|
||||||
|
|
||||||
const Environment.prod()
|
const Environment.prod()
|
||||||
: sourceCodeUrl = 'https://github.com/vodemn/m3_lightmeter',
|
: buildType = BuildType.prod,
|
||||||
|
sourceCodeUrl = 'https://github.com/vodemn/m3_lightmeter',
|
||||||
issuesReportUrl = 'https://github.com/vodemn/m3_lightmeter/issues',
|
issuesReportUrl = 'https://github.com/vodemn/m3_lightmeter/issues',
|
||||||
contactEmail = 'contact.vodemn@gmail.com',
|
contactEmail = 'contact.vodemn@gmail.com',
|
||||||
hasLightSensor = false;
|
hasLightSensor = false;
|
||||||
|
|
||||||
Environment copyWith({bool? hasLightSensor}) => Environment(
|
Environment copyWith({bool? hasLightSensor}) => Environment(
|
||||||
|
buildType: buildType,
|
||||||
sourceCodeUrl: sourceCodeUrl,
|
sourceCodeUrl: sourceCodeUrl,
|
||||||
issuesReportUrl: issuesReportUrl,
|
issuesReportUrl: issuesReportUrl,
|
||||||
contactEmail: contactEmail,
|
contactEmail: contactEmail,
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
"@@locale": "en",
|
"@@locale": "en",
|
||||||
"fastestExposurePair": "Fastest",
|
"fastestExposurePair": "Fastest",
|
||||||
"slowestExposurePair": "Slowest",
|
"slowestExposurePair": "Slowest",
|
||||||
"ev": "{value} EV",
|
"ev": "EV",
|
||||||
"@ev": {
|
"evValue": "{value} EV",
|
||||||
|
"@evValue": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"value": {
|
"value": {
|
||||||
"type": "String"
|
"type": "String"
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
"@@locale": "fr",
|
"@@locale": "fr",
|
||||||
"fastestExposurePair": "Le plus rapide",
|
"fastestExposurePair": "Le plus rapide",
|
||||||
"slowestExposurePair": "Le plus lent",
|
"slowestExposurePair": "Le plus lent",
|
||||||
"ev": "{value} EV",
|
"ev": "EV",
|
||||||
"@ev": {
|
"evValue": "{value} EV",
|
||||||
|
"@evValue": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"value": {
|
"value": {
|
||||||
"type": "String"
|
"type": "String"
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
"@@locale": "ru",
|
"@@locale": "ru",
|
||||||
"fastestExposurePair": "Короткая выдержка",
|
"fastestExposurePair": "Короткая выдержка",
|
||||||
"slowestExposurePair": "Длинная выдержка",
|
"slowestExposurePair": "Длинная выдержка",
|
||||||
"ev": "{value} EV",
|
"ev": "EV",
|
||||||
"@ev": {
|
"evValue": "{value} EV",
|
||||||
|
"@evValue": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"value": {
|
"value": {
|
||||||
"type": "String"
|
"type": "String"
|
||||||
|
|
|
@ -13,6 +13,7 @@ class Dimens {
|
||||||
static const double grid24 = 24;
|
static const double grid24 = 24;
|
||||||
static const double grid48 = 48;
|
static const double grid48 = 48;
|
||||||
static const double grid56 = 56;
|
static const double grid56 = 56;
|
||||||
|
static const double grid72 = 72;
|
||||||
static const double grid168 = 168;
|
static const double grid168 = 168;
|
||||||
|
|
||||||
static const double paddingS = 8;
|
static const double paddingS = 8;
|
||||||
|
|
|
@ -4,6 +4,8 @@ import 'dart:math';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:lightmeter/data/models/photography_values/aperture_value.dart';
|
import 'package:lightmeter/data/models/photography_values/aperture_value.dart';
|
||||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||||
|
import 'package:lightmeter/data/models/photography_values/iso_value.dart';
|
||||||
|
import 'package:lightmeter/data/models/photography_values/nd_value.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';
|
||||||
|
@ -29,16 +31,20 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
|
|
||||||
StopType stopType;
|
StopType stopType;
|
||||||
|
|
||||||
|
late IsoValue _iso = _userPreferencesService.iso;
|
||||||
|
late NdValue _nd = _userPreferencesService.ndFilter;
|
||||||
|
double _ev = 0.0;
|
||||||
|
bool _isMeteringInProgress = false;
|
||||||
|
|
||||||
MeteringBloc(
|
MeteringBloc(
|
||||||
this._communicationBloc,
|
this._communicationBloc,
|
||||||
this._userPreferencesService,
|
this._userPreferencesService,
|
||||||
this._meteringInteractor,
|
this._meteringInteractor,
|
||||||
this.stopType,
|
this.stopType,
|
||||||
) : super(
|
) : super(
|
||||||
MeteringState(
|
MeteringEndedState(
|
||||||
iso: _userPreferencesService.iso,
|
iso: _userPreferencesService.iso,
|
||||||
ev: 0.0,
|
ev: 0.0,
|
||||||
evCompensation: 0.0,
|
|
||||||
nd: _userPreferencesService.ndFilter,
|
nd: _userPreferencesService.ndFilter,
|
||||||
exposurePairs: [],
|
exposurePairs: [],
|
||||||
),
|
),
|
||||||
|
@ -53,8 +59,6 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
on<NdChangedEvent>(_onNdChanged);
|
on<NdChangedEvent>(_onNdChanged);
|
||||||
on<MeasureEvent>(_onMeasure);
|
on<MeasureEvent>(_onMeasure);
|
||||||
on<MeasuredEvent>(_onMeasured);
|
on<MeasuredEvent>(_onMeasured);
|
||||||
|
|
||||||
add(const MeasureEvent());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -65,76 +69,63 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
|
|
||||||
void _onCommunicationState(communication_states.ScreenState communicationState) {
|
void _onCommunicationState(communication_states.ScreenState communicationState) {
|
||||||
if (communicationState is communication_states.MeasuredState) {
|
if (communicationState is communication_states.MeasuredState) {
|
||||||
|
_isMeteringInProgress = communicationState is communication_states.MeteringInProgressState;
|
||||||
add(MeasuredEvent(communicationState.ev100));
|
add(MeasuredEvent(communicationState.ev100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onStopTypeChanged(StopTypeChangedEvent event, Emitter emit) {
|
void _onStopTypeChanged(StopTypeChangedEvent event, Emitter emit) {
|
||||||
stopType = event.stopType;
|
stopType = event.stopType;
|
||||||
emit(MeteringState(
|
_emitMeasuredState(emit);
|
||||||
iso: state.iso,
|
|
||||||
ev: state.ev,
|
|
||||||
evCompensation: state.evCompensation,
|
|
||||||
nd: state.nd,
|
|
||||||
exposurePairs: _buildExposureValues(state.ev),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onIsoChanged(IsoChangedEvent event, Emitter emit) {
|
void _onIsoChanged(IsoChangedEvent event, Emitter emit) {
|
||||||
_userPreferencesService.iso = event.isoValue;
|
_userPreferencesService.iso = event.isoValue;
|
||||||
final ev = state.ev + log2(event.isoValue.value / state.iso.value);
|
_ev = _ev + log2(event.isoValue.value / _iso.value);
|
||||||
emit(MeteringState(
|
_iso = event.isoValue;
|
||||||
iso: event.isoValue,
|
_emitMeasuredState(emit);
|
||||||
ev: ev,
|
|
||||||
evCompensation: state.evCompensation,
|
|
||||||
nd: state.nd,
|
|
||||||
exposurePairs: _buildExposureValues(ev),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onNdChanged(NdChangedEvent event, Emitter emit) {
|
void _onNdChanged(NdChangedEvent event, Emitter emit) {
|
||||||
_userPreferencesService.ndFilter = event.ndValue;
|
_userPreferencesService.ndFilter = event.ndValue;
|
||||||
final ev = state.ev - event.ndValue.stopReduction + state.nd.stopReduction;
|
_ev = _ev - event.ndValue.stopReduction + _nd.stopReduction;
|
||||||
emit(MeteringState(
|
_nd = event.ndValue;
|
||||||
iso: state.iso,
|
_emitMeasuredState(emit);
|
||||||
ev: ev,
|
|
||||||
evCompensation: state.evCompensation,
|
|
||||||
nd: event.ndValue,
|
|
||||||
exposurePairs: _buildExposureValues(ev),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onMeasure(_, __) {
|
void _onMeasure(_, Emitter emit) {
|
||||||
_meteringInteractor.quickVibration();
|
_meteringInteractor.quickVibration();
|
||||||
_communicationBloc.add(const communication_events.MeasureEvent());
|
_communicationBloc.add(const communication_events.MeasureEvent());
|
||||||
|
_isMeteringInProgress = true;
|
||||||
|
emit(const LoadingState());
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onMeasured(MeasuredEvent event, Emitter emit) {
|
void _onMeasured(MeasuredEvent event, Emitter emit) {
|
||||||
_meteringInteractor.responseVibration();
|
_meteringInteractor.responseVibration();
|
||||||
final ev = event.ev100 + log2(state.iso.value / 100);
|
_ev = event.ev100 + log2(_iso.value / 100);
|
||||||
emit(MeteringState(
|
_emitMeasuredState(emit);
|
||||||
iso: state.iso,
|
}
|
||||||
ev: ev,
|
|
||||||
evCompensation: state.evCompensation,
|
void _emitMeasuredState(Emitter emit) {
|
||||||
nd: state.nd,
|
emit(_isMeteringInProgress
|
||||||
exposurePairs: _buildExposureValues(ev),
|
? MeteringInProgressState(
|
||||||
));
|
iso: _iso,
|
||||||
|
ev: _ev,
|
||||||
|
nd: _nd,
|
||||||
|
exposurePairs: _buildExposureValues(_ev),
|
||||||
|
)
|
||||||
|
: MeteringEndedState(
|
||||||
|
iso: _iso,
|
||||||
|
ev: _ev,
|
||||||
|
nd: _nd,
|
||||||
|
exposurePairs: _buildExposureValues(_ev),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ExposurePair> _buildExposureValues(double ev) {
|
List<ExposurePair> _buildExposureValues(double ev) {
|
||||||
late final int evSteps;
|
/// Depending on the `stopType` the exposure pairs list length is multiplied by 1,2 or 3
|
||||||
switch (stopType) {
|
final int evSteps = (ev * (stopType.index + 1)).round();
|
||||||
case StopType.full:
|
final int evOffset =
|
||||||
evSteps = ev.floor();
|
|
||||||
break;
|
|
||||||
case StopType.half:
|
|
||||||
evSteps = (ev / 0.5).floor();
|
|
||||||
break;
|
|
||||||
case StopType.third:
|
|
||||||
evSteps = (ev / 0.3).floor();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
final evOffset =
|
|
||||||
_shutterSpeedValues.indexOf(const ShutterSpeedValue(1, false, StopType.full)) - evSteps;
|
_shutterSpeedValues.indexOf(const ShutterSpeedValue(1, false, StopType.full)) - evSteps;
|
||||||
|
|
||||||
late final int apertureOffset;
|
late final int apertureOffset;
|
||||||
|
@ -147,7 +138,7 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
shutterSpeedOffset = 0;
|
shutterSpeedOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int itemsCount = min(_apertureValues.length + shutterSpeedOffset,
|
final int itemsCount = min(_apertureValues.length + shutterSpeedOffset,
|
||||||
_shutterSpeedValues.length + apertureOffset) -
|
_shutterSpeedValues.length + apertureOffset) -
|
||||||
max(apertureOffset, shutterSpeedOffset);
|
max(apertureOffset, shutterSpeedOffset);
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ class MeteringCommunicationBloc
|
||||||
// `MeasureState` is not const, so that `Bloc` treats each state as new and updates state stream
|
// `MeasureState` is not const, so that `Bloc` treats each state as new and updates state stream
|
||||||
// ignore: prefer_const_constructors
|
// ignore: prefer_const_constructors
|
||||||
on<MeasureEvent>((_, emit) => emit(MeasureState()));
|
on<MeasureEvent>((_, emit) => emit(MeasureState()));
|
||||||
on<MeasuredEvent>((event, emit) => emit(MeasuredState(event.ev100)));
|
on<MeteringInProgressEvent>((event, emit) => emit(MeteringInProgressState(event.ev100)));
|
||||||
|
on<MeteringEndedEvent>((event, emit) => emit(MeteringEndedState(event.ev100)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,16 @@ class MeasureEvent extends ScreenEvent {
|
||||||
const MeasureEvent();
|
const MeasureEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
class MeasuredEvent extends SourceEvent {
|
abstract class MeasuredEvent extends SourceEvent {
|
||||||
final double ev100;
|
final double ev100;
|
||||||
|
|
||||||
const MeasuredEvent(this.ev100);
|
const MeasuredEvent(this.ev100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MeteringInProgressEvent extends MeasuredEvent {
|
||||||
|
const MeteringInProgressEvent(super.ev100);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MeteringEndedEvent extends MeasuredEvent {
|
||||||
|
const MeteringEndedEvent(super.ev100);
|
||||||
|
}
|
||||||
|
|
|
@ -18,8 +18,16 @@ class MeasureState extends SourceState {
|
||||||
const MeasureState();
|
const MeasureState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class MeasuredState extends ScreenState {
|
abstract class MeasuredState extends ScreenState {
|
||||||
final double ev100;
|
final double ev100;
|
||||||
|
|
||||||
const MeasuredState(this.ev100);
|
const MeasuredState(this.ev100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MeteringInProgressState extends MeasuredState {
|
||||||
|
const MeteringInProgressState(super.ev100);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MeteringEndedState extends MeasuredState {
|
||||||
|
const MeteringEndedState(super.ev100);
|
||||||
|
}
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
import 'package:lightmeter/screens/shared/filled_circle/widget_circle_filled.dart';
|
import 'package:lightmeter/screens/shared/filled_circle/widget_circle_filled.dart';
|
||||||
|
|
||||||
class MeteringMeasureButton extends StatefulWidget {
|
class MeteringMeasureButton extends StatefulWidget {
|
||||||
final double size;
|
final double? ev;
|
||||||
|
final bool isMetering;
|
||||||
final VoidCallback onTap;
|
final VoidCallback onTap;
|
||||||
|
|
||||||
const MeteringMeasureButton({
|
const MeteringMeasureButton({
|
||||||
|
required this.ev,
|
||||||
|
required this.isMetering,
|
||||||
required this.onTap,
|
required this.onTap,
|
||||||
this.size = 72,
|
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -19,10 +22,18 @@ class MeteringMeasureButton extends StatefulWidget {
|
||||||
class _MeteringMeasureButtonState extends State<MeteringMeasureButton> {
|
class _MeteringMeasureButtonState extends State<MeteringMeasureButton> {
|
||||||
bool _isPressed = false;
|
bool _isPressed = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant MeteringMeasureButton oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (oldWidget.isMetering != widget.isMetering) {
|
||||||
|
_isPressed = widget.isMetering;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox.fromSize(
|
return IgnorePointer(
|
||||||
size: Size.square(widget.size),
|
ignoring: widget.isMetering && widget.ev == null,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: widget.onTap,
|
onTap: widget.onTap,
|
||||||
onTapDown: (_) {
|
onTapDown: (_) {
|
||||||
|
@ -40,26 +51,52 @@ class _MeteringMeasureButtonState extends State<MeteringMeasureButton> {
|
||||||
_isPressed = false;
|
_isPressed = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: DecoratedBox(
|
child: SizedBox.fromSize(
|
||||||
decoration: BoxDecoration(
|
size: const Size.square(Dimens.grid72),
|
||||||
borderRadius: BorderRadius.circular(widget.size / 2),
|
child: Stack(
|
||||||
border: Border.all(
|
children: [
|
||||||
width: Dimens.grid4,
|
Center(
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
child: AnimatedScale(
|
||||||
),
|
duration: Dimens.durationS,
|
||||||
),
|
scale: _isPressed ? 0.9 : 1.0,
|
||||||
child: Center(
|
child: FilledCircle(
|
||||||
child: AnimatedScale(
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
duration: Dimens.durationS,
|
size: Dimens.grid72 - Dimens.grid8,
|
||||||
scale: _isPressed ? 0.9 : 1.0,
|
child: Center(
|
||||||
child: FilledCircle(
|
child: widget.ev != null ? _EvValueText(ev: widget.ev!) : null,
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
),
|
||||||
size: widget.size - Dimens.grid16,
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
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,
|
||||||
|
strokeWidth: Dimens.grid4,
|
||||||
|
value: widget.isMetering ? null : 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _EvValueText extends StatelessWidget {
|
||||||
|
final double ev;
|
||||||
|
|
||||||
|
const _EvValueText({required this.ev});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
return Text(
|
||||||
|
'${ev.toStringAsFixed(1)}\n${S.of(context).ev}',
|
||||||
|
style: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.surface),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,11 +4,15 @@ import 'package:lightmeter/res/dimens.dart';
|
||||||
import 'widget_bottom_controls.dart';
|
import 'widget_bottom_controls.dart';
|
||||||
|
|
||||||
class MeteringBottomControlsProvider extends StatelessWidget {
|
class MeteringBottomControlsProvider extends StatelessWidget {
|
||||||
|
final double? ev;
|
||||||
|
final bool isMetering;
|
||||||
final VoidCallback? onSwitchEvSourceType;
|
final VoidCallback? onSwitchEvSourceType;
|
||||||
final VoidCallback onMeasure;
|
final VoidCallback onMeasure;
|
||||||
final VoidCallback onSettings;
|
final VoidCallback onSettings;
|
||||||
|
|
||||||
const MeteringBottomControlsProvider({
|
const MeteringBottomControlsProvider({
|
||||||
|
required this.ev,
|
||||||
|
required this.isMetering,
|
||||||
required this.onSwitchEvSourceType,
|
required this.onSwitchEvSourceType,
|
||||||
required this.onMeasure,
|
required this.onMeasure,
|
||||||
required this.onSettings,
|
required this.onSettings,
|
||||||
|
@ -30,6 +34,8 @@ class MeteringBottomControlsProvider extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: MeteringBottomControls(
|
child: MeteringBottomControls(
|
||||||
|
ev: ev,
|
||||||
|
isMetering: isMetering,
|
||||||
onSwitchEvSourceType: onSwitchEvSourceType,
|
onSwitchEvSourceType: onSwitchEvSourceType,
|
||||||
onMeasure: onMeasure,
|
onMeasure: onMeasure,
|
||||||
onSettings: onSettings,
|
onSettings: onSettings,
|
||||||
|
|
|
@ -6,11 +6,15 @@ import 'package:provider/provider.dart';
|
||||||
import 'components/measure_button/widget_button_measure.dart';
|
import 'components/measure_button/widget_button_measure.dart';
|
||||||
|
|
||||||
class MeteringBottomControls extends StatelessWidget {
|
class MeteringBottomControls extends StatelessWidget {
|
||||||
|
final double? ev;
|
||||||
|
final bool isMetering;
|
||||||
final VoidCallback? onSwitchEvSourceType;
|
final VoidCallback? onSwitchEvSourceType;
|
||||||
final VoidCallback onMeasure;
|
final VoidCallback onMeasure;
|
||||||
final VoidCallback onSettings;
|
final VoidCallback onSettings;
|
||||||
|
|
||||||
const MeteringBottomControls({
|
const MeteringBottomControls({
|
||||||
|
required this.ev,
|
||||||
|
required this.isMetering,
|
||||||
required this.onSwitchEvSourceType,
|
required this.onSwitchEvSourceType,
|
||||||
required this.onMeasure,
|
required this.onMeasure,
|
||||||
required this.onSettings,
|
required this.onSettings,
|
||||||
|
@ -46,7 +50,11 @@ class MeteringBottomControls extends StatelessWidget {
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
MeteringMeasureButton(onTap: onMeasure),
|
MeteringMeasureButton(
|
||||||
|
ev: ev,
|
||||||
|
isMetering: isMetering,
|
||||||
|
onTap: onMeasure,
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
|
|
@ -34,6 +34,8 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
|
||||||
double _exposureStep = 0.0;
|
double _exposureStep = 0.0;
|
||||||
double _currentExposureOffset = 0.0;
|
double _currentExposureOffset = 0.0;
|
||||||
|
|
||||||
|
double _ev100 = 0.0;
|
||||||
|
|
||||||
CameraContainerBloc(
|
CameraContainerBloc(
|
||||||
this._meteringInteractor,
|
this._meteringInteractor,
|
||||||
MeteringCommunicationBloc communicationBloc,
|
MeteringCommunicationBloc communicationBloc,
|
||||||
|
@ -58,17 +60,17 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
WidgetsBinding.instance.removeObserver(_observer);
|
WidgetsBinding.instance.removeObserver(_observer);
|
||||||
unawaited(_cameraController?.dispose());
|
unawaited(_cameraController?.dispose());
|
||||||
|
communicationBloc.add(communication_event.MeteringEndedEvent(_ev100));
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onCommunicationState(communication_states.SourceState communicationState) {
|
void onCommunicationState(communication_states.SourceState communicationState) {
|
||||||
if (communicationState is communication_states.MeasureState) {
|
if (communicationState is communication_states.MeasureState) {
|
||||||
_takePhoto().then((ev100) {
|
_takePhoto().then((ev100Raw) {
|
||||||
if (ev100 != null) {
|
if (ev100Raw != null) {
|
||||||
communicationBloc.add(
|
_ev100 = ev100Raw + _meteringInteractor.cameraEvCalibration;
|
||||||
communication_event.MeasuredEvent(ev100 + _meteringInteractor.cameraEvCalibration),
|
communicationBloc.add(communication_event.MeteringEndedEvent(_ev100));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ class LightSensorContainerBloc
|
||||||
final MeteringInteractor _meteringInteractor;
|
final MeteringInteractor _meteringInteractor;
|
||||||
|
|
||||||
StreamSubscription<int>? _luxSubscriptions;
|
StreamSubscription<int>? _luxSubscriptions;
|
||||||
|
double _ev100 = 0.0;
|
||||||
|
|
||||||
LightSensorContainerBloc(
|
LightSensorContainerBloc(
|
||||||
this._meteringInteractor,
|
this._meteringInteractor,
|
||||||
|
@ -30,11 +31,11 @@ class LightSensorContainerBloc
|
||||||
if (communicationState is communication_states.MeasureState) {
|
if (communicationState is communication_states.MeasureState) {
|
||||||
if (_luxSubscriptions == null) {
|
if (_luxSubscriptions == null) {
|
||||||
_luxSubscriptions = _meteringInteractor.luxStream().listen((event) {
|
_luxSubscriptions = _meteringInteractor.luxStream().listen((event) {
|
||||||
communicationBloc.add(communication_event.MeasuredEvent(
|
_ev100 = log2(event.toDouble() / 2.5) + _meteringInteractor.lightSensorEvCalibration;
|
||||||
log2(event.toDouble() / 2.5) + _meteringInteractor.lightSensorEvCalibration,
|
communicationBloc.add(communication_event.MeteringInProgressEvent(_ev100));
|
||||||
));
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
communicationBloc.add(communication_event.MeteringEndedEvent(_ev100));
|
||||||
_luxSubscriptions?.cancel().then((_) => _luxSubscriptions = null);
|
_luxSubscriptions?.cancel().then((_) => _luxSubscriptions = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +43,7 @@ class LightSensorContainerBloc
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
|
communicationBloc.add(communication_event.MeteringEndedEvent(_ev100));
|
||||||
_luxSubscriptions?.cancel().then((_) => _luxSubscriptions = null);
|
_luxSubscriptions?.cancel().then((_) => _luxSubscriptions = null);
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,9 +83,8 @@ class _PhotographyValuePickerDialogState<T extends PhotographyValue>
|
||||||
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(S
|
? Text(S.of(context).evValue(
|
||||||
.of(context)
|
widget.evDifferenceBuilder.call(_selectedValue, widget.values[index])))
|
||||||
.ev(widget.evDifferenceBuilder.call(_selectedValue, widget.values[index])))
|
|
||||||
: null,
|
: null,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
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/models/ev_source_type.dart';
|
import 'package:lightmeter/data/models/ev_source_type.dart';
|
||||||
|
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||||
|
import 'package:lightmeter/data/models/photography_values/iso_value.dart';
|
||||||
|
import 'package:lightmeter/data/models/photography_values/nd_value.dart';
|
||||||
import 'package:lightmeter/data/models/photography_values/photography_value.dart';
|
import 'package:lightmeter/data/models/photography_values/photography_value.dart';
|
||||||
import 'package:lightmeter/environment.dart';
|
import 'package:lightmeter/environment.dart';
|
||||||
import 'package:lightmeter/providers/ev_source_type_provider.dart';
|
import 'package:lightmeter/providers/ev_source_type_provider.dart';
|
||||||
|
@ -36,8 +39,9 @@ class _MeteringScreenState extends State<MeteringScreen> {
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: BlocBuilder<MeteringBloc, MeteringState>(
|
child: BlocBuilder<MeteringBloc, MeteringState>(
|
||||||
builder: (context, state) => context.watch<EvSourceType>() == EvSourceType.camera
|
buildWhen: (_, current) => current is MeteringDataState,
|
||||||
? CameraContainerProvider(
|
builder: (_, state) => state is MeteringDataState
|
||||||
|
? _MeteringContainerBuidler(
|
||||||
fastest: state.fastest,
|
fastest: state.fastest,
|
||||||
slowest: state.slowest,
|
slowest: state.slowest,
|
||||||
iso: state.iso,
|
iso: state.iso,
|
||||||
|
@ -46,26 +50,65 @@ class _MeteringScreenState extends State<MeteringScreen> {
|
||||||
onNdChanged: (value) => _bloc.add(NdChangedEvent(value)),
|
onNdChanged: (value) => _bloc.add(NdChangedEvent(value)),
|
||||||
exposurePairs: state.exposurePairs,
|
exposurePairs: state.exposurePairs,
|
||||||
)
|
)
|
||||||
: LightSensorContainerProvider(
|
: const SizedBox.shrink(),
|
||||||
fastest: state.fastest,
|
|
||||||
slowest: state.slowest,
|
|
||||||
iso: state.iso,
|
|
||||||
nd: state.nd,
|
|
||||||
onIsoChanged: (value) => _bloc.add(IsoChangedEvent(value)),
|
|
||||||
onNdChanged: (value) => _bloc.add(NdChangedEvent(value)),
|
|
||||||
exposurePairs: state.exposurePairs,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
MeteringBottomControlsProvider(
|
BlocBuilder<MeteringBloc, MeteringState>(
|
||||||
onSwitchEvSourceType: context.read<Environment>().hasLightSensor
|
builder: (context, state) => MeteringBottomControlsProvider(
|
||||||
? EvSourceTypeProvider.of(context).toggleType
|
ev: state is MeteringDataState ? state.ev : null,
|
||||||
: null,
|
isMetering: state is LoadingState || state is MeteringInProgressState,
|
||||||
onMeasure: () => _bloc.add(const MeasureEvent()),
|
onSwitchEvSourceType: context.read<Environment>().hasLightSensor
|
||||||
onSettings: () => Navigator.pushNamed(context, 'settings'),
|
? EvSourceTypeProvider.of(context).toggleType
|
||||||
|
: null,
|
||||||
|
onMeasure: () => _bloc.add(const MeasureEvent()),
|
||||||
|
onSettings: () => Navigator.pushNamed(context, 'settings'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _MeteringContainerBuidler extends StatelessWidget {
|
||||||
|
final ExposurePair? fastest;
|
||||||
|
final ExposurePair? slowest;
|
||||||
|
final IsoValue iso;
|
||||||
|
final NdValue nd;
|
||||||
|
final ValueChanged<IsoValue> onIsoChanged;
|
||||||
|
final ValueChanged<NdValue> onNdChanged;
|
||||||
|
final List<ExposurePair> exposurePairs;
|
||||||
|
|
||||||
|
const _MeteringContainerBuidler({
|
||||||
|
required this.fastest,
|
||||||
|
required this.slowest,
|
||||||
|
required this.iso,
|
||||||
|
required this.nd,
|
||||||
|
required this.onIsoChanged,
|
||||||
|
required this.onNdChanged,
|
||||||
|
required this.exposurePairs,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return context.watch<EvSourceType>() == EvSourceType.camera
|
||||||
|
? CameraContainerProvider(
|
||||||
|
fastest: fastest,
|
||||||
|
slowest: slowest,
|
||||||
|
iso: iso,
|
||||||
|
nd: nd,
|
||||||
|
onIsoChanged: onIsoChanged,
|
||||||
|
onNdChanged: onNdChanged,
|
||||||
|
exposurePairs: exposurePairs,
|
||||||
|
)
|
||||||
|
: LightSensorContainerProvider(
|
||||||
|
fastest: fastest,
|
||||||
|
slowest: slowest,
|
||||||
|
iso: iso,
|
||||||
|
nd: nd,
|
||||||
|
onIsoChanged: onIsoChanged,
|
||||||
|
onNdChanged: onNdChanged,
|
||||||
|
exposurePairs: exposurePairs,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,16 +2,22 @@ import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||||
import 'package:lightmeter/data/models/photography_values/iso_value.dart';
|
import 'package:lightmeter/data/models/photography_values/iso_value.dart';
|
||||||
import 'package:lightmeter/data/models/photography_values/nd_value.dart';
|
import 'package:lightmeter/data/models/photography_values/nd_value.dart';
|
||||||
|
|
||||||
class MeteringState {
|
abstract class MeteringState {
|
||||||
|
const MeteringState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoadingState extends MeteringState {
|
||||||
|
const LoadingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class MeteringDataState extends MeteringState {
|
||||||
final double ev;
|
final double ev;
|
||||||
final double evCompensation;
|
|
||||||
final IsoValue iso;
|
final IsoValue iso;
|
||||||
final NdValue nd;
|
final NdValue nd;
|
||||||
final List<ExposurePair> exposurePairs;
|
final List<ExposurePair> exposurePairs;
|
||||||
|
|
||||||
const MeteringState({
|
const MeteringDataState({
|
||||||
required this.ev,
|
required this.ev,
|
||||||
required this.evCompensation,
|
|
||||||
required this.iso,
|
required this.iso,
|
||||||
required this.nd,
|
required this.nd,
|
||||||
required this.exposurePairs,
|
required this.exposurePairs,
|
||||||
|
@ -20,3 +26,21 @@ class MeteringState {
|
||||||
ExposurePair? get fastest => exposurePairs.isEmpty ? null : exposurePairs.first;
|
ExposurePair? get fastest => exposurePairs.isEmpty ? null : exposurePairs.first;
|
||||||
ExposurePair? get slowest => exposurePairs.isEmpty ? null : exposurePairs.last;
|
ExposurePair? get slowest => exposurePairs.isEmpty ? null : exposurePairs.last;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MeteringInProgressState extends MeteringDataState {
|
||||||
|
MeteringInProgressState({
|
||||||
|
required super.ev,
|
||||||
|
required super.iso,
|
||||||
|
required super.nd,
|
||||||
|
required super.exposurePairs,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class MeteringEndedState extends MeteringDataState {
|
||||||
|
MeteringEndedState({
|
||||||
|
required super.ev,
|
||||||
|
required super.iso,
|
||||||
|
required super.nd,
|
||||||
|
required super.exposurePairs,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
|
import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart';
|
||||||
|
|
||||||
|
import 'components/report_issue/widget_list_tile_report_issue.dart';
|
||||||
|
import 'components/source_code/widget_list_tile_source_code.dart';
|
||||||
|
import 'components/version/widget_list_tile_version.dart';
|
||||||
|
import 'components/write_email/widget_list_tile_write_email.dart';
|
||||||
|
|
||||||
|
class AboutSettingsSection extends StatelessWidget {
|
||||||
|
const AboutSettingsSection({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SettingsSection(
|
||||||
|
title: S.of(context).about,
|
||||||
|
children: const [
|
||||||
|
SourceCodeListTile(),
|
||||||
|
ReportIssueListTile(),
|
||||||
|
WriteEmailListTile(),
|
||||||
|
VersionListTile(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
|
import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart';
|
||||||
|
|
||||||
|
import 'components/caffeine/provider_list_tile_caffeine.dart';
|
||||||
|
import 'components/haptics/provider_list_tile_haptics.dart';
|
||||||
|
import 'components/language/widget_list_tile_language.dart';
|
||||||
|
|
||||||
|
class GeneralSettingsSection extends StatelessWidget {
|
||||||
|
const GeneralSettingsSection({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SettingsSection(
|
||||||
|
title: S.of(context).general,
|
||||||
|
children: const [
|
||||||
|
CaffeineListTileProvider(),
|
||||||
|
HapticsListTileProvider(),
|
||||||
|
LanguageListTile(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -101,7 +101,7 @@ class _CalibrationUnit extends StatelessWidget {
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
trailing: Text(S.of(context).ev(value.toStringSignedAsFixed(1))),
|
trailing: Text(S.of(context).evValue(value.toStringSignedAsFixed(1))),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
|
import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart';
|
||||||
|
|
||||||
|
import 'components/calibration/widget_list_tile_calibration.dart';
|
||||||
|
import 'components/fractional_stops/widget_list_tile_fractional_stops.dart';
|
||||||
|
|
||||||
|
class MeteringSettingsSection extends StatelessWidget {
|
||||||
|
const MeteringSettingsSection({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SettingsSection(
|
||||||
|
title: S.of(context).metering,
|
||||||
|
children: const [
|
||||||
|
StopTypeListTile(),
|
||||||
|
CalibrationListTile(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:lightmeter/data/models/dynamic_colors_state.dart';
|
||||||
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
|
import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart';
|
||||||
|
|
||||||
|
import 'components/dynamic_color/widget_list_tile_dynamic_color.dart';
|
||||||
|
import 'components/primary_color/widget_list_tile_primary_color.dart';
|
||||||
|
import 'components/theme_type/widget_list_tile_theme_type.dart';
|
||||||
|
|
||||||
|
class ThemeSettingsSection extends StatelessWidget {
|
||||||
|
const ThemeSettingsSection({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SettingsSection(
|
||||||
|
title: S.of(context).theme,
|
||||||
|
children: [
|
||||||
|
const ThemeTypeListTile(),
|
||||||
|
const PrimaryColorListTile(),
|
||||||
|
if (context.read<DynamicColorState>() != DynamicColorState.unavailable)
|
||||||
|
const DynamicColorListTile(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,22 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/data/models/dynamic_colors_state.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 'package:provider/provider.dart';
|
|
||||||
|
|
||||||
import 'components/caffeine/provider_list_tile_caffeine.dart';
|
import 'components/about/widget_settings_section_about.dart';
|
||||||
import 'components/calibration/widget_list_tile_calibration.dart';
|
import 'components/general/widget_settings_section_general.dart';
|
||||||
import 'components/haptics/provider_list_tile_haptics.dart';
|
import 'components/metering/widget_settings_section_metering.dart';
|
||||||
import 'components/language/widget_list_tile_language.dart';
|
import 'components/theme/widget_settings_section_theme.dart';
|
||||||
import 'components/primary_color/widget_list_tile_primary_color.dart';
|
|
||||||
import 'components/report_issue/widget_list_tile_report_issue.dart';
|
|
||||||
import 'components/shared/settings_section/widget_settings_section.dart';
|
|
||||||
import 'components/source_code/widget_list_tile_source_code.dart';
|
|
||||||
import 'components/dynamic_color/widget_list_tile_dynamic_color.dart';
|
|
||||||
import 'components/theme_type/widget_list_tile_theme_type.dart';
|
|
||||||
import 'components/version/widget_list_tile_version.dart';
|
|
||||||
import 'components/fractional_stops/widget_list_tile_fractional_stops.dart';
|
|
||||||
import 'components/write_email/widget_list_tile_write_email.dart';
|
|
||||||
|
|
||||||
class SettingsScreen extends StatelessWidget {
|
class SettingsScreen extends StatelessWidget {
|
||||||
const SettingsScreen({super.key});
|
const SettingsScreen({super.key});
|
||||||
|
@ -54,39 +43,10 @@ class SettingsScreen extends StatelessWidget {
|
||||||
SliverList(
|
SliverList(
|
||||||
delegate: SliverChildListDelegate(
|
delegate: SliverChildListDelegate(
|
||||||
<Widget>[
|
<Widget>[
|
||||||
SettingsSection(
|
const MeteringSettingsSection(),
|
||||||
title: S.of(context).metering,
|
const GeneralSettingsSection(),
|
||||||
children: const [
|
const ThemeSettingsSection(),
|
||||||
StopTypeListTile(),
|
const AboutSettingsSection(),
|
||||||
CalibrationListTile(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SettingsSection(
|
|
||||||
title: S.of(context).general,
|
|
||||||
children: const [
|
|
||||||
CaffeineListTileProvider(),
|
|
||||||
HapticsListTileProvider(),
|
|
||||||
LanguageListTile(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SettingsSection(
|
|
||||||
title: S.of(context).theme,
|
|
||||||
children: [
|
|
||||||
const ThemeTypeListTile(),
|
|
||||||
const PrimaryColorListTile(),
|
|
||||||
if (context.read<DynamicColorState>() != DynamicColorState.unavailable)
|
|
||||||
const DynamicColorListTile(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SettingsSection(
|
|
||||||
title: S.of(context).about,
|
|
||||||
children: const [
|
|
||||||
SourceCodeListTile(),
|
|
||||||
ReportIssueListTile(),
|
|
||||||
WriteEmailListTile(),
|
|
||||||
VersionListTile(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SizedBox(height: MediaQuery.of(context).padding.bottom),
|
SizedBox(height: MediaQuery.of(context).padding.bottom),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
35
pubspec.yaml
35
pubspec.yaml
|
@ -1,7 +1,7 @@
|
||||||
name: lightmeter
|
name: lightmeter
|
||||||
description: A new Flutter project.
|
description: A new Flutter project.
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
version: 0.6.1+10
|
version: 0.8.1+13
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.18.0 <3.0.0"
|
sdk: ">=2.18.0 <3.0.0"
|
||||||
|
@ -40,37 +40,6 @@ dependency_overrides:
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
|
||||||
# assets:
|
|
||||||
# - images/a_dot_burr.jpeg
|
|
||||||
# - images/a_dot_ham.jpeg
|
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
|
||||||
|
|
||||||
# For details regarding adding assets from package dependencies, see
|
|
||||||
# https://flutter.dev/assets-and-images/#from-packages
|
|
||||||
|
|
||||||
# To add custom fonts to your application, add a fonts section here,
|
|
||||||
# in this "flutter" section. Each entry in this list should have a
|
|
||||||
# "family" key with the font family name, and a "fonts" key with a
|
|
||||||
# list giving the asset and other descriptors for the font. For
|
|
||||||
# example:
|
|
||||||
# fonts:
|
|
||||||
# - family: Schyler
|
|
||||||
# fonts:
|
|
||||||
# - asset: fonts/Schyler-Regular.ttf
|
|
||||||
# - asset: fonts/Schyler-Italic.ttf
|
|
||||||
# style: italic
|
|
||||||
# - family: Trajan Pro
|
|
||||||
# fonts:
|
|
||||||
# - asset: fonts/TrajanPro.ttf
|
|
||||||
# - asset: fonts/TrajanPro_Bold.ttf
|
|
||||||
# weight: 700
|
|
||||||
#
|
|
||||||
# For details regarding fonts from package dependencies,
|
|
||||||
# see https://flutter.dev/custom-fonts/#from-packages
|
|
||||||
|
|
||||||
flutter_icons:
|
flutter_icons:
|
||||||
image_path_android: "assets/launcher_icon_circle.png"
|
image_path_android: "assets/launcher_icon_circle.png"
|
||||||
android: "launcher_icon"
|
android: "launcher_icon"
|
||||||
|
@ -78,7 +47,7 @@ flutter_icons:
|
||||||
image_path_ios: "assets/launcher_icon_square.png"
|
image_path_ios: "assets/launcher_icon_square.png"
|
||||||
ios: true
|
ios: true
|
||||||
remove_alpha_ios: true
|
remove_alpha_ios: true
|
||||||
min_sdk_android: 21 # android min sdk min:16, default 21
|
min_sdk_android: 21
|
||||||
|
|
||||||
flutter_intl:
|
flutter_intl:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
Loading…
Reference in a new issue