NakedTextField
Headless text input component with native editing behavior
Headless text input component. Preserves native text editing behavior (selection handles, keyboard shortcuts, IME). Use builder pattern for custom styling.
When to use this
- Form inputs: Text fields in forms (name, email, message, etc.)
- Search boxes: Search inputs with custom styling
- Text areas: Multi-line text input with custom appearance
- Specialized inputs: Custom styled inputs that standard TextFields can't provide
See the complete example in example/lib/api/naked_textfield.0.dart.
Basic implementation
import 'package:flutter/material.dart';
import 'package:naked_ui/naked_ui.dart';
class HeadlessField extends StatefulWidget {
const HeadlessField({super.key});
@override
State<HeadlessField> createState() => _HeadlessFieldState();
}
class _HeadlessFieldState extends State<HeadlessField> {
final controller = TextEditingController();
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return NakedTextField(
controller: controller,
onChanged: (value) => debugPrint('Value: $value'),
builder: (context, state, editable) {
final borderColor = state.when(
focused: Colors.blue,
hovered: Colors.grey.shade400,
orElse: Colors.grey.shade300,
);
final borderWidth = state.isFocused ? 2 : 1;
return AnimatedContainer(
duration: const Duration(milliseconds: 160),
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
border: Border.all(
color: borderColor,
width: borderWidth,
),
boxShadow: state.isFocused
? [
BoxShadow(
color: Colors.blue.withOpacity(0.12),
blurRadius: 12,
offset: const Offset(0, 4),
),
]
: null,
),
child: editable,
);
},
);
}
}
Builder Contract
NakedTextFieldBuilder has the signature:
typedef NakedTextFieldBuilder = Widget Function(
BuildContext context,
NakedTextFieldState state,
Widget editableText,
);
The provided editableText widget is an EditableText configured with all parameters from NakedTextField. The state parameter gives you access to interaction states like state.isFocused, state.isHovered, and state.when() for conditional styling. Wrap the editable text with decoration, icons, paddings, or animations—just return the composed widget.
Constructor Highlights
const NakedTextField({
Key? key,
this.groupId = EditableText,
this.controller,
this.focusNode,
this.undoController,
this.keyboardType,
this.textInputAction,
this.textCapitalization = TextCapitalization.none,
this.textAlign = TextAlign.start,
this.textDirection,
this.readOnly = false,
this.showCursor,
this.autofocus = false,
this.obscuringCharacter = '•',
this.obscureText = false,
this.autocorrect = true,
SmartDashesType? smartDashesType,
SmartQuotesType? smartQuotesType,
this.enableSuggestions = true,
this.maxLines = 1,
this.minLines,
this.expands = false,
this.maxLength,
this.maxLengthEnforcement,
this.onChanged,
this.onEditingComplete,
this.onSubmitted,
this.onAppPrivateCommand,
this.inputFormatters,
this.enabled = true,
this.cursorWidth = 2.0,
this.cursorHeight,
this.cursorRadius,
this.cursorOpacityAnimates,
this.cursorColor,
this.selectionHeightStyle = ui.BoxHeightStyle.tight,
this.selectionWidthStyle = ui.BoxWidthStyle.tight,
this.keyboardAppearance,
this.scrollPadding = const EdgeInsets.all(20.0),
this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection = true,
this.selectionControls,
this.onTap,
this.onTapAlwaysCalled = false,
this.onTapChange,
this.onTapOutside,
this.scrollController,
this.scrollPhysics,
this.autofillHints = const <String>[],
this.contentInsertionConfiguration,
this.clipBehavior = Clip.hardEdge,
this.restorationId,
this.onTapUpOutside,
this.stylusHandwritingEnabled = true,
this.enableIMEPersonalizedLearning = true,
this.contextMenuBuilder,
this.canRequestFocus = true,
this.spellCheckConfiguration,
this.magnifierConfiguration,
this.onHoverChange,
this.onFocusChange,
this.onPressChange,
this.style,
required this.builder,
this.ignorePointers,
this.semanticLabel,
this.semanticHint,
this.strutStyle,
})
Parameters mirror
EditableText, so you can keep using the same configuration options you know from Flutter’s built-in text fields.
Styling & Pointer Control
stylelets you override the text style without wrapping the builder output in a newDefaultTextStyleignorePointerstemporarily disables pointer input while leaving focus/keyboard flow intact—handy when showing loading states around the same editable instancegroupIdmatchesEditableText’s grouping behaviour (set it when coordinating multiple fields for the same input method)
Event Hooks
onTap,onTapChange,onTapOutside,onTapUpOutside→ fine-grained gesture hooksonChanged,onSubmitted,onEditingComplete→ forward the underlyingEditableTextcallbacksonHoverChange,onFocusChange,onPressChange→ interaction callbacks
Accessibility & Semantics
- Supply
semanticLabel/semanticHintwhen the visual chrome lacks textual context NakedTextFieldwraps the builder result with properSemanticsfor focusable text fields- Respect contrast and focus indicators in your builder to ensure the field remains discoverable
Tips
- For Material/Cupertino visual parity, wrap the builder output with your design system components
- Use
maxLines,minLines, andexpandsto create multi-line editors - Provide an
UndoHistoryControllerwhen you need cross-field undo stacks - Remember to dispose any controllers you allocate alongside the widget