mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-22 07:20:39 +00:00
moved equipment profiles screen from iap
This commit is contained in:
parent
284e95854d
commit
82c63803bb
5 changed files with 367 additions and 18 deletions
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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) {},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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()));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue