Compare commits

..

No commits in common. "a5f211ad4bb19c5d7f36194e3e887c6b8e4b3a09" and "960c2360d2ea2f7b126dc7b14aa2071a8fda7bb3" have entirely different histories.

16 changed files with 130 additions and 295 deletions

View file

@ -63,13 +63,23 @@ void testLogbook(String description) {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.openPickerAndSelect<ApertureValue>(S.current.apertureValue, 'f/5.6'); await tester.openPickerAndSelect<ApertureValue>(S.current.apertureValue, 'f/5.6');
await tester.openPickerAndSelect<ShutterSpeedValue>(S.current.shutterSpeedValue, '1/125'); await tester.openPickerAndSelect<ShutterSpeedValue>(S.current.shutterSpeedValue, '1/125');
_expectPickerListTileValue( expect(
S.current.equipmentProfile, find.descendant(
mockEquipmentProfiles.first.name, of: find.byWidgetPredicate(
(widget) => widget is PickerListTile && widget.title == S.current.equipmentProfile,
),
matching: find.text(mockEquipmentProfiles.first.name),
),
findsOneWidget,
); );
_expectPickerListTileValue( expect(
S.current.film, find.descendant(
mockFilms.first.name, of: find.byWidgetPredicate(
(widget) => widget is PickerListTile && widget.title == S.current.film,
),
matching: find.text(mockFilms.first.name),
),
findsOneWidget,
); );
await tester.openPickerAndSelect<Film>(S.current.film, S.current.notSet); await tester.openPickerAndSelect<Film>(S.current.film, S.current.notSet);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
@ -107,9 +117,14 @@ void testLogbook(String description) {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
/// Verify the edits were saved /// Verify the edits were saved
_expectPickerListTileValue( expect(
S.current.equipmentProfile, find.descendant(
S.current.notSet, of: find.byWidgetPredicate(
(widget) => widget is PickerListTile && widget.title == S.current.equipmentProfile,
),
matching: find.text(S.current.notSet),
),
findsOneWidget,
); );
expect(find.text('Test note'), findsOneWidget); expect(find.text('Test note'), findsOneWidget);
expect(find.text('f/5.6'), findsOneWidget); expect(find.text('f/5.6'), findsOneWidget);
@ -126,138 +141,6 @@ void testLogbook(String description) {
); );
} }
@isTest
void testLogbookEquipmentProfileChanges(String description) {
setUp(() async {
SharedPreferences.setMockInitialValues({
UserPreferencesService.evSourceTypeKey: EvSourceType.camera.index,
UserPreferencesService.seenChangelogVersionKey: await const PlatformUtils().version,
});
});
testWidgets(
description,
(tester) async {
await tester.pumpApplication(
selectedEquipmentProfileId: mockEquipmentProfiles.first.id,
selectedFilmId: mockFilms.first.id,
customFilms: {},
);
await tester.takePhoto();
await tester.openSettings();
await tester.tapDescendantTextOf<SettingsScreen>(S.current.logbook);
await tester.tap(find.byType(LogbookPhotoGridTile).first);
await tester.pumpAndSettle();
await tester.ensureVisible(find.text(mockEquipmentProfiles.first.name));
_expectPickerListTileValue(
S.current.equipmentProfile,
mockEquipmentProfiles.first.name,
);
await tester.openPickerAndSelect<IEquipmentProfile>(S.current.equipmentProfile, mockEquipmentProfiles[1].name);
await tester.pumpAndSettle();
_expectPickerListTileValue(
S.current.equipmentProfile,
mockEquipmentProfiles[1].name,
);
await tester.openPickerAndSelect<IEquipmentProfile>(
S.current.equipmentProfile,
mockPinholeEquipmentProfiles.first.name,
);
await tester.pumpAndSettle();
_expectPickerListTileValue(
S.current.equipmentProfile,
mockPinholeEquipmentProfiles.first.name,
);
_expectPickerListTileValue(
S.current.apertureValue,
ApertureValue(mockPinholeEquipmentProfiles.first.aperture, StopType.full).toString(),
reason: 'Aperture value must be automatically set when selecting a pinhole profile',
);
final aperturePickerFinder = find.descendant(
of: find.byWidgetPredicate(
(widget) => widget is PickerListTile && widget.title == S.current.apertureValue,
),
matching: find.byType(PickerListTile),
);
expect(aperturePickerFinder, findsOneWidget);
await tester.tap(aperturePickerFinder);
await tester.pumpAndSettle();
expect(
find.byType(DialogPicker<Optional<ApertureValue>>),
findsNothing,
reason: 'Aperture picker dialog must not open when pinhole profile is selected',
);
await tester.openPickerAndSelect<IEquipmentProfile>(
S.current.equipmentProfile,
mockPinholeEquipmentProfiles[1].name,
);
await tester.pumpAndSettle();
_expectPickerListTileValue(
S.current.equipmentProfile,
mockPinholeEquipmentProfiles[1].name,
);
_expectPickerListTileValue(
S.current.apertureValue,
ApertureValue(mockPinholeEquipmentProfiles[1].aperture, StopType.full).toString(),
reason: 'Aperture value must be updated when switching to a different pinhole profile',
);
await tester.tap(aperturePickerFinder);
await tester.pumpAndSettle();
expect(
find.byType(DialogPicker<Optional<ApertureValue>>),
findsNothing,
reason: 'Aperture picker dialog must not open when switching between pinhole profiles',
);
await tester.openPickerAndSelect<IEquipmentProfile>(S.current.equipmentProfile, mockEquipmentProfiles.first.name);
await tester.pumpAndSettle();
_expectPickerListTileValue(
S.current.equipmentProfile,
mockEquipmentProfiles.first.name,
);
_expectPickerListTileValue(
S.current.apertureValue,
S.current.notSet,
reason: 'Aperture value must be cleared when switching from pinhole to regular profile',
);
await tester.tap(aperturePickerFinder);
await tester.pumpAndSettle();
expect(
find.byType(DialogPicker<Optional<ApertureValue>>),
findsOneWidget,
reason: 'Aperture picker dialog must open when regular profile is selected',
);
await tester.tap(find.text('Cancel'));
await tester.pumpAndSettle();
await tester.openPickerAndSelect<IEquipmentProfile>(S.current.equipmentProfile, S.current.notSet);
await tester.pumpAndSettle();
_expectPickerListTileValue(
S.current.equipmentProfile,
S.current.notSet,
);
await tester.tap(find.byIcon(Icons.save_outlined));
await tester.pumpAndSettle();
await tester.tap(find.byType(LogbookPhotoGridTile).first);
await tester.pumpAndSettle();
_expectPickerListTileValue(
S.current.equipmentProfile,
S.current.notSet,
);
},
);
}
extension on WidgetTester { extension on WidgetTester {
Future<void> openPickerAndSelect<V>(String title, String valueToSelect) async { Future<void> openPickerAndSelect<V>(String title, String valueToSelect) async {
await tap(find.text(title)); await tap(find.text(title));
@ -273,16 +156,3 @@ extension on WidgetTester {
await tapSelectButton(); await tapSelectButton();
} }
} }
void _expectPickerListTileValue(String title, String value, {String? reason}) {
expect(
find.descendant(
of: find.byWidgetPredicate(
(widget) => widget is PickerListTile && widget.title == title,
),
matching: find.text(value),
),
findsOneWidget,
reason: reason,
);
}

View file

@ -59,7 +59,7 @@ void testToggleLayoutFeatures(String description) {
); );
expectExtremeExposurePairs( expectExtremeExposurePairs(
'f/1.0 - 1/320', 'f/1.0 - 1/320',
'f/45 - 6s', 'f/45 - 6"',
reason: 'Aperture and shutter speed ranges must be reset to default values when equipment profile is reset', reason: 'Aperture and shutter speed ranges must be reset to default values when equipment profile is reset',
); );
expectExposurePairsListItem( expectExposurePairsListItem(
@ -73,7 +73,7 @@ void testToggleLayoutFeatures(String description) {
expectExposurePairsListItem( expectExposurePairsListItem(
tester, tester,
'f/45', 'f/45',
'6s', '6"',
reason: reason:
'Aperture and shutter speed ranges must be reset to default values when equipment profile is reset.', 'Aperture and shutter speed ranges must be reset to default values when equipment profile is reset.',
); );
@ -92,10 +92,10 @@ void testToggleLayoutFeatures(String description) {
(tester) async { (tester) async {
await tester.pumpApplication(); await tester.pumpApplication();
await tester.takePhoto(); await tester.takePhoto();
expectExtremeExposurePairs('f/1.0 - 1/320', 'f/45 - 6s'); expectExtremeExposurePairs('f/1.0 - 1/320', 'f/45 - 6"');
expectExposurePairsListItem(tester, 'f/1.0', '1/320'); expectExposurePairsListItem(tester, 'f/1.0', '1/320');
await tester.scrollToTheLastExposurePair(); await tester.scrollToTheLastExposurePair();
expectExposurePairsListItem(tester, 'f/45', '6s'); expectExposurePairsListItem(tester, 'f/45', '6"');
// Disable layout feature // Disable layout feature
await tester.toggleLayoutFeature(S.current.meteringScreenFeatureExtremeExposurePairs); await tester.toggleLayoutFeature(S.current.meteringScreenFeatureExtremeExposurePairs);
@ -116,7 +116,7 @@ void testToggleLayoutFeatures(String description) {
expectExposurePairsListItem( expectExposurePairsListItem(
tester, tester,
'f/45', 'f/45',
'6s', '6"',
reason: reason:
'Exposure pairs list must not be affected by the visibility of the extreme exposure pairs container.', 'Exposure pairs list must not be affected by the visibility of the extreme exposure pairs container.',
); );
@ -125,7 +125,7 @@ void testToggleLayoutFeatures(String description) {
await tester.toggleLayoutFeature(S.current.meteringScreenFeatureExtremeExposurePairs); await tester.toggleLayoutFeature(S.current.meteringScreenFeatureExtremeExposurePairs);
expectExtremeExposurePairs( expectExtremeExposurePairs(
'f/1.0 - 1/320', 'f/1.0 - 1/320',
'f/45 - 6s', 'f/45 - 6"',
reason: reason:
'Exposure pairs list must not be affected by the visibility of the extreme exposure pairs container.', 'Exposure pairs list must not be affected by the visibility of the extreme exposure pairs container.',
); );
@ -138,10 +138,10 @@ void testToggleLayoutFeatures(String description) {
await tester.pumpApplication(selectedFilmId: mockFilms.first.id); await tester.pumpApplication(selectedFilmId: mockFilms.first.id);
await tester.takePhoto(); await tester.takePhoto();
expectPickerTitle<FilmPicker>(mockFilms.first.name); expectPickerTitle<FilmPicker>(mockFilms.first.name);
expectExtremeExposurePairs('f/1.0 - 1/320', 'f/45 - 12s'); expectExtremeExposurePairs('f/1.0 - 1/320', 'f/45 - 12"');
expectExposurePairsListItem(tester, 'f/1.0', '1/320'); expectExposurePairsListItem(tester, 'f/1.0', '1/320');
await tester.scrollToTheLastExposurePair(); await tester.scrollToTheLastExposurePair();
expectExposurePairsListItem(tester, 'f/45', '12s'); expectExposurePairsListItem(tester, 'f/45', '12"');
// Disable layout feature // Disable layout feature
await tester.toggleLayoutFeature(S.current.meteringScreenFeatureFilmPicker); await tester.toggleLayoutFeature(S.current.meteringScreenFeatureFilmPicker);
@ -153,7 +153,7 @@ void testToggleLayoutFeatures(String description) {
); );
expectExtremeExposurePairs( expectExtremeExposurePairs(
'f/1.0 - 1/320', 'f/1.0 - 1/320',
'f/45 - 6s', 'f/45 - 6"',
reason: 'Shutter speed must not be affected by reciprocity when film is discarded.', reason: 'Shutter speed must not be affected by reciprocity when film is discarded.',
); );
expectExposurePairsListItem( expectExposurePairsListItem(
@ -166,7 +166,7 @@ void testToggleLayoutFeatures(String description) {
expectExposurePairsListItem( expectExposurePairsListItem(
tester, tester,
'f/45', 'f/45',
'6s', '6"',
reason: 'Shutter speed must not be affected by reciprocity when film is discarded.', reason: 'Shutter speed must not be affected by reciprocity when film is discarded.',
); );

View file

@ -19,10 +19,9 @@ void main() {
mockCameraFocalLength(); mockCameraFocalLength();
}); });
testPurchases('Purchase & refund premium features test'); testPurchases('Purchase & refund premium features');
testGuardProTap('Guard Pro tap test'); testGuardProTap('Guard Pro tap');
testToggleLayoutFeatures('Toggle metering screen layout features test'); testToggleLayoutFeatures('Toggle metering screen layout features');
testLogbook('Logbook test'); testLogbook('Logbook');
testLogbookEquipmentProfileChanges('Logbook equipment profile changes test'); testE2E('e2e');
testE2E('e2e test');
} }

View file

@ -1,4 +1,3 @@
import 'dart:collection';
import 'dart:io'; import 'dart:io';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
@ -35,7 +34,7 @@ class LogbookPhotosProvider extends StatefulWidget {
} }
class LogbookPhotosProviderState extends State<LogbookPhotosProvider> { class LogbookPhotosProviderState extends State<LogbookPhotosProvider> {
final LinkedHashMap<String, LogbookPhoto> _photos = LinkedHashMap(); final Map<String, LogbookPhoto> _photos = {};
bool _isEnabled = true; bool _isEnabled = true;
@override @override
@ -91,9 +90,6 @@ class LogbookPhotosProviderState extends State<LogbookPhotosProvider> {
name: path, name: path,
timestamp: DateTime.timestamp(), timestamp: DateTime.timestamp(),
ev: ev100, ev: ev100,
apertureValue: equipmentProfile is PinholeEquipmentProfile
? ApertureValue(equipmentProfile.aperture, StopType.full)
: null,
iso: iso, iso: iso,
nd: nd, nd: nd,
coordinates: coordinates, coordinates: coordinates,

View file

@ -12,15 +12,8 @@ class LogbookPhotoEditBloc extends Bloc<LogbookPhotoEditEvent, LogbookPhotoEditS
LogbookPhotoEditBloc( LogbookPhotoEditBloc(
this.photosProvider, this.photosProvider,
LogbookPhoto photo, LogbookPhoto photo,
IEquipmentProfile? equipmentProfile,
) : _originalPhoto = photo, ) : _originalPhoto = photo,
_newPhoto = photo, _newPhoto = photo,
assert(
equipmentProfile == null ||
equipmentProfile is! PinholeEquipmentProfile ||
photo.apertureValue != null && equipmentProfile.aperture == photo.apertureValue!.rawValue,
"Aperture value must be the same as the equipment profile's aperture value if the equipment profile is a pinhole profile",
),
super( super(
LogbookPhotoEditState( LogbookPhotoEditState(
id: photo.id, id: photo.id,
@ -32,7 +25,7 @@ class LogbookPhotoEditBloc extends Bloc<LogbookPhotoEditEvent, LogbookPhotoEditS
coordinates: photo.coordinates, coordinates: photo.coordinates,
aperture: photo.apertureValue, aperture: photo.apertureValue,
shutterSpeed: photo.shutterSpeedValue, shutterSpeed: photo.shutterSpeedValue,
equipmentProfile: equipmentProfile, equipmentProfileId: photo.equipmentProfileId,
filmId: photo.filmId, filmId: photo.filmId,
note: photo.note, note: photo.note,
canSave: false, canSave: false,
@ -81,22 +74,10 @@ class LogbookPhotoEditBloc extends Bloc<LogbookPhotoEditEvent, LogbookPhotoEditS
} }
Future<void> _onEquipmentProfileChanged(LogbookPhotoEquipmentProfileChangedEvent event, Emitter emit) async { Future<void> _onEquipmentProfileChanged(LogbookPhotoEquipmentProfileChangedEvent event, Emitter emit) async {
final equipmentProfile = event.equipmentProfile; _newPhoto = _newPhoto.copyWith(equipmentProfileId: Optional(event.equipmentProfileId));
Optional<ApertureValue>? apertureValue;
if (state.equipmentProfile is PinholeEquipmentProfile &&
(equipmentProfile == null || equipmentProfile is EquipmentProfile)) {
apertureValue = const Optional(null);
} else if (equipmentProfile is PinholeEquipmentProfile) {
apertureValue = Optional(ApertureValue(equipmentProfile.aperture, StopType.full));
}
_newPhoto = _newPhoto.copyWith(
apertureValue: apertureValue,
equipmentProfileId: Optional(equipmentProfile?.id),
);
emit( emit(
state.copyWith( state.copyWith(
aperture: apertureValue, equipmentProfileId: Optional(event.equipmentProfileId),
equipmentProfile: Optional(event.equipmentProfile),
canSave: _canSave(), canSave: _canSave(),
), ),
); );

View file

@ -9,7 +9,7 @@ class PickerListTile<T> extends StatelessWidget {
final T? selectedValue; final T? selectedValue;
final List<T> values; final List<T> values;
final String Function(T) titleAdapter; final String Function(T) titleAdapter;
final ValueChanged<Optional<T>>? onChanged; final ValueChanged<Optional<T>> onChanged;
const PickerListTile({ const PickerListTile({
required this.icon, required this.icon,
@ -27,8 +27,7 @@ class PickerListTile<T> extends StatelessWidget {
leading: Icon(icon), leading: Icon(icon),
title: Text(title), title: Text(title),
trailing: Text(_titleAdapter(context, selectedValue)), trailing: Text(_titleAdapter(context, selectedValue)),
onTap: onChanged != null onTap: () {
? () {
showDialog<Optional<T>>( showDialog<Optional<T>>(
context: context, context: context,
builder: (_) => DialogPicker<Optional<T>>( builder: (_) => DialogPicker<Optional<T>>(
@ -45,11 +44,10 @@ class PickerListTile<T> extends StatelessWidget {
), ),
).then((value) { ).then((value) {
if (value != null) { if (value != null) {
onChanged!(value); onChanged(value);
} }
}); });
} },
: null,
); );
} }

View file

@ -23,9 +23,9 @@ class LogbookPhotoNoteChangedEvent extends LogbookPhotoEditEvent {
} }
class LogbookPhotoEquipmentProfileChangedEvent extends LogbookPhotoEditEvent { class LogbookPhotoEquipmentProfileChangedEvent extends LogbookPhotoEditEvent {
final IEquipmentProfile? equipmentProfile; final String? equipmentProfileId;
const LogbookPhotoEquipmentProfileChangedEvent(this.equipmentProfile); const LogbookPhotoEquipmentProfileChangedEvent(this.equipmentProfileId);
} }
class LogbookPhotoFilmChangedEvent extends LogbookPhotoEditEvent { class LogbookPhotoFilmChangedEvent extends LogbookPhotoEditEvent {

View file

@ -1,7 +1,5 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lightmeter/providers/equipment_profile_provider.dart';
import 'package:lightmeter/providers/logbook_photos_provider.dart'; import 'package:lightmeter/providers/logbook_photos_provider.dart';
import 'package:lightmeter/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart'; import 'package:lightmeter/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart';
import 'package:lightmeter/screens/logbook_photo_edit/screen_logbook_photo_edit.dart'; import 'package:lightmeter/screens/logbook_photo_edit/screen_logbook_photo_edit.dart';
@ -27,7 +25,6 @@ class LogbookPhotoEditFlow extends StatelessWidget {
create: (_) => LogbookPhotoEditBloc( create: (_) => LogbookPhotoEditBloc(
LogbookPhotosProvider.of(context), LogbookPhotosProvider.of(context),
args.photo, args.photo,
EquipmentProfiles.of(context).firstWhereOrNull((e) => e.id == args.photo.equipmentProfileId),
), ),
child: const LogbookPhotoEditScreen(), child: const LogbookPhotoEditScreen(),
); );

View file

@ -224,15 +224,13 @@ class _AperturePickerListTile extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<LogbookPhotoEditBloc, LogbookPhotoEditState>( return BlocBuilder<LogbookPhotoEditBloc, LogbookPhotoEditState>(
buildWhen: (previous, current) => previous.aperture != current.aperture, buildWhen: (previous, current) => previous.aperture != current.aperture,
builder: (context, state) => PickerListTile<ApertureValue>( builder: (context, state) => PickerListTile(
icon: Icons.camera_outlined, icon: Icons.camera_outlined,
title: S.of(context).apertureValue, title: S.of(context).apertureValue,
values: ApertureValue.values, values: ApertureValue.values,
selectedValue: state.aperture, selectedValue: state.aperture,
titleAdapter: (value) => value.toString(), titleAdapter: (value) => value.toString(),
onChanged: state.equipmentProfile is PinholeEquipmentProfile onChanged: (value) {
? null
: (value) {
context.read<LogbookPhotoEditBloc>().add(LogbookPhotoApertureChangedEvent(value.value)); context.read<LogbookPhotoEditBloc>().add(LogbookPhotoApertureChangedEvent(value.value));
}, },
), ),
@ -267,15 +265,15 @@ class _EquipmentProfilePickerListTile extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<LogbookPhotoEditBloc, LogbookPhotoEditState>( return BlocBuilder<LogbookPhotoEditBloc, LogbookPhotoEditState>(
buildWhen: (previous, current) => previous.equipmentProfile != current.equipmentProfile, buildWhen: (previous, current) => previous.equipmentProfileId != current.equipmentProfileId,
builder: (context, state) => PickerListTile( builder: (context, state) => PickerListTile(
icon: Icons.camera_alt_outlined, icon: Icons.camera_alt_outlined,
title: S.of(context).equipmentProfile, title: S.of(context).equipmentProfile,
values: EquipmentProfiles.of(context).skip(1).toList(growable: false), values: EquipmentProfiles.of(context).skip(1).toList(growable: false),
selectedValue: state.equipmentProfile, selectedValue: EquipmentProfiles.of(context).firstWhereOrNull((e) => e.id == state.equipmentProfileId),
titleAdapter: (value) => value.name, titleAdapter: (value) => value.name,
onChanged: (value) { onChanged: (value) {
context.read<LogbookPhotoEditBloc>().add(LogbookPhotoEquipmentProfileChangedEvent(value.value)); context.read<LogbookPhotoEditBloc>().add(LogbookPhotoEquipmentProfileChangedEvent(value.value?.id));
}, },
), ),
); );

View file

@ -10,7 +10,7 @@ class LogbookPhotoEditState {
final Coordinates? coordinates; final Coordinates? coordinates;
final ApertureValue? aperture; final ApertureValue? aperture;
final ShutterSpeedValue? shutterSpeed; final ShutterSpeedValue? shutterSpeed;
final IEquipmentProfile? equipmentProfile; final String? equipmentProfileId;
final String? filmId; final String? filmId;
final String? note; final String? note;
final bool canSave; final bool canSave;
@ -26,7 +26,7 @@ class LogbookPhotoEditState {
this.coordinates, this.coordinates,
this.aperture, this.aperture,
this.shutterSpeed, this.shutterSpeed,
this.equipmentProfile, this.equipmentProfileId,
this.filmId, this.filmId,
this.note, this.note,
required this.canSave, required this.canSave,
@ -37,7 +37,7 @@ class LogbookPhotoEditState {
String? name, String? name,
Optional<ApertureValue>? aperture, Optional<ApertureValue>? aperture,
Optional<ShutterSpeedValue>? shutterSpeed, Optional<ShutterSpeedValue>? shutterSpeed,
Optional<IEquipmentProfile>? equipmentProfile, Optional<String>? equipmentProfileId,
Optional<String>? filmId, Optional<String>? filmId,
String? note, String? note,
bool? canSave, bool? canSave,
@ -52,7 +52,7 @@ class LogbookPhotoEditState {
nd: nd, nd: nd,
aperture: aperture != null ? aperture.value : this.aperture, aperture: aperture != null ? aperture.value : this.aperture,
shutterSpeed: shutterSpeed != null ? shutterSpeed.value : this.shutterSpeed, shutterSpeed: shutterSpeed != null ? shutterSpeed.value : this.shutterSpeed,
equipmentProfile: equipmentProfile != null ? equipmentProfile.value : this.equipmentProfile, equipmentProfileId: equipmentProfileId != null ? equipmentProfileId.value : this.equipmentProfileId,
filmId: filmId != null ? filmId.value : this.filmId, filmId: filmId != null ? filmId.value : this.filmId,
note: note ?? this.note, note: note ?? this.note,
canSave: canSave ?? this.canSave, canSave: canSave ?? this.canSave,

View file

@ -126,13 +126,11 @@ class CameraContainer extends StatelessWidget {
enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight; enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight;
enabledFeaturesHeight += Dimens.paddingS; enabledFeaturesHeight += Dimens.paddingS;
} }
if (context.meteringFeature(MeteringScreenLayoutFeature.extremeExposurePairs)) {
if (EquipmentProfiles.selectedOf(context) is PinholeEquipmentProfile) { if (EquipmentProfiles.selectedOf(context) is PinholeEquipmentProfile) {
enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight; enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight;
} else { enabledFeaturesHeight += Dimens.paddingS;
} else if (context.meteringFeature(MeteringScreenLayoutFeature.extremeExposurePairs)) {
enabledFeaturesHeight += Dimens.readingContainerDoubleValueHeight; enabledFeaturesHeight += Dimens.readingContainerDoubleValueHeight;
}
enabledFeaturesHeight += Dimens.paddingS; enabledFeaturesHeight += Dimens.paddingS;
} }

View file

@ -65,7 +65,6 @@ class ExposurePairsList extends StatelessWidget {
], ],
), ),
), ),
if (exposurePairs.length > 1)
Positioned( Positioned(
top: 0, top: 0,
bottom: 0, bottom: 0,

View file

@ -46,10 +46,10 @@ class ReadingsContainer extends StatelessWidget {
const EquipmentProfilePicker(), const EquipmentProfilePicker(),
const _InnerPadding(), const _InnerPadding(),
], ],
if (context.meteringFeature(MeteringScreenLayoutFeature.extremeExposurePairs)) ...[ if (EquipmentProfiles.selectedOf(context) is PinholeEquipmentProfile) ...[
if (EquipmentProfiles.selectedOf(context) is PinholeEquipmentProfile) ShutterSpeedContainer(shutterSpeedValue: fastest?.shutterSpeed),
ShutterSpeedContainer(shutterSpeedValue: fastest?.shutterSpeed) const _InnerPadding(),
else ] else if (context.meteringFeature(MeteringScreenLayoutFeature.extremeExposurePairs)) ...[
ExtremeExposurePairsContainer( ExtremeExposurePairsContainer(
fastest: fastest, fastest: fastest,
slowest: slowest, slowest: slowest,

View file

@ -24,19 +24,18 @@ class DialogPicker<T> extends StatefulWidget {
} }
class _DialogPickerState<T> extends State<DialogPicker<T>> { class _DialogPickerState<T> extends State<DialogPicker<T>> {
T? _selected; late T _selected = widget.selectedValue;
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
final selectedIndex = widget.values.indexOf(widget.selectedValue);
if (selectedIndex >= 0) {
_selected = widget.selectedValue;
SchedulerBinding.instance.addPostFrameCallback((_) { SchedulerBinding.instance.addPostFrameCallback((_) {
final selectedIndex = widget.values.indexOf(_selected);
if (selectedIndex >= 0) {
_scrollController.jumpTo((Dimens.grid56 * selectedIndex).clamp(0, _scrollController.position.maxScrollExtent)); _scrollController.jumpTo((Dimens.grid56 * selectedIndex).clamp(0, _scrollController.position.maxScrollExtent));
});
} }
});
} }
@override @override
@ -94,7 +93,7 @@ class _DialogPickerState<T> extends State<DialogPicker<T>> {
child: Text(S.of(context).cancel), child: Text(S.of(context).cancel),
), ),
TextButton( TextButton(
onPressed: _selected != null ? () => Navigator.of(context).pop(_selected) : null, onPressed: () => Navigator.of(context).pop(_selected),
child: Text(S.of(context).select), child: Text(S.of(context).select),
), ),
], ],

View file

@ -170,10 +170,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: built_value name: built_value
sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.12.0" version: "8.10.1"
camera: camera:
dependency: "direct main" dependency: "direct main"
description: description:
@ -861,8 +861,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: main ref: "feature/MLI-48"
resolved-ref: "3f5bae6d2500a746fb83ab345919095a815244d1" resolved-ref: "4a169640bff3d3a3206a2c352a75cbcea4871b1c"
url: "https://github.com/vodemn/m3_lightmeter_iap" url: "https://github.com/vodemn/m3_lightmeter_iap"
source: git source: git
version: "4.1.2+37" version: "4.1.2+37"
@ -870,11 +870,11 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: "v2.5.0" ref: "feature/MLR-18"
resolved-ref: "680affb45c5d03ed4fe61c30ee0d6e6fab0f2c12" resolved-ref: "61bb3f8a9164d19f6e47c96fbea1cbe3aaf39fc3"
url: "https://github.com/vodemn/m3_lightmeter_resources" url: "https://github.com/vodemn/m3_lightmeter_resources"
source: git source: git
version: "2.5.0+14" version: "2.4.0+13"
macros: macros:
dependency: transitive dependency: transitive
description: description:
@ -967,10 +967,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: package_info_plus_platform_interface name: package_info_plus_platform_interface
sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086" sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.2.1" version: "3.2.0"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -1119,10 +1119,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: provider name: provider
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.5+1" version: "6.1.5"
pub_semver: pub_semver:
dependency: transitive dependency: transitive
description: description:
@ -1524,10 +1524,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: watcher name: watcher
sha256: "5bf046f41320ac97a469d506261797f35254fa61c641741ef32dacda98b7d39c" sha256: "0b7fd4a0bbc4b92641dbf20adfd7e3fd1398fe17102d94b674234563e110088a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.3" version: "1.1.2"
web: web:
dependency: transitive dependency: transitive
description: description:

View file

@ -33,11 +33,11 @@ dependencies:
m3_lightmeter_iap: m3_lightmeter_iap:
git: git:
url: "https://github.com/vodemn/m3_lightmeter_iap" url: "https://github.com/vodemn/m3_lightmeter_iap"
ref: main ref: feature/MLI-48
m3_lightmeter_resources: m3_lightmeter_resources:
git: git:
url: "https://github.com/vodemn/m3_lightmeter_resources" url: "https://github.com/vodemn/m3_lightmeter_resources"
ref: v2.5.0 ref: feature/MLR-18
map_launcher: 3.2.0 map_launcher: 3.2.0
material_color_utilities: 0.12.0 material_color_utilities: 0.12.0
package_info_plus: 8.1.3 package_info_plus: 8.1.3