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:flutter/material.dart';
|
||||||
import 'package:lightmeter/generated/l10n.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 {
|
class EquipmentProfilesListTile extends StatelessWidget {
|
||||||
const EquipmentProfilesListTile({super.key});
|
const EquipmentProfilesListTile({super.key});
|
||||||
|
@ -11,23 +13,8 @@ class EquipmentProfilesListTile extends StatelessWidget {
|
||||||
leading: const Icon(Icons.camera),
|
leading: const Icon(Icons.camera),
|
||||||
title: Text(S.of(context).equipmentProfiles),
|
title: Text(S.of(context).equipmentProfiles),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showEquipmentProfilesDialog(
|
Navigator.of(context).push<EquipmentProfileData>(
|
||||||
context,
|
MaterialPageRoute(builder: (_) => const EquipmentProfileScreen()));
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue