moved equipment profiles screen from iap

This commit is contained in:
Vadim 2023-03-19 22:08:29 +03:00
parent 284e95854d
commit 82c63803bb
5 changed files with 367 additions and 18 deletions

View file

@ -0,0 +1,139 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
class DialogFilter<T extends PhotographyValue> extends StatefulWidget {
final Icon icon;
final String title;
final String description;
final List<T> values;
final String Function(BuildContext context, T value) titleAdapter;
final bool rangeSelect;
const DialogFilter({
required this.icon,
required this.title,
required this.description,
required this.values,
required this.titleAdapter,
this.rangeSelect = false,
super.key,
});
@override
State<DialogFilter<T>> createState() => _DialogFilterState<T>();
}
class _DialogFilterState<T extends PhotographyValue> extends State<DialogFilter<T>> {
late final List<bool> _selectedValues = List.generate(
widget.values.length,
(_) => true,
growable: false,
);
bool get _hasAnySelected => _selectedValues.contains(true);
bool get _hasAnyUnselected => _selectedValues.contains(false);
@override
Widget build(BuildContext context) {
return AlertDialog(
icon: widget.icon,
titlePadding: Dimens.dialogIconTitlePadding,
title: Text(widget.title),
contentPadding: EdgeInsets.zero,
content: Column(
children: [
Padding(
padding: Dimens.dialogIconTitlePadding,
child: Text(widget.description),
),
const Divider(),
// TODO: try to find lazy-building solution
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: List.generate(
widget.values.length,
(index) => CheckboxListTile(
value: _selectedValues[index],
controlAffinity: ListTileControlAffinity.leading,
title: Text(
widget.titleAdapter(context, widget.values[index]),
style: Theme.of(context).textTheme.bodyLarge!,
),
onChanged: (value) {
if (value != null) {
setState(() {
if (widget.rangeSelect) {
if (value) {
final indexOfChecked = _selectedValues.indexOf(value);
if (indexOfChecked == -1) {
_selectedValues[index] = value;
} else if (indexOfChecked < index) {
_selectedValues.fillRange(indexOfChecked, index + 1, value);
} else {
_selectedValues.fillRange(index, indexOfChecked, value);
}
} else {
if (index > _selectedValues.length / 2) {
_selectedValues.fillRange(index, _selectedValues.length, false);
_selectedValues[index] = value;
} else {
_selectedValues.fillRange(0, index, false);
_selectedValues[index] = value;
}
}
} else {
_selectedValues[index] = value;
}
});
}
},
),
),
),
),
),
const Divider(),
Padding(
padding: Dimens.dialogActionsPadding,
child: Row(
children: [
SizedBox(
width: 40,
child: IconButton(
padding: EdgeInsets.zero,
icon: Icon(_hasAnyUnselected ? Icons.select_all : Icons.deselect),
onPressed: _toggleAll,
),
),
const Spacer(),
TextButton(
onPressed: Navigator.of(context).pop,
child: Text(S.of(context).cancel),
),
TextButton(
onPressed:
_hasAnySelected ? () => Navigator.of(context).pop(_selectedValues) : null,
child: Text(S.of(context).save),
),
],
),
)
],
),
);
}
void _toggleAll() {
setState(() {
if (_hasAnyUnselected) {
_selectedValues.fillRange(0, _selectedValues.length, true);
} else {
_selectedValues.fillRange(0, _selectedValues.length, false);
}
});
}
}

View file

@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import '../dialog_filter/widget_dialog_filter.dart';
class EquipmentListTile<T extends PhotographyValue> extends StatelessWidget {
final IconData icon;
final String title;
final String description;
final List<T> selectedValues;
final List<T> values;
final ValueChanged<List<T>> onChanged;
final bool rangeSelect;
const EquipmentListTile({
required this.icon,
required this.title,
required this.description,
required this.selectedValues,
required this.values,
required this.onChanged,
required this.rangeSelect,
super.key,
});
@override
Widget build(BuildContext context) {
return ListTile(
leading: Icon(icon),
title: Text(title),
onTap: () {
showDialog(
context: context,
builder: (_) => DialogFilter<T>(
icon: Icon(icon),
title: title,
description: description,
values: values,
titleAdapter: (_, value) => value.toString(),
rangeSelect: rangeSelect,
),
);
},
);
}
}

View file

@ -0,0 +1,141 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'components/equipment_list_tile/widget_list_tile_equipment.dart';
class EquipmentListTilesSection extends StatefulWidget {
final EquipmentProfileData data;
const EquipmentListTilesSection({required this.data, super.key});
@override
State<EquipmentListTilesSection> createState() => _EquipmentListTilesSectionState();
}
class _EquipmentListTilesSectionState extends State<EquipmentListTilesSection> {
final TextEditingController _nameController = TextEditingController(text: 'Default');
final FocusNode _fieldFocusNode = FocusNode();
bool _expanded = false;
@override
void dispose() {
_nameController.dispose();
_fieldFocusNode.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: [
Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(
Dimens.paddingM,
0,
Dimens.paddingM,
0,
),
child: IgnorePointer(
ignoring: !_expanded,
child: TextFormField(
focusNode: _fieldFocusNode,
controller: _nameController,
onChanged: (value) {},
decoration: const InputDecoration(
hintText: 'Profile name',
border: InputBorder.none,
),
),
),
),
),
Row(
children: [
_collapseButton(),
IconButton(
onPressed: () {},
icon: const Icon(Icons.delete),
),
],
),
],
),
if (_expanded) const _DialogsListTiles()
],
),
),
);
}
Widget _collapseButton() {
return IconButton(
onPressed: () {
setState(() {
_expanded = !_expanded;
});
if (!_expanded) {
_fieldFocusNode.unfocus();
}
},
icon: Icon(_expanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down),
);
}
}
class _DialogsListTiles extends StatelessWidget {
const _DialogsListTiles();
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
EquipmentListTile<IsoValue>(
icon: Icons.iso,
title: S.of(context).isoValues,
description: S.of(context).isoValuesFilterDescription,
values: isoValues,
selectedValues: const [],
rangeSelect: false,
onChanged: (value) {},
),
EquipmentListTile<NdValue>(
icon: Icons.filter_b_and_w,
title: S.of(context).ndFilters,
description: S.of(context).ndFiltersFilterDescription,
values: ndValues,
selectedValues: const [],
rangeSelect: false,
onChanged: (value) {},
),
EquipmentListTile<ApertureValue>(
icon: Icons.camera,
title: S.of(context).apertureValues,
description: S.of(context).apertureValuesFilterDescription,
values: apertureValues,
selectedValues: const [],
rangeSelect: true,
onChanged: (value) {},
),
EquipmentListTile<ShutterSpeedValue>(
icon: Icons.shutter_speed,
title: S.of(context).shutterSpeedValues,
description: S.of(context).shutterSpeedValuesFilterDescription,
values: shutterSpeedValues,
selectedValues: const [],
rangeSelect: true,
onChanged: (value) {},
),
],
);
}
}

View file

@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/providers/equipment_profile_provider.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'components/equipment_profile_section/widget_section_equipment_profile.dart';
class EquipmentProfileScreen extends StatefulWidget {
const EquipmentProfileScreen({super.key});
@override
State<EquipmentProfileScreen> createState() => _EquipmentProfileScreenState();
}
class _EquipmentProfileScreenState extends State<EquipmentProfileScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
elevation: 0,
title: Text(S.of(context).equipmentProfiles),
),
body: SafeArea(
bottom: false,
child: ListView.builder(
padding: const EdgeInsets.all(Dimens.paddingM),
itemCount: EquipmentProfiles.of(context)?.length ?? 0,
itemBuilder: (_, index) => EquipmentListTilesSection(
data: EquipmentProfiles.of(context)![index],
),
),
),
);
}
}

View file

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'components/equipment_profile_screen/screen_equipment_profile.dart';
class EquipmentProfilesListTile extends StatelessWidget {
const EquipmentProfilesListTile({super.key});
@ -11,23 +13,8 @@ class EquipmentProfilesListTile extends StatelessWidget {
leading: const Icon(Icons.camera),
title: Text(S.of(context).equipmentProfiles),
onTap: () {
showEquipmentProfilesDialog(
context,
EquipmentProfileSectionLocalizationData(
isoValues: S.of(context).isoValues,
isoValuesFilterDescription: S.of(context).isoValuesFilterDescription,
ndValues: S.of(context).ndFilters,
ndValuesFilterDescription: S.of(context).ndFiltersFilterDescription,
apertureValues: S.of(context).apertureValues,
apertureValuesFilterDescription: S.of(context).apertureValuesFilterDescription,
shutterSpeedValues: S.of(context).shutterSpeedValues,
shutterSpeedValuesFilterDescription: S.of(context).shutterSpeedValuesFilterDescription,
dialogFilterLocalizationData: DialogFilterLocalizationData(
cancel: S.of(context).cancel,
save: S.of(context).save,
),
),
);
Navigator.of(context).push<EquipmentProfileData>(
MaterialPageRoute(builder: (_) => const EquipmentProfileScreen()));
},
);
}