2023-03-30 19:24:18 +00:00
|
|
|
import 'package:flutter/material.dart';
|
2023-11-14 11:26:34 +00:00
|
|
|
import 'package:flutter/scheduler.dart';
|
2023-03-30 19:24:18 +00:00
|
|
|
import 'package:lightmeter/generated/l10n.dart';
|
|
|
|
import 'package:lightmeter/res/dimens.dart';
|
|
|
|
|
2023-09-14 14:59:16 +00:00
|
|
|
class DialogFilter<T> extends StatefulWidget {
|
2023-03-30 19:24:18 +00:00
|
|
|
final Icon icon;
|
|
|
|
final String title;
|
|
|
|
final String description;
|
|
|
|
final List<T> values;
|
|
|
|
final List<T> selectedValues;
|
|
|
|
final String Function(BuildContext context, T value) titleAdapter;
|
|
|
|
|
|
|
|
const DialogFilter({
|
|
|
|
required this.icon,
|
|
|
|
required this.title,
|
|
|
|
required this.description,
|
|
|
|
required this.values,
|
|
|
|
required this.selectedValues,
|
|
|
|
required this.titleAdapter,
|
|
|
|
super.key,
|
|
|
|
});
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<DialogFilter<T>> createState() => _DialogFilterState<T>();
|
|
|
|
}
|
|
|
|
|
2023-09-14 14:59:16 +00:00
|
|
|
class _DialogFilterState<T> extends State<DialogFilter<T>> {
|
2023-03-30 19:24:18 +00:00
|
|
|
late final List<bool> checkboxValues = List.generate(
|
|
|
|
widget.values.length,
|
2023-09-14 14:59:16 +00:00
|
|
|
(index) => widget.selectedValues.any((element) => element == widget.values[index]),
|
2023-03-30 19:24:18 +00:00
|
|
|
growable: false,
|
|
|
|
);
|
|
|
|
|
|
|
|
bool get _hasAnySelected => checkboxValues.contains(true);
|
|
|
|
bool get _hasAnyUnselected => checkboxValues.contains(false);
|
|
|
|
|
2023-11-14 11:26:34 +00:00
|
|
|
final ScrollController _scrollController = ScrollController();
|
2023-09-28 21:29:33 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
2023-11-14 11:26:34 +00:00
|
|
|
SchedulerBinding.instance.addPostFrameCallback((_) {
|
|
|
|
int i = 0;
|
|
|
|
for (; i < checkboxValues.length; i++) {
|
|
|
|
if (checkboxValues[i]) {
|
|
|
|
break;
|
|
|
|
}
|
2023-09-28 21:29:33 +00:00
|
|
|
}
|
2023-11-14 11:26:34 +00:00
|
|
|
_scrollController.jumpTo((Dimens.grid56 * i).clamp(0, _scrollController.position.maxScrollExtent));
|
|
|
|
});
|
2023-09-28 21:29:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
_scrollController.dispose();
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
2023-03-30 19:24:18 +00:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return AlertDialog(
|
|
|
|
icon: widget.icon,
|
|
|
|
titlePadding: Dimens.dialogIconTitlePadding,
|
|
|
|
title: Text(widget.title),
|
|
|
|
contentPadding: EdgeInsets.zero,
|
2023-11-14 11:26:34 +00:00
|
|
|
content: SizedBox(
|
|
|
|
width: double.maxFinite,
|
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
Padding(
|
|
|
|
padding: Dimens.dialogIconTitlePadding,
|
|
|
|
child: Text(widget.description),
|
|
|
|
),
|
|
|
|
const Divider(),
|
|
|
|
Expanded(
|
|
|
|
child: SingleChildScrollView(
|
|
|
|
controller: _scrollController,
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
children: List.generate(
|
|
|
|
widget.values.length,
|
|
|
|
(index) => CheckboxListTile(
|
|
|
|
value: checkboxValues[index],
|
|
|
|
controlAffinity: ListTileControlAffinity.leading,
|
|
|
|
title: Text(
|
|
|
|
widget.titleAdapter(context, widget.values[index]),
|
|
|
|
style: Theme.of(context).textTheme.bodyLarge,
|
|
|
|
),
|
|
|
|
onChanged: (value) {
|
|
|
|
if (value != null) {
|
|
|
|
setState(() {
|
|
|
|
checkboxValues[index] = value;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
2023-03-30 19:24:18 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
2023-11-14 11:26:34 +00:00
|
|
|
const Divider(),
|
|
|
|
Padding(
|
|
|
|
padding: Dimens.dialogActionsPadding,
|
|
|
|
child: Row(
|
|
|
|
children: [
|
|
|
|
SizedBox(
|
|
|
|
width: 40,
|
|
|
|
child: IconButton(
|
|
|
|
padding: EdgeInsets.zero,
|
2024-05-20 15:08:37 +00:00
|
|
|
icon: Icon(_hasAnyUnselected ? Icons.select_all_outlined : Icons.deselect_outlined),
|
2023-11-14 11:26:34 +00:00
|
|
|
onPressed: _toggleAll,
|
|
|
|
tooltip: _hasAnyUnselected ? S.of(context).tooltipSelectAll : S.of(context).tooltipDesecelectAll,
|
|
|
|
),
|
2023-03-30 19:24:18 +00:00
|
|
|
),
|
2023-11-14 11:26:34 +00:00
|
|
|
const Spacer(),
|
|
|
|
TextButton(
|
|
|
|
onPressed: Navigator.of(context).pop,
|
|
|
|
child: Text(S.of(context).cancel),
|
|
|
|
),
|
|
|
|
TextButton(
|
|
|
|
onPressed: _hasAnySelected
|
|
|
|
? () {
|
|
|
|
final List<T> selectedValues = [];
|
|
|
|
for (int i = 0; i < widget.values.length; i++) {
|
|
|
|
if (checkboxValues[i]) {
|
|
|
|
selectedValues.add(widget.values[i]);
|
|
|
|
}
|
2023-03-30 19:24:18 +00:00
|
|
|
}
|
2023-11-14 11:26:34 +00:00
|
|
|
Navigator.of(context).pop(selectedValues);
|
2023-03-30 19:24:18 +00:00
|
|
|
}
|
2023-11-14 11:26:34 +00:00
|
|
|
: null,
|
|
|
|
child: Text(S.of(context).save),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
2024-04-06 17:14:37 +00:00
|
|
|
),
|
2023-11-14 11:26:34 +00:00
|
|
|
],
|
|
|
|
),
|
2023-03-30 19:24:18 +00:00
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _toggleAll() {
|
|
|
|
setState(() {
|
|
|
|
if (_hasAnyUnselected) {
|
|
|
|
checkboxValues.fillRange(0, checkboxValues.length, true);
|
|
|
|
} else {
|
|
|
|
checkboxValues.fillRange(0, checkboxValues.length, false);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|