mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-25 08:50:40 +00:00
Format & tasks
This commit is contained in:
parent
130f5ff0b2
commit
42fe5d45bc
30 changed files with 293 additions and 119 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -50,3 +50,5 @@ app.*.map.json
|
||||||
|
|
||||||
pubspec.lock
|
pubspec.lock
|
||||||
/ios/Podfile.lock
|
/ios/Podfile.lock
|
||||||
|
|
||||||
|
.fvm/
|
24
.vscode/settings.json
vendored
Normal file
24
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"task.slowProviderWarning": true,
|
||||||
|
"dart.flutterSdkPaths": [
|
||||||
|
"fvm"
|
||||||
|
],
|
||||||
|
"dart.lineLength": 100,
|
||||||
|
"[dart]": {
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.formatOnType": true,
|
||||||
|
"editor.rulers": [
|
||||||
|
100,
|
||||||
|
120,
|
||||||
|
],
|
||||||
|
"editor.selectionHighlight": true,
|
||||||
|
"editor.suggest.snippetsPreventQuickSuggestions": false,
|
||||||
|
"editor.suggestSelection": "first",
|
||||||
|
"editor.tabCompletion": "onlySnippets",
|
||||||
|
"editor.wordBasedSuggestions": false
|
||||||
|
},
|
||||||
|
"dart.doNotFormat": [
|
||||||
|
"**/generated/**",
|
||||||
|
"lib/data/**"
|
||||||
|
]
|
||||||
|
}
|
53
.vscode/tasks.json
vendored
Normal file
53
.vscode/tasks.json
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "fvm_build_android_dev",
|
||||||
|
"type": "shell",
|
||||||
|
"command": ".fvm/flutter_sdk/bin/flutter",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"apk",
|
||||||
|
"--flavor",
|
||||||
|
"dev",
|
||||||
|
"--release",
|
||||||
|
"--dart-define",
|
||||||
|
"cameraPreviewAspectRatio=2/3",
|
||||||
|
"-t",
|
||||||
|
"lib/main_dev.dart",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "fvm_build_android_prod",
|
||||||
|
"type": "shell",
|
||||||
|
"command": ".fvm/flutter_sdk/bin/flutter",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"apk",
|
||||||
|
"--flavor",
|
||||||
|
"prod",
|
||||||
|
"--release",
|
||||||
|
"--dart-define",
|
||||||
|
"cameraPreviewAspectRatio=2/3",
|
||||||
|
"-t",
|
||||||
|
"lib/main_prod.dart",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "fvm_build_appbundle",
|
||||||
|
"type": "shell",
|
||||||
|
"command": ".fvm/flutter_sdk/bin/flutter",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"appbundle",
|
||||||
|
"--flavor",
|
||||||
|
"prod",
|
||||||
|
"--release",
|
||||||
|
"--dart-define",
|
||||||
|
"cameraPreviewAspectRatio=2/3",
|
||||||
|
"-t",
|
||||||
|
"lib/main_prod.dart",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
|
@ -49,6 +49,18 @@ android {
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
debug {}
|
||||||
|
/*
|
||||||
|
release {
|
||||||
|
keyAlias keystoreProperties['keyAlias']
|
||||||
|
keyPassword keystoreProperties['keyPassword']
|
||||||
|
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||||
|
storePassword keystoreProperties['storePassword']
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
flavorDimensions "app"
|
flavorDimensions "app"
|
||||||
productFlavors {
|
productFlavors {
|
||||||
dev {
|
dev {
|
||||||
|
@ -62,6 +74,17 @@ android {
|
||||||
signingConfig signingConfigs.debug
|
signingConfig signingConfigs.debug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
debug {
|
||||||
|
signingConfig signingConfigs.debug
|
||||||
|
}
|
||||||
|
release {
|
||||||
|
signingConfig signingConfigs.debug
|
||||||
|
minifyEnabled true
|
||||||
|
shrinkResources true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flutter {
|
flutter {
|
||||||
|
|
|
@ -39,13 +39,15 @@ class Application extends StatelessWidget {
|
||||||
child: StopTypeProvider(
|
child: StopTypeProvider(
|
||||||
child: ThemeProvider(
|
child: ThemeProvider(
|
||||||
builder: (context, _) {
|
builder: (context, _) {
|
||||||
final systemIconsBrightness =
|
final systemIconsBrightness = ThemeData.estimateBrightnessForColor(
|
||||||
ThemeData.estimateBrightnessForColor(context.watch<ThemeData>().colorScheme.onSurface);
|
context.watch<ThemeData>().colorScheme.onSurface,
|
||||||
|
);
|
||||||
return AnnotatedRegion(
|
return AnnotatedRegion(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarColor: Colors.transparent,
|
statusBarColor: Colors.transparent,
|
||||||
statusBarBrightness:
|
statusBarBrightness: systemIconsBrightness == Brightness.light
|
||||||
systemIconsBrightness == Brightness.light ? Brightness.dark : Brightness.light,
|
? Brightness.dark
|
||||||
|
: Brightness.light,
|
||||||
statusBarIconBrightness: systemIconsBrightness,
|
statusBarIconBrightness: systemIconsBrightness,
|
||||||
systemNavigationBarColor: context.watch<ThemeData>().colorScheme.surface,
|
systemNavigationBarColor: context.watch<ThemeData>().colorScheme.surface,
|
||||||
systemNavigationBarIconBrightness: systemIconsBrightness,
|
systemNavigationBarIconBrightness: systemIconsBrightness,
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
enum ThemeType {light, dark, systemDefault}
|
enum ThemeType { light, dark, systemDefault }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class PlatformConfig {
|
class PlatformConfig {
|
||||||
static double get cameraPreviewAspectRatio {
|
static double get cameraPreviewAspectRatio {
|
||||||
final rational = const String.fromEnvironment('cameraPreviewAspectRatio', defaultValue: "3/4").split('/');
|
final rational = const String.fromEnvironment('cameraPreviewAspectRatio').split('/');
|
||||||
return int.parse(rational[0]) / int.parse(rational[1]);
|
return int.parse(rational[0]) / int.parse(rational[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,10 @@ class ThemeProvider extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ThemeProviderState extends State<ThemeProvider> {
|
class ThemeProviderState extends State<ThemeProvider> {
|
||||||
late final _themeTypeNotifier = ValueNotifier<ThemeType>(context.read<UserPreferencesService>().themeType);
|
UserPreferencesService get _prefs => context.read<UserPreferencesService>();
|
||||||
late final _dynamicColorNotifier = ValueNotifier<bool>(context.read<UserPreferencesService>().dynamicColor);
|
|
||||||
|
late final _themeTypeNotifier = ValueNotifier<ThemeType>(_prefs.themeType);
|
||||||
|
late final _dynamicColorNotifier = ValueNotifier<bool>(_prefs.dynamicColor);
|
||||||
late final _primaryColorNotifier = ValueNotifier<Color>(const Color(0xFF2196f3));
|
late final _primaryColorNotifier = ValueNotifier<Color>(const Color(0xFF2196f3));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -64,7 +66,7 @@ class ThemeProviderState extends State<ThemeProvider> {
|
||||||
|
|
||||||
void setThemeType(ThemeType themeType) {
|
void setThemeType(ThemeType themeType) {
|
||||||
_themeTypeNotifier.value = themeType;
|
_themeTypeNotifier.value = themeType;
|
||||||
context.read<UserPreferencesService>().themeType = themeType;
|
_prefs.themeType = themeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
Brightness get _themeBrightness {
|
Brightness get _themeBrightness {
|
||||||
|
@ -80,7 +82,7 @@ class ThemeProviderState extends State<ThemeProvider> {
|
||||||
|
|
||||||
void enableDynamicColor(bool enable) {
|
void enableDynamicColor(bool enable) {
|
||||||
_dynamicColorNotifier.value = enable;
|
_dynamicColorNotifier.value = enable;
|
||||||
context.read<UserPreferencesService>().dynamicColor = enable;
|
_prefs.dynamicColor = enable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +105,8 @@ class _DynamicColorProvider extends StatelessWidget {
|
||||||
late final Color? dynamicPrimaryColor;
|
late final Color? dynamicPrimaryColor;
|
||||||
if (lightDynamic != null && darkDynamic != null) {
|
if (lightDynamic != null && darkDynamic != null) {
|
||||||
if (useDynamicColor) {
|
if (useDynamicColor) {
|
||||||
dynamicPrimaryColor = (themeBrightness == Brightness.light ? lightDynamic : darkDynamic).primary;
|
dynamicPrimaryColor =
|
||||||
|
(themeBrightness == Brightness.light ? lightDynamic : darkDynamic).primary;
|
||||||
state = DynamicColorState.enabled;
|
state = DynamicColorState.enabled;
|
||||||
} else {
|
} else {
|
||||||
dynamicPrimaryColor = null;
|
dynamicPrimaryColor = null;
|
||||||
|
@ -153,7 +156,9 @@ class _ThemeDataProvider extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorScheme _colorSchemeFromColor() {
|
ColorScheme _colorSchemeFromColor() {
|
||||||
final scheme = brightness == Brightness.light ? Scheme.light(primaryColor.value) : Scheme.dark(primaryColor.value);
|
final scheme = brightness == Brightness.light
|
||||||
|
? Scheme.light(primaryColor.value)
|
||||||
|
: Scheme.dark(primaryColor.value);
|
||||||
return ColorScheme(
|
return ColorScheme(
|
||||||
brightness: brightness,
|
brightness: brightness,
|
||||||
primary: Color(scheme.primary),
|
primary: Color(scheme.primary),
|
||||||
|
@ -168,7 +173,8 @@ class _ThemeDataProvider extends StatelessWidget {
|
||||||
onBackground: Color(scheme.onBackground),
|
onBackground: Color(scheme.onBackground),
|
||||||
surface: Color.alphaBlend(Color(scheme.primary).withOpacity(0.05), Color(scheme.background)),
|
surface: Color.alphaBlend(Color(scheme.primary).withOpacity(0.05), Color(scheme.background)),
|
||||||
onSurface: Color(scheme.onSurface),
|
onSurface: Color(scheme.onSurface),
|
||||||
surfaceVariant: Color.alphaBlend(Color(scheme.primary).withOpacity(0.5), Color(scheme.background)),
|
surfaceVariant:
|
||||||
|
Color.alphaBlend(Color(scheme.primary).withOpacity(0.5), Color(scheme.background)),
|
||||||
onSurfaceVariant: Color(scheme.onSurfaceVariant),
|
onSurfaceVariant: Color(scheme.onSurfaceVariant),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,10 @@ 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/metering_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'
|
||||||
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states;
|
as communication_events;
|
||||||
|
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';
|
||||||
|
|
||||||
import 'communication/bloc_communication_metering.dart';
|
import 'communication/bloc_communication_metering.dart';
|
||||||
|
@ -132,7 +134,8 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
evSteps = (ev / 0.3).floor();
|
evSteps = (ev / 0.3).floor();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
final evOffset = _shutterSpeedValues.indexOf(const ShutterSpeedValue(1, false, StopType.full)) - evSteps;
|
final evOffset =
|
||||||
|
_shutterSpeedValues.indexOf(const ShutterSpeedValue(1, false, StopType.full)) - evSteps;
|
||||||
|
|
||||||
late final int apertureOffset;
|
late final int apertureOffset;
|
||||||
late final int shutterSpeedOffset;
|
late final int shutterSpeedOffset;
|
||||||
|
@ -144,7 +147,8 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
shutterSpeedOffset = 0;
|
shutterSpeedOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int itemsCount = min(_apertureValues.length + shutterSpeedOffset, _shutterSpeedValues.length + apertureOffset) -
|
int itemsCount = min(_apertureValues.length + shutterSpeedOffset,
|
||||||
|
_shutterSpeedValues.length + apertureOffset) -
|
||||||
max(apertureOffset, shutterSpeedOffset);
|
max(apertureOffset, shutterSpeedOffset);
|
||||||
|
|
||||||
if (itemsCount < 0) {
|
if (itemsCount < 0) {
|
||||||
|
|
|
@ -3,7 +3,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'event_communication_metering.dart';
|
import 'event_communication_metering.dart';
|
||||||
import 'state_communication_metering.dart';
|
import 'state_communication_metering.dart';
|
||||||
|
|
||||||
class MeteringCommunicationBloc extends Bloc<MeteringCommunicationEvent, MeteringCommunicationState> {
|
class MeteringCommunicationBloc
|
||||||
|
extends Bloc<MeteringCommunicationEvent, MeteringCommunicationState> {
|
||||||
MeteringCommunicationBloc() : super(const InitState()) {
|
MeteringCommunicationBloc() : super(const InitState()) {
|
||||||
on<MeasureEvent>((_, emit) => emit(const MeasureState()));
|
on<MeasureEvent>((_, emit) => emit(const MeasureState()));
|
||||||
on<MeasuredEvent>((event, emit) => emit(MeasuredState(event.ev100)));
|
on<MeasuredEvent>((event, emit) => emit(MeasuredState(event.ev100)));
|
||||||
|
|
|
@ -6,7 +6,11 @@ class ExposurePairsListItem<T extends PhotographyStopValue> extends StatelessWid
|
||||||
final T value;
|
final T value;
|
||||||
final bool tickOnTheLeft;
|
final bool tickOnTheLeft;
|
||||||
|
|
||||||
const ExposurePairsListItem(this.value, {required this.tickOnTheLeft, super.key});
|
const ExposurePairsListItem(
|
||||||
|
this.value, {
|
||||||
|
required this.tickOnTheLeft,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/exposure_pairs_list/components/widget_item_list_exposure_pairs.dart';
|
|
||||||
|
import 'components/widget_item_list_exposure_pairs.dart';
|
||||||
|
|
||||||
class ExposurePairsList extends StatelessWidget {
|
class ExposurePairsList extends StatelessWidget {
|
||||||
final List<ExposurePair> exposurePairs;
|
final List<ExposurePair> exposurePairs;
|
||||||
|
@ -51,7 +52,9 @@ class ExposurePairsList extends StatelessWidget {
|
||||||
builder: (context, constraints) => Align(
|
builder: (context, constraints) => Align(
|
||||||
alignment: index == 0
|
alignment: index == 0
|
||||||
? Alignment.bottomCenter
|
? Alignment.bottomCenter
|
||||||
: (index == exposurePairs.length - 1 ? Alignment.topCenter : Alignment.center),
|
: (index == exposurePairs.length - 1
|
||||||
|
? Alignment.topCenter
|
||||||
|
: Alignment.center),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: index == 0 || index == exposurePairs.length - 1
|
height: index == 0 || index == exposurePairs.length - 1
|
||||||
? constraints.maxHeight / 2
|
? constraints.maxHeight / 2
|
||||||
|
|
|
@ -60,18 +60,19 @@ class _ReadingValueBuilder extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final textTheme = Theme.of(context).textTheme;
|
final textTheme = Theme.of(context).textTheme;
|
||||||
|
final textColor = Theme.of(context).colorScheme.onPrimaryContainer;
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
reading.label,
|
reading.label,
|
||||||
style: textTheme.labelMedium?.copyWith(color: Theme.of(context).colorScheme.onPrimaryContainer),
|
style: textTheme.labelMedium?.copyWith(color: textColor),
|
||||||
),
|
),
|
||||||
const SizedBox(height: Dimens.grid4),
|
const SizedBox(height: Dimens.grid4),
|
||||||
Text(
|
Text(
|
||||||
reading.value,
|
reading.value,
|
||||||
style: textTheme.titleMedium?.copyWith(color: Theme.of(context).colorScheme.onPrimaryContainer),
|
style: textTheme.titleMedium?.copyWith(color: textColor),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -47,7 +47,10 @@ class AnimatedDialogState extends State<AnimatedDialog> with SingleTickerProvide
|
||||||
reverseDuration: Dimens.durationML,
|
reverseDuration: Dimens.durationML,
|
||||||
vsync: this,
|
vsync: this,
|
||||||
);
|
);
|
||||||
_defaultCurvedAnimation = CurvedAnimation(parent: _animationController, curve: Curves.easeInOut);
|
_defaultCurvedAnimation = CurvedAnimation(
|
||||||
|
parent: _animationController,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
_barrierColorAnimation = ColorTween(
|
_barrierColorAnimation = ColorTween(
|
||||||
begin: Colors.transparent,
|
begin: Colors.transparent,
|
||||||
end: Colors.black54,
|
end: Colors.black54,
|
||||||
|
|
|
@ -55,6 +55,8 @@ class CameraView extends StatelessWidget {
|
||||||
DeviceOrientation _getApplicableOrientation(CameraValue value) {
|
DeviceOrientation _getApplicableOrientation(CameraValue value) {
|
||||||
return value.isRecordingVideo
|
return value.isRecordingVideo
|
||||||
? value.recordingOrientation!
|
? value.recordingOrientation!
|
||||||
: (value.previewPauseOrientation ?? value.lockedCaptureOrientation ?? value.deviceOrientation);
|
: (value.previewPauseOrientation ??
|
||||||
|
value.lockedCaptureOrientation ??
|
||||||
|
value.deviceOrientation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,8 @@ import 'package:lightmeter/data/models/photography_values/photography_value.dart
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
|
|
||||||
typedef DialogPickerItemBuilder<T extends PhotographyValue> = Widget Function(BuildContext, T);
|
typedef DialogPickerItemBuilder<T extends PhotographyValue> = Widget Function(BuildContext, T);
|
||||||
typedef DialogPickerEvDifferenceBuilder<T extends PhotographyValue> = String Function(T selected, T other);
|
typedef DialogPickerEvDifferenceBuilder<T extends PhotographyValue> = String Function(
|
||||||
|
T selected, T other);
|
||||||
|
|
||||||
class MeteringScreenDialogPicker<T extends PhotographyValue> extends StatefulWidget {
|
class MeteringScreenDialogPicker<T extends PhotographyValue> extends StatefulWidget {
|
||||||
final String title;
|
final String title;
|
||||||
|
@ -32,7 +33,8 @@ class MeteringScreenDialogPicker<T extends PhotographyValue> extends StatefulWid
|
||||||
State<MeteringScreenDialogPicker<T>> createState() => _MeteringScreenDialogPickerState<T>();
|
State<MeteringScreenDialogPicker<T>> createState() => _MeteringScreenDialogPickerState<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MeteringScreenDialogPickerState<T extends PhotographyValue> extends State<MeteringScreenDialogPicker<T>> {
|
class _MeteringScreenDialogPickerState<T extends PhotographyValue>
|
||||||
|
extends State<MeteringScreenDialogPicker<T>> {
|
||||||
late T _selectedValue = widget.initialValue;
|
late T _selectedValue = widget.initialValue;
|
||||||
final _scrollController = ScrollController();
|
final _scrollController = ScrollController();
|
||||||
|
|
||||||
|
@ -101,7 +103,9 @@ 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(S.of(context).ev(widget.evDifferenceBuilder.call(_selectedValue, widget.values[index])))
|
? Text(S
|
||||||
|
.of(context)
|
||||||
|
.ev(widget.evDifferenceBuilder.call(_selectedValue, widget.values[index])))
|
||||||
: null,
|
: null,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
|
|
|
@ -54,7 +54,7 @@ class TopBarShape extends CustomPainter {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Bottom side with step
|
// Bottom side with step
|
||||||
final double allowedRadius = min(appendixHeight.abs() / 2, Dimens.borderRadiusL);
|
final allowedRadius = min(appendixHeight.abs() / 2, Dimens.borderRadiusL);
|
||||||
path.lineTo(appendixWidth - allowedRadius, size.height + appendixHeight);
|
path.lineTo(appendixWidth - allowedRadius, size.height + appendixHeight);
|
||||||
|
|
||||||
final bool isCutout = appendixHeight < 0;
|
final bool isCutout = appendixHeight < 0;
|
||||||
|
|
|
@ -174,7 +174,9 @@ class _NdValueTile extends StatelessWidget {
|
||||||
subtitle: S.of(context).ndFilterFactor,
|
subtitle: S.of(context).ndFilterFactor,
|
||||||
selectedValue: value,
|
selectedValue: value,
|
||||||
values: ndValues,
|
values: ndValues,
|
||||||
itemTitleBuilder: (_, value) => Text(value.value == 0 ? S.of(context).none : value.value.toString()),
|
itemTitleBuilder: (_, value) => Text(
|
||||||
|
value.value == 0 ? S.of(context).none : value.value.toString(),
|
||||||
|
),
|
||||||
// using descending order, because ND filter darkens image & lowers EV
|
// using descending order, because ND filter darkens image & lowers EV
|
||||||
evDifferenceBuilder: (selected, other) => other.toStringDifference(selected),
|
evDifferenceBuilder: (selected, other) => other.toStringDifference(selected),
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
|
|
|
@ -9,8 +9,10 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:lightmeter/interactors/metering_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'
|
||||||
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states;
|
as communication_event;
|
||||||
|
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';
|
||||||
|
|
||||||
import 'event_camera.dart';
|
import 'event_camera.dart';
|
||||||
|
@ -61,7 +63,9 @@ class CameraBloc extends EvSourceBloc<CameraEvent, CameraState> {
|
||||||
if (communicationState is communication_states.MeasureState) {
|
if (communicationState is communication_states.MeasureState) {
|
||||||
_takePhoto().then((ev100) {
|
_takePhoto().then((ev100) {
|
||||||
if (ev100 != null) {
|
if (ev100 != null) {
|
||||||
communicationBloc.add(communication_event.MeasuredEvent(ev100 + _meteringInteractor.cameraEvCalibration));
|
communicationBloc.add(
|
||||||
|
communication_event.MeasuredEvent(ev100 + _meteringInteractor.cameraEvCalibration),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -92,7 +96,12 @@ class CameraBloc extends EvSourceBloc<CameraEvent, CameraState> {
|
||||||
_exposureOffsetRange = await Future.wait<double>([
|
_exposureOffsetRange = await Future.wait<double>([
|
||||||
_cameraController!.getMinExposureOffset(),
|
_cameraController!.getMinExposureOffset(),
|
||||||
_cameraController!.getMaxExposureOffset(),
|
_cameraController!.getMaxExposureOffset(),
|
||||||
]).then((levels) => RangeValues(max(_exposureMaxRange.start, levels[0]), min(_exposureMaxRange.end, levels[1])));
|
]).then(
|
||||||
|
(levels) => RangeValues(
|
||||||
|
max(_exposureMaxRange.start, levels[0]),
|
||||||
|
min(_exposureMaxRange.end, levels[1]),
|
||||||
|
),
|
||||||
|
);
|
||||||
await _cameraController!.getExposureOffsetStepSize().then((value) {
|
await _cameraController!.getExposureOffsetStepSize().then((value) {
|
||||||
_exposureStep = value == 0 ? 0.1 : value;
|
_exposureStep = value == 0 ? 0.1 : value;
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_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/state_communication_metering.dart' as communication_states;
|
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart'
|
||||||
|
as communication_states;
|
||||||
|
|
||||||
abstract class EvSourceBloc<E, S> extends Bloc<E, S> {
|
abstract class EvSourceBloc<E, S> extends Bloc<E, S> {
|
||||||
final MeteringCommunicationBloc communicationBloc;
|
final MeteringCommunicationBloc communicationBloc;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
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'
|
||||||
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states;
|
as communication_event;
|
||||||
|
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart'
|
||||||
|
as communication_states;
|
||||||
|
|
||||||
import 'event_random_ev.dart';
|
import 'event_random_ev.dart';
|
||||||
import 'state_random_ev.dart';
|
import 'state_random_ev.dart';
|
||||||
|
|
|
@ -22,10 +22,12 @@ class MeteringScreen extends StatefulWidget {
|
||||||
class _MeteringScreenState extends State<MeteringScreen> {
|
class _MeteringScreenState extends State<MeteringScreen> {
|
||||||
double topBarOverflow = 0.0;
|
double topBarOverflow = 0.0;
|
||||||
|
|
||||||
|
MeteringBloc get _bloc => context.read<MeteringBloc>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
context.read<MeteringBloc>().add(StopTypeChangedEvent(context.watch<StopType>()));
|
_bloc.add(StopTypeChangedEvent(context.watch<StopType>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -33,10 +35,7 @@ class _MeteringScreenState extends State<MeteringScreen> {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Theme.of(context).colorScheme.background,
|
backgroundColor: Theme.of(context).colorScheme.background,
|
||||||
body: BlocBuilder<MeteringBloc, MeteringState>(
|
body: BlocBuilder<MeteringBloc, MeteringState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) => Column(
|
||||||
return Stack(
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
children: [
|
children: [
|
||||||
MeteringTopBar(
|
MeteringTopBar(
|
||||||
fastest: state.fastest,
|
fastest: state.fastest,
|
||||||
|
@ -44,31 +43,18 @@ class _MeteringScreenState extends State<MeteringScreen> {
|
||||||
ev: state.ev,
|
ev: state.ev,
|
||||||
iso: state.iso,
|
iso: state.iso,
|
||||||
nd: state.nd,
|
nd: state.nd,
|
||||||
onIsoChanged: (value) => context.read<MeteringBloc>().add(IsoChangedEvent(value)),
|
onIsoChanged: (value) => _bloc.add(IsoChangedEvent(value)),
|
||||||
onNdChanged: (value) => context.read<MeteringBloc>().add(NdChangedEvent(value)),
|
onNdChanged: (value) => _bloc.add(NdChangedEvent(value)),
|
||||||
onCutoutLayout: (value) => topBarOverflow = value,
|
onCutoutLayout: (value) => topBarOverflow = value,
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: LayoutBuilder(
|
|
||||||
builder: (context, constraints) => OverflowBox(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
maxHeight: constraints.maxHeight + topBarOverflow.abs(),
|
|
||||||
maxWidth: constraints.maxWidth,
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
|
padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
|
||||||
child: Row(
|
child: _MiddleContentWrapper(
|
||||||
children: [
|
topBarOverflow: topBarOverflow,
|
||||||
Expanded(
|
leftContent: ExposurePairsList(state.exposurePairs),
|
||||||
child: Padding(
|
rightContent: Padding(
|
||||||
padding: topBarOverflow >= 0 ? EdgeInsets.only(top: topBarOverflow) : EdgeInsets.zero,
|
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM),
|
||||||
child: ExposurePairsList(state.exposurePairs),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: Dimens.grid8),
|
|
||||||
Expanded(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM).add(
|
|
||||||
topBarOverflow <= 0 ? EdgeInsets.only(top: -topBarOverflow) : EdgeInsets.zero),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
children: const [
|
children: const [
|
||||||
Expanded(child: CameraExposureSlider()),
|
Expanded(child: CameraExposureSlider()),
|
||||||
|
@ -78,21 +64,54 @@ class _MeteringScreenState extends State<MeteringScreen> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
MeteringBottomControls(
|
MeteringBottomControls(
|
||||||
onMeasure: () => context.read<MeteringBloc>().add(const MeasureEvent()),
|
onMeasure: () => _bloc.add(const MeasureEvent()),
|
||||||
onSettings: () => Navigator.pushNamed(context, 'settings'),
|
onSettings: () => Navigator.pushNamed(context, 'settings'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MiddleContentWrapper extends StatelessWidget {
|
||||||
|
final double topBarOverflow;
|
||||||
|
final Widget leftContent;
|
||||||
|
final Widget rightContent;
|
||||||
|
|
||||||
|
const _MiddleContentWrapper({
|
||||||
|
required this.topBarOverflow,
|
||||||
|
required this.leftContent,
|
||||||
|
required this.rightContent,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (context, constraints) => OverflowBox(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
maxHeight: constraints.maxHeight + topBarOverflow.abs(),
|
||||||
|
maxWidth: constraints.maxWidth,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(top: topBarOverflow >= 0 ? topBarOverflow : 0),
|
||||||
|
child: leftContent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: Dimens.grid8),
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(top: topBarOverflow <= 0 ? -topBarOverflow : 0),
|
||||||
|
child: rightContent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ class CalibrationDialog extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CalibrationDialogState extends State<CalibrationDialog> {
|
class _CalibrationDialogState extends State<CalibrationDialog> {
|
||||||
|
CalibrationDialogBloc get bloc => context.read<CalibrationDialogBloc>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
|
@ -37,8 +39,8 @@ class _CalibrationDialogState extends State<CalibrationDialog> {
|
||||||
_CalibrationUnit(
|
_CalibrationUnit(
|
||||||
title: S.of(context).camera,
|
title: S.of(context).camera,
|
||||||
value: state.cameraEvCalibration,
|
value: state.cameraEvCalibration,
|
||||||
onChanged: (value) => context.read<CalibrationDialogBloc>().add(CameraEvCalibrationChangedEvent(value)),
|
onChanged: (value) => bloc.add(CameraEvCalibrationChangedEvent(value)),
|
||||||
onReset: () => context.read<CalibrationDialogBloc>().add(const CameraEvCalibrationResetEvent()),
|
onReset: () => bloc.add(const CameraEvCalibrationResetEvent()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -56,7 +58,7 @@ class _CalibrationDialogState extends State<CalibrationDialog> {
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.read<CalibrationDialogBloc>().add(const SaveCalibrationDialogEvent());
|
bloc.add(const SaveCalibrationDialogEvent());
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: Text(S.of(context).save),
|
child: Text(S.of(context).save),
|
||||||
|
|
|
@ -12,12 +12,9 @@ class VersionListTile extends StatelessWidget {
|
||||||
title: Text(S.of(context).version),
|
title: Text(S.of(context).version),
|
||||||
trailing: FutureBuilder<PackageInfo>(
|
trailing: FutureBuilder<PackageInfo>(
|
||||||
future: PackageInfo.fromPlatform(),
|
future: PackageInfo.fromPlatform(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) => snapshot.data != null
|
||||||
if (snapshot.data != null) {
|
? Text(S.of(context).versionNumber(snapshot.data!.version, snapshot.data!.buildNumber))
|
||||||
return Text(S.of(context).versionNumber(snapshot.data!.version, snapshot.data!.buildNumber));
|
: const SizedBox.shrink(),
|
||||||
}
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,8 @@ class WriteEmailListTile extends StatelessWidget {
|
||||||
leading: const Icon(Icons.email),
|
leading: const Icon(Icons.email),
|
||||||
title: Text(S.of(context).writeEmail),
|
title: Text(S.of(context).writeEmail),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
launchUrl(Uri.parse('mailto:${context.read<Environment>().contactEmail}?subject=M3 Lightmeter'));
|
launchUrl(
|
||||||
|
Uri.parse('mailto:${context.read<Environment>().contactEmail}?subject=M3 Lightmeter'));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,10 @@ class SettingsScreen extends StatelessWidget {
|
||||||
titlePadding: const EdgeInsets.all(Dimens.paddingM),
|
titlePadding: const EdgeInsets.all(Dimens.paddingM),
|
||||||
title: Text(
|
title: Text(
|
||||||
S.of(context).settings,
|
S.of(context).settings,
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: 24),
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
fontSize: 24,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
|
|
|
@ -51,8 +51,14 @@ class _CenteredSliderState extends State<CenteredSlider> {
|
||||||
quarterTurns: widget.isVertical ? -1 : 0,
|
quarterTurns: widget.isVertical ? -1 : 0,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
onTapUp: (details) => _updateHandlePosition(details.localPosition.dx, handleDistance),
|
onTapUp: (details) => _updateHandlePosition(
|
||||||
onHorizontalDragUpdate: (details) => _updateHandlePosition(details.localPosition.dx, handleDistance),
|
details.localPosition.dx,
|
||||||
|
handleDistance,
|
||||||
|
),
|
||||||
|
onHorizontalDragUpdate: (details) => _updateHandlePosition(
|
||||||
|
details.localPosition.dx,
|
||||||
|
handleDistance,
|
||||||
|
),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: Dimens.cameraSliderHandleSize,
|
height: Dimens.cameraSliderHandleSize,
|
||||||
width: biggestSize,
|
width: biggestSize,
|
||||||
|
@ -83,10 +89,8 @@ class _CenteredSliderState extends State<CenteredSlider> {
|
||||||
relativeValue = (offset - Dimens.cameraSliderHandleSize / 2) / handleDistance;
|
relativeValue = (offset - Dimens.cameraSliderHandleSize / 2) / handleDistance;
|
||||||
}
|
}
|
||||||
setState(() {});
|
setState(() {});
|
||||||
widget.onChanged(_clampToRange(relativeValue));
|
widget.onChanged(relativeValue * (widget.max - widget.min) + widget.min);
|
||||||
}
|
}
|
||||||
|
|
||||||
double _clampToRange(double relativeValue) => relativeValue * (widget.max - widget.min) + widget.min;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Slider extends StatelessWidget {
|
class _Slider extends StatelessWidget {
|
||||||
|
@ -111,7 +115,9 @@ class _Slider extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Positioned(
|
Positioned(
|
||||||
height: trackThickness,
|
height: trackThickness,
|
||||||
width: handleDistance + trackThickness, // add thickness to maintain radius overlap with handle
|
|
||||||
|
/// add thickness to maintain radius overlap with handle
|
||||||
|
width: handleDistance + trackThickness,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(trackThickness / 2),
|
borderRadius: BorderRadius.circular(trackThickness / 2),
|
||||||
child: ColoredBox(color: Theme.of(context).colorScheme.surfaceVariant),
|
child: ColoredBox(color: Theme.of(context).colorScheme.surfaceVariant),
|
||||||
|
|
Loading…
Reference in a new issue