disable only list tiles

This commit is contained in:
Vadim 2023-09-17 16:05:01 +02:00
parent e1532736c2
commit 613a0f18a6
7 changed files with 60 additions and 266 deletions

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.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 {
@ -8,7 +9,7 @@ class EquipmentProfilesListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
return IAPListTile(
leading: const Icon(Icons.camera),
title: Text(S.of(context).equipmentProfiles),
onTap: () {

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/screens/settings/components/shared/dialog_filter/widget_dialog_filter.dart';
import 'package:lightmeter/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
@ -9,7 +10,7 @@ class FilmsListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
return IAPListTile(
leading: const Icon(Icons.camera_roll),
title: Text(S.of(context).filmsInUse),
onTap: () {

View file

@ -3,7 +3,6 @@ import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/widget_list_tile_equipment_profiles.dart';
import 'package:lightmeter/screens/settings/components/equipment/components/films/widget_list_tile_films.dart';
import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
class EquipmentSettingsSection extends StatelessWidget {
const EquipmentSettingsSection({super.key});
@ -12,7 +11,6 @@ class EquipmentSettingsSection extends StatelessWidget {
Widget build(BuildContext context) {
return SettingsSection(
title: S.of(context).equipment,
enabled: IAPProducts.isPurchased(context, IAPProductType.paidFeatures),
children: const [
EquipmentProfilesListTile(),
FilmsListTile(),

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
class BuyProListTile extends StatelessWidget {
const BuyProListTile({super.key});
@ -28,6 +29,7 @@ class BuyProListTile extends StatelessWidget {
FilledButton(
onPressed: () {
Navigator.of(context).pop();
IAPProductsProvider.of(context).buy(IAPProductType.paidFeatures);
},
child: Text(S.of(context).buy),
),

View file

@ -1,242 +0,0 @@
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

@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
/// Depends on the product status and replaces [onTap] with purchase callback
/// if the product is purchasable.
class IAPListTile extends StatelessWidget {
final IAPProductType product;
final Icon leading;
final Text title;
final VoidCallback onTap;
const IAPListTile({
this.product = IAPProductType.paidFeatures,
required this.leading,
required this.title,
required this.onTap,
super.key,
});
@override
Widget build(BuildContext context) {
return Opacity(
opacity: IAPProducts.isPurchased(context, product)
? Dimens.enabledOpacity
: Dimens.disabledOpacity,
child: ListTile(
leading: leading,
title: title,
onTap: switch (IAPProducts.productOf(context, product)?.status) {
IAPProductStatus.purchasable => () => IAPProductsProvider.of(context).buy(product),
IAPProductStatus.pending => null,
IAPProductStatus.purchased => onTap,
null => null,
},
),
);
}
}

View file

@ -4,12 +4,10 @@ import 'package:lightmeter/res/dimens.dart';
class SettingsSection extends StatelessWidget {
final String title;
final List<Widget> children;
final bool enabled;
const SettingsSection({
required this.title,
required this.children,
this.enabled = true,
super.key,
});
@ -25,25 +23,22 @@ class SettingsSection extends StatelessWidget {
child: Card(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM),
child: Opacity(
opacity: enabled ? Dimens.enabledOpacity : Dimens.disabledOpacity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
child: Text(
title,
style: Theme.of(context)
.textTheme
.labelLarge
?.copyWith(color: Theme.of(context).colorScheme.onSurface),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
child: Text(
title,
style: Theme.of(context)
.textTheme
.labelLarge
?.copyWith(color: Theme.of(context).colorScheme.onSurface),
),
...children,
],
),
),
...children,
],
),
),
),