90 lines
2.1 KiB
Dart
90 lines
2.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import 'app_searchable_dropdown.dart';
|
|
|
|
class AppDropdownOption<T> {
|
|
const AppDropdownOption({
|
|
required this.value,
|
|
required this.label,
|
|
this.subtitle,
|
|
this.searchText,
|
|
});
|
|
|
|
final T value;
|
|
final String label;
|
|
|
|
/// Secondary line under [label] in the picker (e.g. item code).
|
|
final String? subtitle;
|
|
|
|
/// Extra searchable text (defaults to [label] + [subtitle]).
|
|
final String? searchText;
|
|
|
|
String get searchable =>
|
|
(searchText ?? [label, if (subtitle != null) subtitle!].join(' '))
|
|
.toLowerCase();
|
|
|
|
bool matchesQuery(String query) {
|
|
final q = query.trim().toLowerCase();
|
|
if (q.isEmpty) return true;
|
|
return searchable.contains(q);
|
|
}
|
|
}
|
|
|
|
/// Searchable dropdown — all app dropdowns use [AppSearchableDropdown] under the hood.
|
|
class AppDropdown<T> extends StatelessWidget {
|
|
const AppDropdown({
|
|
super.key,
|
|
required this.label,
|
|
required this.value,
|
|
required this.options,
|
|
required this.onChanged,
|
|
this.validator,
|
|
this.hint,
|
|
this.searchHint,
|
|
this.enabled = true,
|
|
this.isDense = false,
|
|
this.addNewLabel,
|
|
this.onAddNew,
|
|
});
|
|
|
|
final String label;
|
|
final T? value;
|
|
final List<AppDropdownOption<T>> options;
|
|
final ValueChanged<T?> onChanged;
|
|
final String? Function(T?)? validator;
|
|
final String? hint;
|
|
final String? searchHint;
|
|
final bool enabled;
|
|
final bool isDense;
|
|
final String? addNewLabel;
|
|
final Future<void> Function()? onAddNew;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AppSearchableDropdown<T>(
|
|
label: label,
|
|
value: value,
|
|
options: options,
|
|
onChanged: onChanged,
|
|
validator: validator,
|
|
hint: hint,
|
|
searchHint: searchHint ?? 'Search ${label.toLowerCase()}...',
|
|
enabled: enabled,
|
|
isDense: isDense,
|
|
addNewLabel: addNewLabel,
|
|
onAddNew: onAddNew,
|
|
);
|
|
}
|
|
}
|
|
|
|
List<AppDropdownOption<String>> stringDropdownOptions(List<String> values) {
|
|
return values
|
|
.map(
|
|
(value) => AppDropdownOption(
|
|
value: value,
|
|
label: value.replaceAll('_', ' '),
|
|
),
|
|
)
|
|
.toList();
|
|
}
|