Frontend first draft
This commit is contained in:
114
frontend/pweb/lib/pages/settings/widgets/pick.dart
Normal file
114
frontend/pweb/lib/pages/settings/widgets/pick.dart
Normal file
@@ -0,0 +1,114 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
import 'package:pweb/pages/settings/widgets/base.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: ListView(
|
||||
shrinkWrap: true,
|
||||
children: displayedOptions.map((o) => RadioListTile<T>(
|
||||
value: o,
|
||||
groupValue: initial,
|
||||
title: Text(labelBuilder(o)),
|
||||
onChanged: isSaving ? null : (v) { if (v != null) onSave(v); },
|
||||
)).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;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user