Skip to content

TextArea

A multi-line editable text widget. Pair with a TextEditingController to read/drive the text; newlines live in the text like any character.

See also: TextInput for a single line.

Learn more: Forms & validation

Import: package:fleury/fleury.dart, or package:fleury/fleury_core.dart in browser code.

final controller = TextEditingController(text: releaseNotes);
TextArea(
controller: controller,
minLines: 4,
maxLines: 4,
semanticLabel: 'Release notes',
keymap: TextEditingKeymap.chat,
onChanged: (text) => updateReleaseNotes(text),
onSubmit: (text) => saveReleaseNotes(text),
)

While focused, the area takes typed characters. With the default keymap (TextEditingKeymap.defaultMultiline):

  • Enter inserts a newline.
  • The arrow keys move the caret, Up and Down between lines; they never pass on at the edges of the text. Ctrl+Left and Ctrl+Right (or Alt+Left and Alt+Right) move by word, Home and End jump to the start and end of the line, and Ctrl+Home and Ctrl+End to the start and end of the text. Add Shift to any of these to extend the selection.
  • Backspace, Delete, Ctrl+A, Ctrl+C, Ctrl+X, Ctrl+Z, Ctrl+Y, and their variants work as they do in TextInput. Ctrl+C and Ctrl+X pass on when nothing is selected, and the area consumes Ctrl+Z even with nothing to undo, so a terminal app doesn’t suspend on it while the area has focus.
  • Tab passes on, so it can move focus. Escape calls onEscape, or passes on when onEscape is null.

For a chat composer or prompt, TextEditingKeymap.chat makes Enter call onSubmit and Alt+Enter (or Shift+Enter, where the terminal reports it) insert a newline. TextEditingKeymap.emacsMultiline adds Emacs-style keys: among others, Ctrl+A and Ctrl+E jump to the start and end of the line, Ctrl+K cuts to its end, and Ctrl+Y pastes what was cut.

Newlines are the only control rune a TextArea keeps. Everything else — \r, \t, ESC and the whole escape sequence behind it — is replaced when the text enters the model, so a row’s characters and its painted cells stay the same index space. See TextEditingController for the rules and what an app reads back.

ParameterTypeDefaultDescription
controller:TextEditingController?—External editing state. When null, the widget owns an internal controller.
focusNode:FocusNode?—External focus state. When null, the widget owns an internal focus node.
autofocus:boolfalseWhether to request focus when the widget first mounts.
onChanged:void Function(String text)?—Called with the accepted text after an editing interaction changes it. More
onEscape:void Function()?—Called when the user presses Escape; bubbles if null.
onSubmit:void Function(String value)?—Called with the current text when the keymap resolves a submit action (e.g. Enter under TextEditingKeymap.chat); bubbles if null. The default TextEditingKeymap.defaultMultiline never emits submit, so this only fires under a submit-oriented keymap.
placeholder:String''Hint text shown while the area is empty. May contain newlines.
placeholderStyle:CellStyleconst CellStyle(dim: true)Style for the placeholder text. Defaults to dim.
style:CellStyleCellStyle.noneStyle applied to editable text and its interactive states. More
cursorStyle:CellStyleconst CellStyle(inverse: true)Style applied to the grapheme cell under the visible cursor.
enabled:booltrueWhether the area can receive focus and editing input.
readOnly:boolfalseWhether the area refuses edits while still allowing focus, caret movement, selection, and copy.
obscureText:boolfalseMask every UTF-16 code unit except line breaks. Selection offsets remain aligned with the editor’s real value. While masked, semantics and copy/cut/kill-ring capture cannot expose the value. An explicit disabled clipboard policy stays disabled while masking is on. Set clipboardPolicy to redacted explicitly when a Show control should retain that policy after turning masking off.
validationError:String?—Validation message attached directly to this area, or null when valid. More
semanticLabel:String?—Label exposed through the semantic app graph. More
semanticState:SemanticStateSemanticState.emptyExtra semantic state merged into the text-area node.
clipboardPolicy:TextClipboardPolicyTextClipboardPolicy.allowedHow copy, cut, and the kill ring treat this area’s text. While obscureText is on, any policy other than TextClipboardPolicy.disabled acts as TextClipboardPolicy.redacted.
keymap:TextEditingKeymapTextEditingKeymap.defaultMultilineWhich keys trigger which editing actions. Defaults to TextEditingKeymap.defaultMultiline; TextEditingKeymap.chat makes Enter submit, and TextEditingKeymap.emacsMultiline adds Emacs-style keys such as Ctrl+A, Ctrl+E, and Ctrl+K.
pastePolicy:TextPastePolicyconst TextPastePolicy()Policy for chunking large bracketed paste payloads.
minLines:int1Auto-grow floor: the area is at least this many rows tall. Default 1.
maxLines:int?—Auto-grow cap. When non-null, the area’s height tracks its content between minLines and maxLines rows — a composer that grows with the draft — and a bounded parent caps it further. When null (default), height is unchanged: it fills a bounded parent, otherwise sizes to its content.
  • onChanged: Like TextInput.onChanged, reports user and semantic edits. Programmatic controller writes notify controller listeners instead.
  • style: Pass a plain CellStyle for the common case. Use CellStyle.interactive only when focus, hover, disabled, or invalid should look different locally.
  • validationError: Use this when the area is not inside a FormField. An enclosing FormField supplies its current error automatically.
  • semanticLabel: When omitted, placeholder is used when non-empty. Use this when the placeholder is example text rather than the durable field name.

TextArea is defined in packages/fleury/lib/src/widgets/text_area.dart.

Category: Inputs & controls · All widgets