NakedCombobox
Editable selection with filtered options
Use a combobox when users should type to filter choices. Keep the text controller, selected value and filtering in the caller. NakedCombobox<T> manages the option overlay, highlight, keyboard selection and semantics. NakedComboboxOption<T> supplies each option’s interaction state.
Keep option values stable across filtering. Dispose your text and focus controllers. Provide an accessible label for the editable field and a visible label for every option.
Basic implementation
import 'package:flutter/material.dart';
import 'package:naked_ui/naked_ui.dart';
import '../src/example_app.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const ExampleApp(child: Center(child: ComboboxExample()));
}
}
class ComboboxExample extends StatelessWidget {
const ComboboxExample({super.key});
static const fruits = ['Apple', 'Apricot', 'Banana', 'Blueberry', 'Cherry'];
@override
Widget build(BuildContext context) {
return SizedBox(
width: 240,
child: NakedCombobox<String>(
optionsBuilder: (value) {
final query = value.text.toLowerCase();
return fruits.where((fruit) => fruit.toLowerCase().startsWith(query));
},
onSelected: (_) {},
fieldBuilder:
(context, state, controller, focusNode, onFieldSubmitted) {
return NakedTextField(
controller: controller,
focusNode: focusNode,
onSubmitted: (_) => onFieldSubmitted(),
builder: (context, fieldState, child) {
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 10,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: fieldState.isFocused
? Colors.blue
: Colors.grey.shade300,
),
),
child: child,
);
},
);
},
overlayBuilder: (context, options) {
return Padding(
padding: const .only(top: 4),
child: Material(
elevation: 4,
shadowColor: Colors.black.withAlpha(50),
clipBehavior: .antiAlias,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: .circular(10),
side: BorderSide(color: Colors.grey.shade300),
),
child: Column(
mainAxisSize: .min,
children: [
for (final fruit in options)
NakedCombobox.Option<String>(
value: fruit,
builder: (context, state, _) {
return Container(
width: .infinity,
color: state.isHighlighted
? Colors.blue.shade50
: null,
padding: const .symmetric(
horizontal: 12,
vertical: 8,
),
child: Text(fruit),
);
},
),
],
),
),
);
},
),
);
}
}Source and accessibility
See the package implementation for the complete API. Keep visible labels, focus indication and sufficient contrast in your presentation layer.