mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-26 01:10:39 +00:00
disable only list tiles
This commit is contained in:
parent
e1532736c2
commit
613a0f18a6
7 changed files with 60 additions and 266 deletions
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/generated/l10n.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/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';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
class EquipmentProfilesListTile extends StatelessWidget {
|
class EquipmentProfilesListTile extends StatelessWidget {
|
||||||
|
@ -8,7 +9,7 @@ class EquipmentProfilesListTile extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListTile(
|
return IAPListTile(
|
||||||
leading: const Icon(Icons.camera),
|
leading: const Icon(Icons.camera),
|
||||||
title: Text(S.of(context).equipmentProfiles),
|
title: Text(S.of(context).equipmentProfiles),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/generated/l10n.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/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_iap/m3_lightmeter_iap.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
|
@ -9,7 +10,7 @@ class FilmsListTile extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListTile(
|
return IAPListTile(
|
||||||
leading: const Icon(Icons.camera_roll),
|
leading: const Icon(Icons.camera_roll),
|
||||||
title: Text(S.of(context).filmsInUse),
|
title: Text(S.of(context).filmsInUse),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
|
|
@ -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/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/equipment/components/films/widget_list_tile_films.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.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 {
|
class EquipmentSettingsSection extends StatelessWidget {
|
||||||
const EquipmentSettingsSection({super.key});
|
const EquipmentSettingsSection({super.key});
|
||||||
|
@ -12,7 +11,6 @@ class EquipmentSettingsSection extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SettingsSection(
|
return SettingsSection(
|
||||||
title: S.of(context).equipment,
|
title: S.of(context).equipment,
|
||||||
enabled: IAPProducts.isPurchased(context, IAPProductType.paidFeatures),
|
|
||||||
children: const [
|
children: const [
|
||||||
EquipmentProfilesListTile(),
|
EquipmentProfilesListTile(),
|
||||||
FilmsListTile(),
|
FilmsListTile(),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
|
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||||
|
|
||||||
class BuyProListTile extends StatelessWidget {
|
class BuyProListTile extends StatelessWidget {
|
||||||
const BuyProListTile({super.key});
|
const BuyProListTile({super.key});
|
||||||
|
@ -28,6 +29,7 @@ class BuyProListTile extends StatelessWidget {
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
IAPProductsProvider.of(context).buy(IAPProductType.paidFeatures);
|
||||||
},
|
},
|
||||||
child: Text(S.of(context).buy),
|
child: Text(S.of(context).buy),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,12 +4,10 @@ import 'package:lightmeter/res/dimens.dart';
|
||||||
class SettingsSection extends StatelessWidget {
|
class SettingsSection extends StatelessWidget {
|
||||||
final String title;
|
final String title;
|
||||||
final List<Widget> children;
|
final List<Widget> children;
|
||||||
final bool enabled;
|
|
||||||
|
|
||||||
const SettingsSection({
|
const SettingsSection({
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.children,
|
required this.children,
|
||||||
this.enabled = true,
|
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -25,8 +23,6 @@ class SettingsSection extends StatelessWidget {
|
||||||
child: Card(
|
child: Card(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM),
|
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM),
|
||||||
child: Opacity(
|
|
||||||
opacity: enabled ? Dimens.enabledOpacity : Dimens.disabledOpacity,
|
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
@ -46,7 +42,6 @@ class SettingsSection extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue