added LightmeterProSettingsSection

This commit is contained in:
Vadim 2023-09-17 12:21:19 +02:00
parent db86b001aa
commit a1e6214671
7 changed files with 293 additions and 70 deletions

View file

@ -93,8 +93,10 @@
}
}
},
"lightmeterPro": "Lightmeter PRO",
"buyLightmeterPro": "Buy Lightmeter PRO",
"lightmeterProDescription": "TBD",
"buyLightmeterPro": "Buy Lightmeter Pro",
"lightmeterPro": "Lightmeter Pro",
"lightmeterProDescription": "Unlocks extra features, such as equipment profiles containing aperture, shutter speed, ISO and ND values filters and films.\n\nThe source code of Lightmeter is available on GitHub. You are welcome to compile it yourself. However, if you want to support the development and receive new features and updates, consider purchasing Lightmeter Pro.",
"equipmentProfilesFeatureDescription": "Each equipment profile allows you to select:\n- Aperture values and shutter speeds, that your lens and camera have\n- ND filters, that fit the chosen lens\n- ISO values, that your camera supports\nCreating multiple profiles for different cameras and lenses allows you to easily switch between them and always have the relevant readings!",
"filmsInUseFeatureDescription": "Select the films that you usually use. Selecting one will apply a correction to shutter speeds greater than 1\" to compensate for the reciprocity failure.",
"buy": "Buy"
}
}

View file

@ -1,41 +0,0 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
class BuyProScreen extends StatelessWidget {
const BuyProScreen({super.key});
@override
Widget build(BuildContext context) {
return SliverScreen(
title: S.of(context).lightmeterPro,
appBarActions: [
IconButton(
onPressed: Navigator.of(context).pop,
icon: const Icon(Icons.close),
),
],
slivers: [
SliverList(
delegate: SliverChildListDelegate(
[
Text(S.of(context).lightmeterProDescription),
Row(
children: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(true);
},
child: Text(S.of(context).buy),
),
],
)
],
),
),
SliverToBoxAdapter(child: SizedBox(height: MediaQuery.paddingOf(context).bottom)),
],
);
}
}

View file

@ -1,20 +0,0 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/screens/settings/components/buy_pro/components/equipment_profiles/components/equipment_profile_screen/screen_buy_pro.dart';
class BuyProListTile extends StatelessWidget {
const BuyProListTile({super.key});
@override
Widget build(BuildContext context) {
return ListTile(
leading: const Icon(Icons.camera),
title: Text(S.of(context).buyLightmeterPro),
onTap: () {
Navigator.of(context).push<bool>(
MaterialPageRoute(builder: (_) => const BuyProScreen()),
);
},
);
}
}

View file

@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/res/dimens.dart';
class BuyProListTile extends StatelessWidget {
const BuyProListTile({super.key});
@override
Widget build(BuildContext context) {
return ListTile(
leading: const Icon(Icons.star),
title: Text(S.of(context).buyLightmeterPro),
onTap: () {
showDialog(
context: context,
builder: (_) => AlertDialog(
icon: const Icon(Icons.star),
titlePadding: Dimens.dialogIconTitlePadding,
title: Text(S.of(context).lightmeterPro),
contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingL),
content: SingleChildScrollView(child: Text(S.of(context).lightmeterProDescription)),
actionsPadding: Dimens.dialogActionsPadding,
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: Text(S.of(context).cancel),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(S.of(context).buy),
),
],
),
);
},
);
}
}

View file

@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/screens/settings/components/buy_pro/components/equipment_profiles/widget_list_tile_buy_pro.dart';
import 'package:lightmeter/screens/settings/components/lightmeter_pro/components/buy_pro/widget_list_tile_buy_pro.dart';
import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart';
class BuyProSettingsSection extends StatelessWidget {
const BuyProSettingsSection({super.key});
class LightmeterProSettingsSection extends StatelessWidget {
const LightmeterProSettingsSection({super.key});
@override
Widget build(BuildContext context) {

View file

@ -0,0 +1,242 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/widget_list_tiles_equipments.dart';
import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_name_dialog/widget_dialog_equipment_profile_name.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
class ExpandableSection extends StatefulWidget {
final EquipmentProfile data;
final ValueChanged<EquipmentProfile> onUpdate;
final VoidCallback onDelete;
final VoidCallback onExpand;
const ExpandableSection({
required this.data,
required this.onUpdate,
required this.onDelete,
required this.onExpand,
super.key,
});
@override
State<ExpandableSection> createState() => ExpandableSectionState();
}
class ExpandableSectionState extends State<ExpandableSection>
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,
);
late final AnimationController _controller = AnimationController(
duration: Dimens.durationM,
vsync: this,
);
bool get _expanded => _controller.isCompleted;
@override
void didUpdateWidget(ExpandableSection 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,
);
}
@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: Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
_AnimatedArrowButton(
controller: _controller,
onPressed: () => _expanded ? collapse() : expand(),
),
IconButton(
onPressed: widget.onDelete,
icon: const Icon(Icons.delete),
),
],
),
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);
},
),
],
),
),
);
}
void _showNameDialog() {
showDialog<String>(
context: context,
builder: (_) => EquipmentProfileNameDialog(initialValue: _equipmentData.name),
).then((value) {
if (value != null) {
_equipmentData = _equipmentData.copyWith(name: value);
widget.onUpdate(_equipmentData);
}
});
}
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,
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),
),
);
}
}
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;
const _AnimatedEquipmentListTiles({
required AnimationController controller,
required this.equipmentData,
required this.onApertureValuesSelected,
required this.onIsoValuesSelecred,
required this.onNdValuesSelected,
required this.onShutterSpeedValuesSelected,
}) : 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 * 4,
),
child: Opacity(
opacity: _progress.value,
child: EquipmentListTiles(
selectedApertureValues: equipmentData.apertureValues,
selectedIsoValues: equipmentData.isoValues,
selectedNdValues: equipmentData.ndValues,
selectedShutterSpeedValues: equipmentData.shutterSpeedValues,
onApertureValuesSelected: onApertureValuesSelected,
onIsoValuesSelecred: onIsoValuesSelecred,
onNdValuesSelected: onNdValuesSelected,
onShutterSpeedValuesSelected: onShutterSpeedValuesSelected,
),
),
);
}
}

View file

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/screens/settings/components/about/widget_settings_section_about.dart';
import 'package:lightmeter/screens/settings/components/buy_pro/widget_settings_section_pro.dart';
import 'package:lightmeter/screens/settings/components/lightmeter_pro/widget_settings_section_lightmeter_pro.dart';
import 'package:lightmeter/screens/settings/components/equipment/widget_settings_section_equipment.dart';
import 'package:lightmeter/screens/settings/components/general/widget_settings_section_general.dart';
import 'package:lightmeter/screens/settings/components/metering/widget_settings_section_metering.dart';
@ -44,7 +44,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
SliverList(
delegate: SliverChildListDelegate(
<Widget>[
const BuyProSettingsSection(),
const LightmeterProSettingsSection(),
const MeteringSettingsSection(),
const EquipmentSettingsSection(),
const GeneralSettingsSection(),