---
title: NakedTextField
description: Headless text input component with native editing behavior
keywords:
  - flutter
  - textfield
  - editabletext
  - headless
  - input
---
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.page/btwld/naked_ui/llms.txt
> Use this file to discover all available pages before exploring further.

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

<Info>
  See the complete example in [`packages/example/lib/api/naked_textfield.0.dart`](https://github.com/btwld/naked_ui/blob/main/packages/example/lib/api/naked_textfield.0.dart).
</Info>

## Basic implementation

```dart
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:

```dart
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

```dart
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.error = false,
  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.semanticErrorText,
  this.strutStyle,
  this.excludeSemantics = false,
})
```

> Parameters mirror `EditableText`, so you can keep using the same configuration options you know from Flutter’s built-in text fields.

### Styling & Pointer Control

- `style` lets you override the text style without wrapping the builder output in a new `DefaultTextStyle`
- `ignorePointers` temporarily disables pointer input while leaving focus/keyboard flow intact—handy when showing loading states around the same editable instance
- `groupId` matches `EditableText`’s grouping behaviour (set it when coordinating multiple fields for the same input method)

## Event Hooks

- `onTap`, `onTapChange`, `onTapOutside`, `onTapUpOutside` → fine-grained gesture hooks
- `onChanged`, `onSubmitted`, `onEditingComplete` → forward the underlying `EditableText` callbacks
- `onHoverChange`, `onFocusChange`, `onPressChange` → interaction callbacks

## Accessibility & Semantics

- Supply `semanticLabel` and `semanticHint` when the visual chrome lacks textual context
- Pair `semanticErrorText` with `error: true`; when `error` is false, the error text is not announced and the field is not a live region
- Set `excludeSemantics` to hide the field and its descendants from accessibility services
- `NakedTextField` wraps the builder result with proper `Semantics` for focusable text fields
- Respect contrast and focus indicators in your builder to ensure the field remains discoverable

For a validation error, derive both parameters from the same state:

```dart
NakedTextField(
  controller: emailController,
  error: emailError != null,
  semanticLabel: 'Email',
  semanticErrorText: emailError,
  builder: (context, state, editableText) => editableText,
);
```

## Tips

- For Material/Cupertino visual parity, wrap the builder output with your design system components
- Use `maxLines`, `minLines`, and `expands` to create multi-line editors
- Provide an `UndoHistoryController` when you need cross-field undo stacks
- Remember to dispose any controllers you allocate alongside the widget
