Format & tasks

This commit is contained in:
Vadim 2023-01-26 18:03:48 +03:00
parent 130f5ff0b2
commit 42fe5d45bc
30 changed files with 293 additions and 119 deletions

2
.gitignore vendored
View file

@ -50,3 +50,5 @@ app.*.map.json
pubspec.lock
/ios/Podfile.lock
.fvm/

24
.vscode/settings.json vendored Normal file
View 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
View 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",
],
},
]
}

View file

@ -49,6 +49,18 @@ android {
versionName flutterVersionName
}
signingConfigs {
debug {}
/*
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
*/
}
flavorDimensions "app"
productFlavors {
dev {
@ -62,6 +74,17 @@ android {
signingConfig signingConfigs.debug
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
signingConfig signingConfigs.debug
minifyEnabled true
shrinkResources true
}
}
}
flutter {

View file

@ -39,13 +39,15 @@ class Application extends StatelessWidget {
child: StopTypeProvider(
child: ThemeProvider(
builder: (context, _) {
final systemIconsBrightness =
ThemeData.estimateBrightnessForColor(context.watch<ThemeData>().colorScheme.onSurface);
final systemIconsBrightness = ThemeData.estimateBrightnessForColor(
context.watch<ThemeData>().colorScheme.onSurface,
);
return AnnotatedRegion(
value: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarBrightness:
systemIconsBrightness == Brightness.light ? Brightness.dark : Brightness.light,
statusBarBrightness: systemIconsBrightness == Brightness.light
? Brightness.dark
: Brightness.light,
statusBarIconBrightness: systemIconsBrightness,
systemNavigationBarColor: context.watch<ThemeData>().colorScheme.surface,
systemNavigationBarIconBrightness: systemIconsBrightness,

View file

@ -1 +1 @@
enum ThemeType {light, dark, systemDefault}
enum ThemeType { light, dark, systemDefault }

View file

@ -1,6 +1,6 @@
class PlatformConfig {
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]);
}
}

View file

@ -25,8 +25,10 @@ class ThemeProvider extends StatefulWidget {
}
class ThemeProviderState extends State<ThemeProvider> {
late final _themeTypeNotifier = ValueNotifier<ThemeType>(context.read<UserPreferencesService>().themeType);
late final _dynamicColorNotifier = ValueNotifier<bool>(context.read<UserPreferencesService>().dynamicColor);
UserPreferencesService get _prefs => context.read<UserPreferencesService>();
late final _themeTypeNotifier = ValueNotifier<ThemeType>(_prefs.themeType);
late final _dynamicColorNotifier = ValueNotifier<bool>(_prefs.dynamicColor);
late final _primaryColorNotifier = ValueNotifier<Color>(const Color(0xFF2196f3));
@override
@ -64,7 +66,7 @@ class ThemeProviderState extends State<ThemeProvider> {
void setThemeType(ThemeType themeType) {
_themeTypeNotifier.value = themeType;
context.read<UserPreferencesService>().themeType = themeType;
_prefs.themeType = themeType;
}
Brightness get _themeBrightness {
@ -80,7 +82,7 @@ class ThemeProviderState extends State<ThemeProvider> {
void enableDynamicColor(bool enable) {
_dynamicColorNotifier.value = enable;
context.read<UserPreferencesService>().dynamicColor = enable;
_prefs.dynamicColor = enable;
}
}
@ -103,7 +105,8 @@ class _DynamicColorProvider extends StatelessWidget {
late final Color? dynamicPrimaryColor;
if (lightDynamic != null && darkDynamic != null) {
if (useDynamicColor) {
dynamicPrimaryColor = (themeBrightness == Brightness.light ? lightDynamic : darkDynamic).primary;
dynamicPrimaryColor =
(themeBrightness == Brightness.light ? lightDynamic : darkDynamic).primary;
state = DynamicColorState.enabled;
} else {
dynamicPrimaryColor = null;
@ -153,7 +156,9 @@ class _ThemeDataProvider extends StatelessWidget {
}
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(
brightness: brightness,
primary: Color(scheme.primary),
@ -168,7 +173,8 @@ class _ThemeDataProvider extends StatelessWidget {
onBackground: Color(scheme.onBackground),
surface: Color.alphaBlend(Color(scheme.primary).withOpacity(0.05), Color(scheme.background)),
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),
);
}

View file

@ -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/shared_prefs_service.dart';
import 'package:lightmeter/interactors/metering_interactor.dart';
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' as communication_events;
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states;
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart'
as communication_events;
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart'
as communication_states;
import 'package:lightmeter/utils/log_2.dart';
import 'communication/bloc_communication_metering.dart';
@ -132,7 +134,8 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
evSteps = (ev / 0.3).floor();
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 shutterSpeedOffset;
@ -144,7 +147,8 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
shutterSpeedOffset = 0;
}
int itemsCount = min(_apertureValues.length + shutterSpeedOffset, _shutterSpeedValues.length + apertureOffset) -
int itemsCount = min(_apertureValues.length + shutterSpeedOffset,
_shutterSpeedValues.length + apertureOffset) -
max(apertureOffset, shutterSpeedOffset);
if (itemsCount < 0) {

View file

@ -3,7 +3,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'event_communication_metering.dart';
import 'state_communication_metering.dart';
class MeteringCommunicationBloc extends Bloc<MeteringCommunicationEvent, MeteringCommunicationState> {
class MeteringCommunicationBloc
extends Bloc<MeteringCommunicationEvent, MeteringCommunicationState> {
MeteringCommunicationBloc() : super(const InitState()) {
on<MeasureEvent>((_, emit) => emit(const MeasureState()));
on<MeasuredEvent>((event, emit) => emit(MeasuredState(event.ev100)));

View file

@ -6,7 +6,11 @@ class ExposurePairsListItem<T extends PhotographyStopValue> extends StatelessWid
final T value;
final bool tickOnTheLeft;
const ExposurePairsListItem(this.value, {required this.tickOnTheLeft, super.key});
const ExposurePairsListItem(
this.value, {
required this.tickOnTheLeft,
super.key,
});
@override
Widget build(BuildContext context) {

View file

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/data/models/exposure_pair.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 {
final List<ExposurePair> exposurePairs;
@ -51,7 +52,9 @@ class ExposurePairsList extends StatelessWidget {
builder: (context, constraints) => Align(
alignment: index == 0
? Alignment.bottomCenter
: (index == exposurePairs.length - 1 ? Alignment.topCenter : Alignment.center),
: (index == exposurePairs.length - 1
? Alignment.topCenter
: Alignment.center),
child: SizedBox(
height: index == 0 || index == exposurePairs.length - 1
? constraints.maxHeight / 2

View file

@ -60,18 +60,19 @@ class _ReadingValueBuilder extends StatelessWidget {
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
final textColor = Theme.of(context).colorScheme.onPrimaryContainer;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
reading.label,
style: textTheme.labelMedium?.copyWith(color: Theme.of(context).colorScheme.onPrimaryContainer),
style: textTheme.labelMedium?.copyWith(color: textColor),
),
const SizedBox(height: Dimens.grid4),
Text(
reading.value,
style: textTheme.titleMedium?.copyWith(color: Theme.of(context).colorScheme.onPrimaryContainer),
style: textTheme.titleMedium?.copyWith(color: textColor),
),
],
);

View file

@ -47,7 +47,10 @@ class AnimatedDialogState extends State<AnimatedDialog> with SingleTickerProvide
reverseDuration: Dimens.durationML,
vsync: this,
);
_defaultCurvedAnimation = CurvedAnimation(parent: _animationController, curve: Curves.easeInOut);
_defaultCurvedAnimation = CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
);
_barrierColorAnimation = ColorTween(
begin: Colors.transparent,
end: Colors.black54,

View file

@ -55,6 +55,8 @@ class CameraView extends StatelessWidget {
DeviceOrientation _getApplicableOrientation(CameraValue value) {
return value.isRecordingVideo
? value.recordingOrientation!
: (value.previewPauseOrientation ?? value.lockedCaptureOrientation ?? value.deviceOrientation);
: (value.previewPauseOrientation ??
value.lockedCaptureOrientation ??
value.deviceOrientation);
}
}

View file

@ -4,7 +4,8 @@ import 'package:lightmeter/data/models/photography_values/photography_value.dart
import 'package:lightmeter/res/dimens.dart';
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 {
final String title;
@ -32,7 +33,8 @@ class MeteringScreenDialogPicker<T extends PhotographyValue> extends StatefulWid
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;
final _scrollController = ScrollController();
@ -101,7 +103,9 @@ class _MeteringScreenDialogPickerState<T extends PhotographyValue> extends State
child: widget.itemTitleBuilder(context, widget.values[index]),
),
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,
onChanged: (value) {
if (value != null) {

View file

@ -54,7 +54,7 @@ class TopBarShape extends CustomPainter {
);
// 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);
final bool isCutout = appendixHeight < 0;

View file

@ -174,7 +174,9 @@ class _NdValueTile extends StatelessWidget {
subtitle: S.of(context).ndFilterFactor,
selectedValue: value,
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
evDifferenceBuilder: (selected, other) => other.toStringDifference(selected),
onChanged: onChanged,

View file

@ -9,8 +9,10 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lightmeter/interactors/metering_interactor.dart';
import 'package:lightmeter/screens/metering/ev_source/ev_source_bloc.dart';
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' as communication_event;
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states;
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart'
as communication_event;
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart'
as communication_states;
import 'package:lightmeter/utils/log_2.dart';
import 'event_camera.dart';
@ -61,7 +63,9 @@ class CameraBloc extends EvSourceBloc<CameraEvent, CameraState> {
if (communicationState is communication_states.MeasureState) {
_takePhoto().then((ev100) {
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>([
_cameraController!.getMinExposureOffset(),
_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) {
_exposureStep = value == 0 ? 0.1 : value;
});

View file

@ -1,7 +1,8 @@
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.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> {
final MeteringCommunicationBloc communicationBloc;

View file

@ -1,8 +1,10 @@
import 'dart:math';
import 'package:lightmeter/screens/metering/ev_source/ev_source_bloc.dart';
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' as communication_event;
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states;
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart'
as communication_event;
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart'
as communication_states;
import 'event_random_ev.dart';
import 'state_random_ev.dart';

View file

@ -22,10 +22,12 @@ class MeteringScreen extends StatefulWidget {
class _MeteringScreenState extends State<MeteringScreen> {
double topBarOverflow = 0.0;
MeteringBloc get _bloc => context.read<MeteringBloc>();
@override
void didChangeDependencies() {
super.didChangeDependencies();
context.read<MeteringBloc>().add(StopTypeChangedEvent(context.watch<StopType>()));
_bloc.add(StopTypeChangedEvent(context.watch<StopType>()));
}
@override
@ -33,66 +35,83 @@ class _MeteringScreenState extends State<MeteringScreen> {
return Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
body: BlocBuilder<MeteringBloc, MeteringState>(
builder: (context, state) {
return Stack(
children: [
Column(
children: [
MeteringTopBar(
fastest: state.fastest,
slowest: state.slowest,
ev: state.ev,
iso: state.iso,
nd: state.nd,
onIsoChanged: (value) => context.read<MeteringBloc>().add(IsoChangedEvent(value)),
onNdChanged: (value) => context.read<MeteringBloc>().add(NdChangedEvent(value)),
onCutoutLayout: (value) => topBarOverflow = value,
),
Expanded(
child: LayoutBuilder(
builder: (context, constraints) => OverflowBox(
alignment: Alignment.bottomCenter,
maxHeight: constraints.maxHeight + topBarOverflow.abs(),
maxWidth: constraints.maxWidth,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
child: Row(
children: [
Expanded(
child: Padding(
padding: topBarOverflow >= 0 ? EdgeInsets.only(top: topBarOverflow) : EdgeInsets.zero,
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(
children: const [
Expanded(child: CameraExposureSlider()),
SizedBox(height: Dimens.grid24),
CameraZoomSlider(),
],
),
),
),
],
),
),
),
builder: (context, state) => Column(
children: [
MeteringTopBar(
fastest: state.fastest,
slowest: state.slowest,
ev: state.ev,
iso: state.iso,
nd: state.nd,
onIsoChanged: (value) => _bloc.add(IsoChangedEvent(value)),
onNdChanged: (value) => _bloc.add(NdChangedEvent(value)),
onCutoutLayout: (value) => topBarOverflow = value,
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
child: _MiddleContentWrapper(
topBarOverflow: topBarOverflow,
leftContent: ExposurePairsList(state.exposurePairs),
rightContent: Padding(
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM),
child: Column(
children: const [
Expanded(child: CameraExposureSlider()),
SizedBox(height: Dimens.grid24),
CameraZoomSlider(),
],
),
),
MeteringBottomControls(
onMeasure: () => context.read<MeteringBloc>().add(const MeasureEvent()),
onSettings: () => Navigator.pushNamed(context, 'settings'),
),
],
),
),
],
);
},
),
MeteringBottomControls(
onMeasure: () => _bloc.add(const MeasureEvent()),
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,
),
),
],
),
),
);
}

View file

@ -17,6 +17,8 @@ class CalibrationDialog extends StatefulWidget {
}
class _CalibrationDialogState extends State<CalibrationDialog> {
CalibrationDialogBloc get bloc => context.read<CalibrationDialogBloc>();
@override
Widget build(BuildContext context) {
return AlertDialog(
@ -37,8 +39,8 @@ class _CalibrationDialogState extends State<CalibrationDialog> {
_CalibrationUnit(
title: S.of(context).camera,
value: state.cameraEvCalibration,
onChanged: (value) => context.read<CalibrationDialogBloc>().add(CameraEvCalibrationChangedEvent(value)),
onReset: () => context.read<CalibrationDialogBloc>().add(const CameraEvCalibrationResetEvent()),
onChanged: (value) => bloc.add(CameraEvCalibrationChangedEvent(value)),
onReset: () => bloc.add(const CameraEvCalibrationResetEvent()),
),
],
),
@ -56,7 +58,7 @@ class _CalibrationDialogState extends State<CalibrationDialog> {
),
TextButton(
onPressed: () {
context.read<CalibrationDialogBloc>().add(const SaveCalibrationDialogEvent());
bloc.add(const SaveCalibrationDialogEvent());
Navigator.of(context).pop();
},
child: Text(S.of(context).save),

View file

@ -12,12 +12,9 @@ class VersionListTile extends StatelessWidget {
title: Text(S.of(context).version),
trailing: FutureBuilder<PackageInfo>(
future: PackageInfo.fromPlatform(),
builder: (context, snapshot) {
if (snapshot.data != null) {
return Text(S.of(context).versionNumber(snapshot.data!.version, snapshot.data!.buildNumber));
}
return const SizedBox.shrink();
},
builder: (context, snapshot) => snapshot.data != null
? Text(S.of(context).versionNumber(snapshot.data!.version, snapshot.data!.buildNumber))
: const SizedBox.shrink(),
),
);
}

View file

@ -13,7 +13,8 @@ class WriteEmailListTile extends StatelessWidget {
leading: const Icon(Icons.email),
title: Text(S.of(context).writeEmail),
onTap: () {
launchUrl(Uri.parse('mailto:${context.read<Environment>().contactEmail}?subject=M3 Lightmeter'));
launchUrl(
Uri.parse('mailto:${context.read<Environment>().contactEmail}?subject=M3 Lightmeter'));
},
);
}

View file

@ -32,7 +32,10 @@ class SettingsScreen extends StatelessWidget {
titlePadding: const EdgeInsets.all(Dimens.paddingM),
title: Text(
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: [

View file

@ -51,8 +51,14 @@ class _CenteredSliderState extends State<CenteredSlider> {
quarterTurns: widget.isVertical ? -1 : 0,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTapUp: (details) => _updateHandlePosition(details.localPosition.dx, handleDistance),
onHorizontalDragUpdate: (details) => _updateHandlePosition(details.localPosition.dx, handleDistance),
onTapUp: (details) => _updateHandlePosition(
details.localPosition.dx,
handleDistance,
),
onHorizontalDragUpdate: (details) => _updateHandlePosition(
details.localPosition.dx,
handleDistance,
),
child: SizedBox(
height: Dimens.cameraSliderHandleSize,
width: biggestSize,
@ -83,10 +89,8 @@ class _CenteredSliderState extends State<CenteredSlider> {
relativeValue = (offset - Dimens.cameraSliderHandleSize / 2) / handleDistance;
}
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 {
@ -111,7 +115,9 @@ class _Slider extends StatelessWidget {
children: [
Positioned(
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(
borderRadius: BorderRadius.circular(trackThickness / 2),
child: ColoredBox(color: Theme.of(context).colorScheme.surfaceVariant),