mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-24 08:20:40 +00:00
replaced old EquipmentProfilesScreen
This commit is contained in:
parent
c8eafd9e2c
commit
3f0eef67a0
7 changed files with 11 additions and 378 deletions
|
@ -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';
|
||||
|
|
|
@ -112,9 +112,9 @@ class EquipmentProfileEditBloc extends Bloc<EquipmentProfileEditEvent, Equipment
|
|||
Future<void> _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<EquipmentProfileEditEvent, Equipment
|
|||
),
|
||||
);
|
||||
} else {
|
||||
await profilesProvider.updateProfile(
|
||||
await profilesProvider.addProfile(
|
||||
EquipmentProfile(
|
||||
id: _originalEquipmentProfile.id,
|
||||
id: const Uuid().v1(),
|
||||
name: state.name,
|
||||
apertureValues: state.apertureValues,
|
||||
ndValues: state.ndValues,
|
||||
|
|
|
@ -19,7 +19,7 @@ class _EquipmentProfilesScreenState extends State<EquipmentProfilesScreen> 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<EquipmentProfilesScreen> 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
|
||||
|
|
|
@ -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<EquipmentProfile> 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<EquipmentProfileContainer> createState() => EquipmentProfileContainerState();
|
||||
}
|
||||
|
||||
class EquipmentProfileContainerState extends State<EquipmentProfileContainer> 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<double> get _progress => listenable as Animation<double>;
|
||||
|
||||
@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<double> get _progress => listenable as Animation<double>;
|
||||
|
||||
@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<List<ApertureValue>> onApertureValuesSelected;
|
||||
final ValueChanged<List<IsoValue>> onIsoValuesSelecred;
|
||||
final ValueChanged<List<NdValue>> onNdValuesSelected;
|
||||
final ValueChanged<List<ShutterSpeedValue>> onShutterSpeedValuesSelected;
|
||||
final ValueChanged<double> 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<double> get _progress => listenable as Animation<double>;
|
||||
|
||||
@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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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<EquipmentProfilesScreen> createState() => _EquipmentProfilesScreenState();
|
||||
}
|
||||
|
||||
class _EquipmentProfilesScreenState extends State<EquipmentProfilesScreen> {
|
||||
final Map<String, GlobalKey<EquipmentProfileContainerState>> 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<String> idsToAdd = [];
|
||||
for (final profile in profiles) {
|
||||
if (!keysMap.keys.contains(profile.id)) idsToAdd.add(profile.id);
|
||||
}
|
||||
for (final id in idsToAdd) {
|
||||
keysMap[id] = GlobalKey<EquipmentProfileContainerState>(debugLabel: id);
|
||||
}
|
||||
idsToAdd.clear();
|
||||
} else if (profiles.length < keysMap.length) {
|
||||
// profile deleted
|
||||
final List<String> 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<EquipmentProfile>(
|
||||
MaterialPageRoute(builder: (_) => const EquipmentProfilesScreen()),
|
||||
);
|
||||
Navigator.of(context).pushNamed(NavigationRoutes.equipmentProfilesListScreen.name);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
Loading…
Reference in a new issue