ML-258 Allow to set equipment profile and film for logbook photo (#259)

* added film and equipment profile picker to logbook edit

* unified camera icon usages

* Update settings_screen.png

* save equipment profile and film when taking a photo

* extended logbook integration test

* cover cascade deletion

* updated dependencies

* Update logbook_photos_provider_test.dart

* regenerated screenshots

* refined integration tests

* update stub pubspec

* fixed lens zoom on screenshots
This commit is contained in:
Vadim 2025-08-28 12:34:07 +02:00 committed by GitHub
parent e06201cabc
commit f06715bd12
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 238 additions and 29 deletions

View file

@ -12,7 +12,7 @@ dependencies:
m3_lightmeter_resources:
git:
url: "https://github.com/vodemn/m3_lightmeter_resources"
ref: v2.3.0
ref: v2.4.0
shared_preferences:
dev_dependencies:

View file

@ -3,6 +3,8 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:lightmeter/data/models/ev_source_type.dart';
import 'package:lightmeter/data/shared_prefs_service.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/screens/logbook_photo_edit/components/picker_list_tile/widget_list_tile_picker.dart';
import 'package:lightmeter/screens/logbook_photo_edit/screen_logbook_photo_edit.dart';
import 'package:lightmeter/screens/logbook_photos/components/grid_tile/widget_grid_tile_logbook_photo.dart';
import 'package:lightmeter/screens/settings/components/shared/dialog_picker/widget_dialog_picker.dart';
@ -28,8 +30,8 @@ void testLogbook(String description) {
description,
(tester) async {
await tester.pumpApplication(
equipmentProfiles: {},
predefinedFilms: mockFilms.toTogglableMap(),
selectedEquipmentProfileId: mockEquipmentProfiles.first.id,
selectedFilmId: mockFilms.first.id,
customFilms: {},
);
await tester.takePhoto();
@ -45,6 +47,9 @@ void testLogbook(String description) {
await tester.tap(find.byType(LogbookPhotoGridTile).first);
await tester.pumpAndSettle();
await tester.ensureVisible(find.text(mockEquipmentProfiles.first.name));
await tester.ensureVisible(find.text(mockFilms.first.name));
/// Add a note, select aperture value and shutter speed value
await tester.enterText(
find.descendant(
@ -58,6 +63,26 @@ void testLogbook(String description) {
await tester.pumpAndSettle();
await tester.openPickerAndSelect<ApertureValue>(S.current.apertureValue, 'f/5.6');
await tester.openPickerAndSelect<ShutterSpeedValue>(S.current.shutterSpeedValue, '1/125');
expect(
find.descendant(
of: find.byWidgetPredicate(
(widget) => widget is PickerListTile && widget.title == S.current.equipmentProfile,
),
matching: find.text(mockEquipmentProfiles.first.name),
),
findsOneWidget,
);
expect(
find.descendant(
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.pumpAndSettle();
/// Save the edits
await tester.tap(find.byIcon(Icons.save_outlined));
@ -76,11 +101,32 @@ void testLogbook(String description) {
/// Verify that only one photo is present
expect(find.byType(LogbookPhotoGridTile), findsOneWidget);
/// Got back and delete the equipment profile used to take the first picture
await tester.navigatorPop();
await tester.tapDescendantTextOf<SettingsScreen>(S.current.equipmentProfiles);
await tester.tap(find.byIcon(Icons.edit_outlined).first);
await tester.pumpAndSettle();
await tester.tap(find.byIcon(Icons.delete_outlined));
await tester.pumpAndSettle(Dimens.durationML);
expect(find.text(mockEquipmentProfiles[0].name), findsNothing);
expect(find.text(mockEquipmentProfiles[1].name), findsOneWidget);
await tester.navigatorPop();
/// Open photo again
await tester.tapDescendantTextOf<SettingsScreen>(S.current.logbook);
await tester.tap(find.byType(LogbookPhotoGridTile).first);
await tester.pumpAndSettle();
/// Verify the edits were saved
expect(
find.descendant(
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('f/5.6'), findsOneWidget);
expect(find.text('1/125'), findsOneWidget);
@ -101,7 +147,7 @@ extension on WidgetTester {
await tap(find.text(title));
await pumpAndSettle();
final dialogFinder = find.byType(DialogPicker<Optional<V>>);
final listTileFinder = find.text(valueToSelect);
final listTileFinder = find.descendant(of: dialogFinder, matching: find.text(valueToSelect));
await scrollUntilVisible(
listTileFinder,
56,

View file

@ -114,6 +114,7 @@ class FilmsProviderState extends State<FilmsProvider> {
}
enum _FilmsModelAspect {
all,
customFilms,
predefinedFilms,
filmsInUse,
@ -134,6 +135,14 @@ class Films extends InheritedModel<_FilmsModelAspect> {
required super.child,
});
static List<Film> of<T>(BuildContext context) {
final model = InheritedModel.inheritFrom<Films>(context, aspect: _FilmsModelAspect.all)!;
return [
...model.customFilms.values.map((e) => e.value),
...model.predefinedFilms.values.map((e) => e.value),
];
}
static List<Film> predefinedFilmsOf<T>(BuildContext context) {
return InheritedModel.inheritFrom<Films>(context, aspect: _FilmsModelAspect.predefinedFilms)!
.predefinedFilms
@ -171,10 +180,12 @@ class Films extends InheritedModel<_FilmsModelAspect> {
bool updateShouldNotifyDependent(Films oldWidget, Set<_FilmsModelAspect> dependencies) {
return (dependencies.contains(_FilmsModelAspect.selected) && oldWidget.selected != selected) ||
((dependencies.contains(_FilmsModelAspect.predefinedFilms) ||
dependencies.contains(_FilmsModelAspect.filmsInUse)) &&
dependencies.contains(_FilmsModelAspect.filmsInUse) ||
dependencies.contains(_FilmsModelAspect.all)) &&
const DeepCollectionEquality().equals(oldWidget.predefinedFilms, predefinedFilms)) ||
((dependencies.contains(_FilmsModelAspect.customFilms) ||
dependencies.contains(_FilmsModelAspect.filmsInUse)) &&
dependencies.contains(_FilmsModelAspect.filmsInUse) ||
dependencies.contains(_FilmsModelAspect.all)) &&
const DeepCollectionEquality().equals(oldWidget.customFilms, customFilms));
}
}

View file

@ -4,6 +4,8 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:lightmeter/data/geolocation_service.dart';
import 'package:lightmeter/platform_config.dart';
import 'package:lightmeter/providers/equipment_profile_provider.dart';
import 'package:lightmeter/providers/films_provider.dart';
import 'package:lightmeter/utils/context_utils.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
@ -74,6 +76,13 @@ class LogbookPhotosProviderState extends State<LogbookPhotosProvider> {
required int nd,
}) async {
if (context.isPro && _isEnabled) {
final equipmentProfile = EquipmentProfiles.selectedOf(context);
final equipmentProfileId =
equipmentProfile == EquipmentProfilesProvider.defaultProfile ? null : equipmentProfile.id;
final selectedFilm = Films.selectedOf(context);
final filmId = selectedFilm == const FilmStub() ? null : selectedFilm.id;
final coordinates = await widget.geolocationService.getCurrentPosition();
final photo = LogbookPhoto(
@ -84,6 +93,8 @@ class LogbookPhotosProviderState extends State<LogbookPhotosProvider> {
iso: iso,
nd: nd,
coordinates: coordinates,
equipmentProfileId: equipmentProfileId,
filmId: filmId,
);
await widget.storageService.addPhoto(photo);
_photos[photo.id] = photo;

View file

@ -25,6 +25,8 @@ class LogbookPhotoEditBloc extends Bloc<LogbookPhotoEditEvent, LogbookPhotoEditS
coordinates: photo.coordinates,
aperture: photo.apertureValue,
shutterSpeed: photo.shutterSpeedValue,
equipmentProfileId: photo.equipmentProfileId,
filmId: photo.filmId,
note: photo.note,
canSave: false,
),
@ -36,6 +38,10 @@ class LogbookPhotoEditBloc extends Bloc<LogbookPhotoEditEvent, LogbookPhotoEditS
await _onApertureChanged(e, emit);
case final LogbookPhotoShutterSpeedChangedEvent e:
await _onShutterSpeedChanged(e, emit);
case final LogbookPhotoEquipmentProfileChangedEvent e:
await _onEquipmentProfileChanged(e, emit);
case final LogbookPhotoFilmChangedEvent e:
await _onFilmChanged(e, emit);
case final LogbookPhotoNoteChangedEvent e:
await _onNoteChanged(e, emit);
case LogbookPhotoSaveEvent():
@ -67,6 +73,26 @@ class LogbookPhotoEditBloc extends Bloc<LogbookPhotoEditEvent, LogbookPhotoEditS
);
}
Future<void> _onEquipmentProfileChanged(LogbookPhotoEquipmentProfileChangedEvent event, Emitter emit) async {
_newPhoto = _newPhoto.copyWith(equipmentProfileId: Optional(event.equipmentProfile?.id));
emit(
state.copyWith(
equipmentProfileId: Optional(event.equipmentProfile?.id),
canSave: _canSave(),
),
);
}
Future<void> _onFilmChanged(LogbookPhotoFilmChangedEvent event, Emitter emit) async {
_newPhoto = _newPhoto.copyWith(filmId: Optional(event.film?.id));
emit(
state.copyWith(
filmId: Optional(event.film?.id),
canSave: _canSave(),
),
);
}
Future<void> _onNoteChanged(LogbookPhotoNoteChangedEvent event, Emitter emit) async {
_newPhoto = _newPhoto.copyWith(note: event.note);
emit(

View file

@ -3,11 +3,12 @@ import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/screens/settings/components/shared/dialog_picker/widget_dialog_picker.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
class PickerListTile<T extends PhotographyValue> extends StatelessWidget {
class PickerListTile<T> extends StatelessWidget {
final IconData icon;
final String title;
final T? selectedValue;
final List<T> values;
final String Function(T) titleAdapter;
final ValueChanged<Optional<T>> onChanged;
const PickerListTile({
@ -15,6 +16,7 @@ class PickerListTile<T extends PhotographyValue> extends StatelessWidget {
required this.title,
required this.selectedValue,
required this.values,
required this.titleAdapter,
required this.onChanged,
super.key,
});
@ -24,7 +26,7 @@ class PickerListTile<T extends PhotographyValue> extends StatelessWidget {
return ListTile(
leading: Icon(icon),
title: Text(title),
trailing: Text(selectedValue?.toString() ?? S.of(context).notSet),
trailing: Text(_titleAdapter(context, selectedValue)),
onTap: () {
showDialog<Optional<T>>(
context: context,
@ -36,7 +38,7 @@ class PickerListTile<T extends PhotographyValue> extends StatelessWidget {
const Optional(null),
...values.toSet().map((e) => Optional(e)),
],
titleAdapter: (context, value) => value.value?.toString() ?? S.of(context).notSet,
titleAdapter: (context, value) => _titleAdapter(context, value.value),
),
).then((value) {
if (value != null) {
@ -46,4 +48,8 @@ class PickerListTile<T extends PhotographyValue> extends StatelessWidget {
},
);
}
String _titleAdapter(BuildContext context, T? value) {
return value != null ? titleAdapter(value) : S.of(context).notSet;
}
}

View file

@ -22,6 +22,18 @@ class LogbookPhotoNoteChangedEvent extends LogbookPhotoEditEvent {
const LogbookPhotoNoteChangedEvent(this.note);
}
class LogbookPhotoEquipmentProfileChangedEvent extends LogbookPhotoEditEvent {
final EquipmentProfile? equipmentProfile;
const LogbookPhotoEquipmentProfileChangedEvent(this.equipmentProfile);
}
class LogbookPhotoFilmChangedEvent extends LogbookPhotoEditEvent {
final Film? film;
const LogbookPhotoFilmChangedEvent(this.film);
}
class LogbookPhotoSaveEvent extends LogbookPhotoEditEvent {
const LogbookPhotoSaveEvent();
}

View file

@ -1,9 +1,12 @@
import 'dart:io';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/platform_config.dart';
import 'package:lightmeter/providers/equipment_profile_provider.dart';
import 'package:lightmeter/providers/films_provider.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart';
import 'package:lightmeter/screens/logbook_photo_edit/components/coordinates_list_tile/widget_list_tile_coordinates_logbook_photo.dart';
@ -73,6 +76,8 @@ class _LogbookPhotoEditScreenState extends State<LogbookPhotoEditScreen> {
child: Column(
children: [
LogbookPhotoCoordinatesListTile(),
_EquipmentProfilePickerListTile(),
_FilmPickerListTile(),
_NoteListTile(),
_EvListTile(),
_IsoListTile(),
@ -224,6 +229,7 @@ class _AperturePickerListTile extends StatelessWidget {
title: S.of(context).apertureValue,
values: ApertureValue.values,
selectedValue: state.aperture,
titleAdapter: (value) => value.toString(),
onChanged: (value) {
context.read<LogbookPhotoEditBloc>().add(LogbookPhotoApertureChangedEvent(value.value));
},
@ -244,6 +250,7 @@ class _ShutterSpeedPickerListTile extends StatelessWidget {
title: S.of(context).shutterSpeedValue,
values: ShutterSpeedValue.values,
selectedValue: state.shutterSpeed,
titleAdapter: (value) => value.toString(),
onChanged: (value) {
context.read<LogbookPhotoEditBloc>().add(LogbookPhotoShutterSpeedChangedEvent(value.value));
},
@ -251,3 +258,45 @@ class _ShutterSpeedPickerListTile extends StatelessWidget {
);
}
}
class _EquipmentProfilePickerListTile extends StatelessWidget {
const _EquipmentProfilePickerListTile();
@override
Widget build(BuildContext context) {
return BlocBuilder<LogbookPhotoEditBloc, LogbookPhotoEditState>(
buildWhen: (previous, current) => previous.equipmentProfileId != current.equipmentProfileId,
builder: (context, state) => PickerListTile(
icon: Icons.camera_alt_outlined,
title: S.of(context).equipmentProfile,
values: EquipmentProfiles.of(context).skip(1).toList(growable: false),
selectedValue: EquipmentProfiles.of(context).firstWhereOrNull((e) => e.id == state.equipmentProfileId),
titleAdapter: (value) => value.name,
onChanged: (value) {
context.read<LogbookPhotoEditBloc>().add(LogbookPhotoEquipmentProfileChangedEvent(value.value));
},
),
);
}
}
class _FilmPickerListTile extends StatelessWidget {
const _FilmPickerListTile();
@override
Widget build(BuildContext context) {
return BlocBuilder<LogbookPhotoEditBloc, LogbookPhotoEditState>(
buildWhen: (previous, current) => previous.filmId != current.filmId,
builder: (context, state) => PickerListTile(
icon: Icons.camera_roll_outlined,
title: S.of(context).film,
values: Films.of(context),
selectedValue: Films.of(context).firstWhereOrNull((e) => e.id == state.filmId),
titleAdapter: (value) => value.name,
onChanged: (value) {
context.read<LogbookPhotoEditBloc>().add(LogbookPhotoFilmChangedEvent(value.value));
},
),
);
}
}

View file

@ -10,6 +10,8 @@ class LogbookPhotoEditState {
final Coordinates? coordinates;
final ApertureValue? aperture;
final ShutterSpeedValue? shutterSpeed;
final String? equipmentProfileId;
final String? filmId;
final String? note;
final bool canSave;
final bool isLoading;
@ -24,6 +26,8 @@ class LogbookPhotoEditState {
this.coordinates,
this.aperture,
this.shutterSpeed,
this.equipmentProfileId,
this.filmId,
this.note,
required this.canSave,
this.isLoading = false,
@ -33,6 +37,8 @@ class LogbookPhotoEditState {
String? name,
Optional<ApertureValue>? aperture,
Optional<ShutterSpeedValue>? shutterSpeed,
Optional<String>? equipmentProfileId,
Optional<String>? filmId,
String? note,
bool? canSave,
bool? isLoading,
@ -46,6 +52,8 @@ class LogbookPhotoEditState {
nd: nd,
aperture: aperture != null ? aperture.value : this.aperture,
shutterSpeed: shutterSpeed != null ? shutterSpeed.value : this.shutterSpeed,
equipmentProfileId: equipmentProfileId != null ? equipmentProfileId.value : this.equipmentProfileId,
filmId: filmId != null ? filmId.value : this.filmId,
note: note ?? this.note,
canSave: canSave ?? this.canSave,
isLoading: isLoading ?? this.isLoading,

View file

@ -26,7 +26,7 @@ class _EquipmentProfilePickerState extends State<EquipmentProfilePicker> {
@override
Widget build(BuildContext context) {
return AnimatedDialogPicker<EquipmentProfile>(
icon: Icons.camera_outlined,
icon: Icons.camera_alt_outlined,
title: S.of(context).equipmentProfile,
selectedValue: EquipmentProfiles.selectedOf(context),
values: EquipmentProfiles.inUseOf(context),

View file

@ -11,7 +11,7 @@ class CameraFeaturesListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
leading: const Icon(Icons.camera_alt_outlined),
leading: const Icon(Icons.camera_enhance_outlined),
title: Text(S.of(context).cameraFeatures),
onTap: () {
UserPreferencesProvider.cameraConfigOf(context).entries.map(
@ -26,7 +26,7 @@ class CameraFeaturesListTile extends StatelessWidget {
showDialog(
context: context,
builder: (_) => DialogSwitch<CameraFeature>(
icon: Icons.camera_alt_outlined,
icon: Icons.camera_enhance_outlined,
title: S.of(context).cameraFeatures,
items: [
DialogSwitchListItem(

View file

@ -8,7 +8,7 @@ class EquipmentProfilesListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
leading: const Icon(Icons.camera_outlined),
leading: const Icon(Icons.camera_alt_outlined),
title: Text(S.of(context).equipmentProfiles),
onTap: () {
Navigator.of(context).pushNamed(NavigationRoutes.equipmentProfilesListScreen.name);

View file

@ -861,20 +861,20 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "v4.0.1"
resolved-ref: ce431af9fc86694b3bd8d1e2a2a988d9b6184b17
ref: "v4.1.0"
resolved-ref: "15adfa4f6fea06a6c62c58f5171a41813058040c"
url: "https://github.com/vodemn/m3_lightmeter_iap"
source: git
version: "4.0.1+34"
version: "4.1.0+35"
m3_lightmeter_resources:
dependency: "direct main"
description:
path: "."
ref: "v2.3.1"
resolved-ref: "8b39ac1927b791652618509abe0391f844229b93"
ref: "v2.4.0"
resolved-ref: cc9ae43a7859398a6ab2ecf7f8713153dbfd99cd
url: "https://github.com/vodemn/m3_lightmeter_resources"
source: git
version: "2.3.1+12"
version: "2.4.0+13"
macros:
dependency: transitive
description:

View file

@ -33,11 +33,11 @@ dependencies:
m3_lightmeter_iap:
git:
url: "https://github.com/vodemn/m3_lightmeter_iap"
ref: v4.0.1
ref: v4.1.0
m3_lightmeter_resources:
git:
url: "https://github.com/vodemn/m3_lightmeter_resources"
ref: v2.3.1
ref: v2.4.0
map_launcher: 3.2.0
material_color_utilities: 0.12.0
package_info_plus: 8.1.3
@ -68,10 +68,6 @@ dev_dependencies:
dependency_overrides:
geolocator_android: 4.6.1
m3_lightmeter_resources:
git:
url: "https://github.com/vodemn/m3_lightmeter_resources"
ref: v2.3.1
material_color_utilities: 0.11.1
flutter:

View file

@ -36,6 +36,31 @@ import 'models/screenshot_args.dart';
//https://stackoverflow.com/a/67186625/13167574
final _mockEquipmentProfile = EquipmentProfile(
id: '1',
name: 'Praktica + Zenitar',
apertureValues: ApertureValue.values.sublist(
ApertureValue.values.indexOf(const ApertureValue(1.7, StopType.half)),
ApertureValue.values.indexOf(const ApertureValue(16, StopType.full)) + 1,
),
ndValues: NdValue.values.sublist(0, 3),
shutterSpeedValues: ShutterSpeedValue.values.sublist(
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(1000, true, StopType.full)),
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(1, false, StopType.full)) + 1,
),
isoValues: const [
IsoValue(50, StopType.full),
IsoValue(100, StopType.full),
IsoValue(200, StopType.full),
IsoValue(250, StopType.third),
IsoValue(400, StopType.full),
IsoValue(500, StopType.third),
IsoValue(800, StopType.full),
IsoValue(1600, StopType.full),
IsoValue(3200, StopType.full),
],
lensZoom: 50 / (Platform.isAndroid ? 24 : 26),
);
const _mockFilm = FilmExponential(id: '1', name: 'Ilford HP5+', iso: 400, exponent: 1.34);
final Color _lightThemeColor = primaryColorsList[5];
final Color _darkThemeColor = primaryColorsList[3];
@ -109,6 +134,8 @@ void main() {
testWidgets('Generate light theme screenshots', (tester) async {
await mockSharedPrefs(theme: ThemeType.light, color: _lightThemeColor);
await tester.pumpApplication(
equipmentProfiles: [_mockEquipmentProfile].toTogglableMap(),
selectedEquipmentProfileId: _mockEquipmentProfile.id,
predefinedFilms: [_mockFilm].toTogglableMap(),
customFilms: {},
selectedFilmId: _mockFilm.id,
@ -159,6 +186,8 @@ void main() {
nd: photo.nd,
apertureValue: const ApertureValue(2.0, StopType.full),
shutterSpeedValue: photo.shutterSpeedValue,
equipmentProfileId: _mockEquipmentProfile.id,
filmId: _mockFilm.id,
),
);
await tester.tapDescendantTextOf<SettingsScreen>(S.current.logbook);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 KiB

After

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 KiB

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 737 KiB

After

Width:  |  Height:  |  Size: 572 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

After

Width:  |  Height:  |  Size: 479 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 KiB

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 343 KiB

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 535 KiB

After

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 339 KiB

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 KiB

After

Width:  |  Height:  |  Size: 512 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 792 KiB

After

Width:  |  Height:  |  Size: 617 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 499 KiB

After

Width:  |  Height:  |  Size: 506 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

After

Width:  |  Height:  |  Size: 228 KiB

View file

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:lightmeter/data/geolocation_service.dart';
import 'package:lightmeter/providers/equipment_profile_provider.dart';
import 'package:lightmeter/providers/films_provider.dart';
import 'package:lightmeter/providers/logbook_photos_provider.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
@ -36,6 +38,13 @@ void main() {
when(() => storageService.deletePhoto(any<String>())).thenAnswer((_) async {});
when(() => storageService.getPhotos()).thenAnswer((_) => Future.value(_customPhotos));
when(() => storageService.selectedEquipmentProfileId).thenReturn('');
when(() => storageService.getEquipmentProfiles()).thenAnswer((_) => Future.value({}));
when(() => storageService.selectedFilmId).thenReturn(const FilmStub().id);
when(() => storageService.getPredefinedFilms()).thenAnswer((_) => Future.value({}));
when(() => storageService.getCustomFilms()).thenAnswer((_) => Future.value({}));
when(() => geolocationService.getCurrentPosition()).thenAnswer((_) => Future.value());
});
@ -47,10 +56,16 @@ void main() {
await tester.pumpWidget(
IAPProducts(
isPro: isPro,
child: LogbookPhotosProvider(
child: EquipmentProfilesProvider(
storageService: storageService,
geolocationService: geolocationService,
child: const _Application(),
child: FilmsProvider(
storageService: storageService,
child: LogbookPhotosProvider(
storageService: storageService,
geolocationService: geolocationService,
child: const _Application(),
),
),
),
),
);

View file

@ -47,7 +47,7 @@ void main() {
await pumpApplication(tester);
expectReadingValueContainerText(S.current.equipmentProfile);
await tester.openAnimatedPicker<EquipmentProfilePicker>();
expect(find.byIcon(Icons.camera_outlined), findsOneWidget);
expect(find.byIcon(Icons.camera_alt_outlined), findsOneWidget);
expectDialogPickerText<EquipmentProfile>(S.current.equipmentProfile);
},
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 474 KiB

After

Width:  |  Height:  |  Size: 468 KiB