diff --git a/lib/application.dart b/lib/application.dart index 7f90b35..a58de01 100644 --- a/lib/application.dart +++ b/lib/application.dart @@ -8,11 +8,11 @@ import 'package:lightmeter/navigation/routes.dart'; import 'package:lightmeter/platform_config.dart'; import 'package:lightmeter/providers/user_preferences_provider.dart'; import 'package:lightmeter/screens/equipment_profile_edit/flow_equipment_profile_edit.dart'; +import 'package:lightmeter/screens/equipment_profiles/screen_equipment_profiles.dart'; import 'package:lightmeter/screens/film_edit/flow_film_edit.dart'; import 'package:lightmeter/screens/films/screen_films.dart'; import 'package:lightmeter/screens/lightmeter_pro/screen_lightmeter_pro.dart'; import 'package:lightmeter/screens/metering/flow_metering.dart'; -import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart'; import 'package:lightmeter/screens/settings/flow_settings.dart'; import 'package:lightmeter/screens/shared/release_notes_dialog/flow_dialog_release_notes.dart'; import 'package:lightmeter/screens/timer/flow_timer.dart'; diff --git a/lib/screens/equipment_profile_edit/bloc_equipment_profile_edit.dart b/lib/screens/equipment_profile_edit/bloc_equipment_profile_edit.dart index d151b1c..ecdf2ec 100644 --- a/lib/screens/equipment_profile_edit/bloc_equipment_profile_edit.dart +++ b/lib/screens/equipment_profile_edit/bloc_equipment_profile_edit.dart @@ -112,9 +112,9 @@ class EquipmentProfileEditBloc extends Bloc _onSave(EquipmentProfileSaveEvent _, Emitter emit) async { emit(state.copyWith(isLoading: true)); if (_isEdit) { - await profilesProvider.addProfile( + await profilesProvider.updateProfile( EquipmentProfile( - id: const Uuid().v1(), + id: _originalEquipmentProfile.id, name: state.name, apertureValues: state.apertureValues, ndValues: state.ndValues, @@ -124,9 +124,9 @@ class EquipmentProfileEditBloc extends Bloc with @override Widget build(BuildContext context) { return SliverScreen( - title: Text(S.of(context).films), + title: Text(S.of(context).equipmentProfiles), appBarActions: [ IconButton( onPressed: _addProfile, @@ -28,9 +28,9 @@ class _EquipmentProfilesScreenState extends State with ), ], slivers: [ - if (EquipmentProfiles.of(context).isNotEmpty) + if (EquipmentProfiles.of(context).length > 1) _EquipmentProfilesListBuilder( - values: EquipmentProfiles.of(context).toList(), + values: EquipmentProfiles.of(context).skip(1).toList(), onEdit: _editProfile, ) else diff --git a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/widget_container_equipment_profile.dart b/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/widget_container_equipment_profile.dart deleted file mode 100644 index 043ca87..0000000 --- a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/widget_container_equipment_profile.dart +++ /dev/null @@ -1,250 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; -import 'package:lightmeter/generated/l10n.dart'; -import 'package:lightmeter/res/dimens.dart'; -import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; - -class EquipmentProfileContainer extends StatefulWidget { - final EquipmentProfile data; - final ValueChanged onUpdate; - final VoidCallback onCopy; - final VoidCallback onDelete; - final VoidCallback onExpand; - - const EquipmentProfileContainer({ - required this.data, - required this.onUpdate, - required this.onCopy, - required this.onDelete, - required this.onExpand, - super.key, - }); - - @override - State createState() => EquipmentProfileContainerState(); -} - -class EquipmentProfileContainerState extends State with TickerProviderStateMixin { - late EquipmentProfile _equipmentData = EquipmentProfile( - id: widget.data.id, - name: widget.data.name, - apertureValues: widget.data.apertureValues, - ndValues: widget.data.ndValues, - shutterSpeedValues: widget.data.shutterSpeedValues, - isoValues: widget.data.isoValues, - lensZoom: widget.data.lensZoom, - ); - - late final AnimationController _controller = AnimationController( - duration: Dimens.durationM, - vsync: this, - ); - bool get _expanded => _controller.isCompleted; - - @override - void didUpdateWidget(EquipmentProfileContainer oldWidget) { - super.didUpdateWidget(oldWidget); - _equipmentData = EquipmentProfile( - id: widget.data.id, - name: widget.data.name, - apertureValues: widget.data.apertureValues, - ndValues: widget.data.ndValues, - shutterSpeedValues: widget.data.shutterSpeedValues, - isoValues: widget.data.isoValues, - lensZoom: widget.data.lensZoom, - ); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Card( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM), - title: Row( - children: [ - _AnimatedNameLeading(controller: _controller), - const SizedBox(width: Dimens.grid8), - Flexible( - child: Text( - _equipmentData.name, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - trailing: _AnimatedArrowButton( - controller: _controller, - onPressed: () => _expanded ? collapse() : expand(), - ), - onTap: () => _expanded ? _showNameDialog() : expand(), - ), - _AnimatedEquipmentListTiles( - controller: _controller, - equipmentData: _equipmentData, - onApertureValuesSelected: (value) { - _equipmentData = _equipmentData.copyWith(apertureValues: value); - widget.onUpdate(_equipmentData); - }, - onIsoValuesSelecred: (value) { - _equipmentData = _equipmentData.copyWith(isoValues: value); - widget.onUpdate(_equipmentData); - }, - onNdValuesSelected: (value) { - _equipmentData = _equipmentData.copyWith(ndValues: value); - widget.onUpdate(_equipmentData); - }, - onShutterSpeedValuesSelected: (value) { - _equipmentData = _equipmentData.copyWith(shutterSpeedValues: value); - widget.onUpdate(_equipmentData); - }, - onLensZoomChanged: (value) { - _equipmentData = _equipmentData.copyWith(lensZoom: value); - widget.onUpdate(_equipmentData); - }, - onCopy: widget.onCopy, - onDelete: widget.onDelete, - ), - ], - ), - ), - ); - } - - void _showNameDialog() {} - - void expand() { - widget.onExpand(); - _controller.forward(); - SchedulerBinding.instance.addPostFrameCallback((_) { - Future.delayed(_controller.duration!).then((_) { - Scrollable.ensureVisible( - context, - alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtEnd, - duration: _controller.duration!, - ); - }); - }); - } - - void collapse() { - _controller.reverse(); - } -} - -class _AnimatedNameLeading extends AnimatedWidget { - const _AnimatedNameLeading({required AnimationController controller}) : super(listenable: controller); - - Animation get _progress => listenable as Animation; - - @override - Widget build(BuildContext context) { - return Padding( - padding: EdgeInsets.only(right: _progress.value * Dimens.grid8), - child: Icon( - Icons.edit_outlined, - size: _progress.value * Dimens.grid24, - ), - ); - } -} - -class _AnimatedArrowButton extends AnimatedWidget { - final VoidCallback onPressed; - - const _AnimatedArrowButton({ - required AnimationController controller, - required this.onPressed, - }) : super(listenable: controller); - - Animation get _progress => listenable as Animation; - - @override - Widget build(BuildContext context) { - return IconButton( - onPressed: onPressed, - icon: Transform.rotate( - angle: _progress.value * pi, - child: const Icon(Icons.keyboard_arrow_down_outlined), - ), - tooltip: _progress.value == 0 ? S.of(context).tooltipExpand : S.of(context).tooltipCollapse, - ); - } -} - -class _AnimatedEquipmentListTiles extends AnimatedWidget { - final EquipmentProfile equipmentData; - final ValueChanged> onApertureValuesSelected; - final ValueChanged> onIsoValuesSelecred; - final ValueChanged> onNdValuesSelected; - final ValueChanged> onShutterSpeedValuesSelected; - final ValueChanged onLensZoomChanged; - final VoidCallback onCopy; - final VoidCallback onDelete; - - const _AnimatedEquipmentListTiles({ - required AnimationController controller, - required this.equipmentData, - required this.onApertureValuesSelected, - required this.onIsoValuesSelecred, - required this.onNdValuesSelected, - required this.onShutterSpeedValuesSelected, - required this.onLensZoomChanged, - required this.onCopy, - required this.onDelete, - }) : super(listenable: controller); - - Animation get _progress => listenable as Animation; - - @override - Widget build(BuildContext context) { - return SizedOverflowBox( - alignment: Alignment.topCenter, - size: Size( - double.maxFinite, - _progress.value * Dimens.grid56 * 6, - ), - // https://github.com/gskinnerTeam/flutter-folio/pull/62 - child: Opacity( - opacity: _progress.value, - child: Column( - children: [ - ListTile( - contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM), - trailing: Row( - mainAxisAlignment: MainAxisAlignment.end, - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - onPressed: onCopy, - icon: const Icon(Icons.copy_outlined), - tooltip: S.of(context).tooltipCopy, - ), - IconButton( - onPressed: onDelete, - icon: const Icon(Icons.delete_outlined), - tooltip: S.of(context).tooltipDelete, - ), - ], - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart b/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart deleted file mode 100644 index 290d531..0000000 --- a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart +++ /dev/null @@ -1,114 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lightmeter/generated/l10n.dart'; -import 'package:lightmeter/providers/equipment_profile_provider.dart'; -import 'package:lightmeter/res/dimens.dart'; -import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/widget_container_equipment_profile.dart'; -import 'package:lightmeter/screens/shared/sliver_placeholder/widget_sliver_placeholder.dart'; -import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart'; -import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; - -class EquipmentProfilesScreen extends StatefulWidget { - const EquipmentProfilesScreen({super.key}); - - @override - State createState() => _EquipmentProfilesScreenState(); -} - -class _EquipmentProfilesScreenState extends State { - final Map> keysMap = {}; - int get profilesCount => keysMap.length; - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - _updateProfilesKeys(); - } - - @override - Widget build(BuildContext context) { - return SliverScreen( - title: Text(S.of(context).equipmentProfiles), - appBarActions: [ - IconButton( - onPressed: _addProfile, - icon: const Icon(Icons.add_outlined), - tooltip: S.of(context).tooltipAdd, - ), - ], - slivers: profilesCount == 1 - ? [SliverPlaceholder(onTap: _addProfile)] - : [ - SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - if (index == 0) { - // skip default profile - return const SizedBox.shrink(); - } - - final profile = EquipmentProfiles.of(context)[index]; - return Padding( - padding: EdgeInsets.fromLTRB( - Dimens.paddingM, - index == 0 ? Dimens.paddingM : 0, - Dimens.paddingM, - Dimens.paddingM, - ), - child: EquipmentProfileContainer( - key: keysMap[profile.id], - data: profile, - onExpand: () => _keepExpandedAt(index), - onUpdate: _updateProfileAt, - onCopy: () => _addProfile(profile), - onDelete: () => _removeProfileAt(profile), - ), - ); - }, - childCount: EquipmentProfiles.of(context).length, - ), - ), - SliverToBoxAdapter(child: SizedBox(height: MediaQuery.paddingOf(context).bottom)), - ], - ); - } - - void _addProfile([EquipmentProfile? copyFrom]) {} - - void _updateProfileAt(EquipmentProfile data) {} - - void _removeProfileAt(EquipmentProfile data) {} - - void _keepExpandedAt(int index) { - keysMap.values.toList().getRange(0, index).forEach((element) { - element.currentState?.collapse(); - }); - keysMap.values.toList().getRange(index + 1, profilesCount).forEach((element) { - element.currentState?.collapse(); - }); - } - - void _updateProfilesKeys() { - final profiles = EquipmentProfiles.of(context); - if (profiles.length > keysMap.length) { - // profile added - final List idsToAdd = []; - for (final profile in profiles) { - if (!keysMap.keys.contains(profile.id)) idsToAdd.add(profile.id); - } - for (final id in idsToAdd) { - keysMap[id] = GlobalKey(debugLabel: id); - } - idsToAdd.clear(); - } else if (profiles.length < keysMap.length) { - // profile deleted - final List idsToDelete = []; - for (final id in keysMap.keys) { - if (!profiles.any((p) => p.id == id)) idsToDelete.add(id); - } - idsToDelete.forEach(keysMap.remove); - idsToDelete.clear(); - } else { - // profile updated, no need to updated keys - } - } -} diff --git a/lib/screens/settings/components/metering/components/equipment_profiles/widget_list_tile_equipment_profiles.dart b/lib/screens/settings/components/metering/components/equipment_profiles/widget_list_tile_equipment_profiles.dart index 610dd59..1c8c6db 100644 --- a/lib/screens/settings/components/metering/components/equipment_profiles/widget_list_tile_equipment_profiles.dart +++ b/lib/screens/settings/components/metering/components/equipment_profiles/widget_list_tile_equipment_profiles.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/generated/l10n.dart'; -import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart'; +import 'package:lightmeter/navigation/routes.dart'; import 'package:lightmeter/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart'; -import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class EquipmentProfilesListTile extends StatelessWidget { const EquipmentProfilesListTile({super.key}); @@ -13,9 +12,7 @@ class EquipmentProfilesListTile extends StatelessWidget { leading: const Icon(Icons.camera_outlined), title: Text(S.of(context).equipmentProfiles), onTap: () { - Navigator.of(context).push( - MaterialPageRoute(builder: (_) => const EquipmentProfilesScreen()), - ); + Navigator.of(context).pushNamed(NavigationRoutes.equipmentProfilesListScreen.name); }, ); } diff --git a/screenshots/generate_screenshots.dart b/screenshots/generate_screenshots.dart index 77ad568..40dc892 100644 --- a/screenshots/generate_screenshots.dart +++ b/screenshots/generate_screenshots.dart @@ -15,10 +15,10 @@ import 'package:lightmeter/data/shared_prefs_service.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/res/theme.dart'; +import 'package:lightmeter/screens/equipment_profiles/screen_equipment_profiles.dart'; import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart'; import 'package:lightmeter/screens/metering/components/shared/readings_container/components/iso_picker/widget_picker_iso.dart'; import 'package:lightmeter/screens/metering/screen_metering.dart'; -import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart'; import 'package:lightmeter/screens/settings/screen_settings.dart'; import 'package:lightmeter/screens/shared/animated_circular_button/widget_button_circular_animated.dart'; import 'package:lightmeter/screens/timer/screen_timer.dart';