123 lines
3.5 KiB
Dart
123 lines
3.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import 'package:pweb/pages/settings/widgets/base.dart';
|
|
|
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
|
|
|
|
|
const _kSearchThreshold = 12;
|
|
|
|
class SelectValueTile<T> extends BaseEditTile<T> {
|
|
final double? maxEditorHeight;
|
|
final double? maxEditorWidth;
|
|
|
|
const SelectValueTile({
|
|
super.key,
|
|
required super.icon,
|
|
required super.title,
|
|
required super.valueGetter,
|
|
required super.valueSetter,
|
|
required super.errorSituation,
|
|
required this.options,
|
|
required this.labelBuilder,
|
|
this.filterOptions,
|
|
this.maxEditorHeight,
|
|
this.maxEditorWidth,
|
|
});
|
|
|
|
final List<T> options;
|
|
final String Function(T) labelBuilder;
|
|
final List<T> Function(String)? filterOptions;
|
|
|
|
@override
|
|
bool get useDialogEditor => true;
|
|
|
|
@override
|
|
Widget buildView(BuildContext context, T? value) {
|
|
return Text(
|
|
value == null ? AppLocalizations.of(context)!.notSet : labelBuilder(value),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget buildEditor(
|
|
BuildContext context,
|
|
T? initial,
|
|
void Function(T) onSave,
|
|
VoidCallback onCancel,
|
|
bool isSaving,
|
|
) {
|
|
// local state for the current search query
|
|
String searchText = '';
|
|
|
|
return StatefulBuilder(
|
|
builder: (context, setState) {
|
|
// decide which list to show
|
|
final displayedOptions = (options.length > _kSearchThreshold && filterOptions != null && searchText.isNotEmpty)
|
|
? filterOptions!(searchText)
|
|
: options;
|
|
|
|
final content = Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
if (options.length > _kSearchThreshold && filterOptions != null)
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 4),
|
|
child: TextField(
|
|
decoration: InputDecoration(
|
|
hintText: AppLocalizations.of(context)!.search,
|
|
prefixIcon: const Icon(Icons.search),
|
|
border: const OutlineInputBorder(),
|
|
isDense: true,
|
|
),
|
|
onChanged: (value) {
|
|
// update the local searchText and rebuild the list
|
|
setState(() {
|
|
searchText = value;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
Flexible(
|
|
child: RadioGroup<T>(
|
|
groupValue: initial,
|
|
onChanged: (value) {
|
|
if (value != null && !isSaving) {
|
|
onSave(value);
|
|
}
|
|
},
|
|
child: ListView(
|
|
shrinkWrap: true,
|
|
children: displayedOptions.map((o) => RadioListTile<T>(
|
|
value: o,
|
|
title: Text(labelBuilder(o)),
|
|
enabled: !isSaving,
|
|
)).toList(),
|
|
),
|
|
),
|
|
),
|
|
const Divider(),
|
|
TextButton(
|
|
onPressed: onCancel,
|
|
child: Text(AppLocalizations.of(context)!.cancel),
|
|
),
|
|
],
|
|
);
|
|
|
|
// if the caller passed a max size, enforce it:
|
|
if (maxEditorHeight != null || maxEditorWidth != null) {
|
|
return ConstrainedBox(
|
|
constraints: BoxConstraints(
|
|
maxHeight: maxEditorHeight ?? double.infinity,
|
|
maxWidth: maxEditorWidth ?? double.infinity,
|
|
),
|
|
child: content,
|
|
);
|
|
}
|
|
|
|
return content;
|
|
},
|
|
);
|
|
}
|
|
}
|