1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// @docImport 'input_border.dart';
6/// @docImport 'material.dart';
7/// @docImport 'scaffold.dart';
8/// @docImport 'text_form_field.dart';
9/// @docImport 'text_theme.dart';
10library;
11
12import 'dart:ui' as ui show BoxHeightStyle, BoxWidthStyle;
13
14import 'package:flutter/cupertino.dart';
15import 'package:flutter/foundation.dart';
16import 'package:flutter/gestures.dart';
17import 'package:flutter/rendering.dart';
18import 'package:flutter/services.dart';
19
20import 'adaptive_text_selection_toolbar.dart';
21import 'color_scheme.dart';
22import 'colors.dart';
23import 'debug.dart';
24import 'desktop_text_selection.dart';
25import 'input_decorator.dart';
26import 'magnifier.dart';
27import 'material_localizations.dart';
28import 'material_state.dart';
29import 'selectable_text.dart' show iOSHorizontalOffset;
30import 'spell_check_suggestions_toolbar.dart';
31import 'text_selection.dart';
32import 'theme.dart';
33
34export 'package:flutter/services.dart'
35 show SmartDashesType, SmartQuotesType, TextCapitalization, TextInputAction, TextInputType;
36
37// Examples can assume:
38// late BuildContext context;
39// late FocusNode myFocusNode;
40
41/// Signature for the [TextField.buildCounter] callback.
42typedef InputCounterWidgetBuilder =
43 Widget? Function(
44 /// The build context for the TextField.
45 BuildContext context, {
46
47 /// The length of the string currently in the input.
48 required int currentLength,
49
50 /// The maximum string length that can be entered into the TextField.
51 required int? maxLength,
52
53 /// Whether or not the TextField is currently focused. Mainly provided for
54 /// the [liveRegion] parameter in the [Semantics] widget for accessibility.
55 required bool isFocused,
56 });
57
58class _TextFieldSelectionGestureDetectorBuilder extends TextSelectionGestureDetectorBuilder {
59 _TextFieldSelectionGestureDetectorBuilder({required _TextFieldState state})
60 : _state = state,
61 super(delegate: state);
62
63 final _TextFieldState _state;
64
65 @override
66 bool get onUserTapAlwaysCalled => _state.widget.onTapAlwaysCalled;
67
68 @override
69 void onUserTap() {
70 _state.widget.onTap?.call();
71 }
72}
73
74/// A Material Design text field.
75///
76/// A text field lets the user enter text, either with hardware keyboard or with
77/// an onscreen keyboard.
78///
79/// The text field calls the [onChanged] callback whenever the user changes the
80/// text in the field. If the user indicates that they are done typing in the
81/// field (e.g., by pressing a button on the soft keyboard), the text field
82/// calls the [onSubmitted] callback.
83///
84/// To control the text that is displayed in the text field, use the
85/// [controller]. For example, to set the initial value of the text field, use
86/// a [controller] that already contains some text. The [controller] can also
87/// control the selection and composing region (and to observe changes to the
88/// text, selection, and composing region).
89///
90/// By default, a text field has a [decoration] that draws a divider below the
91/// text field. You can use the [decoration] property to control the decoration,
92/// for example by adding a label or an icon. If you set the [decoration]
93/// property to null, the decoration will be removed entirely, including the
94/// extra padding introduced by the decoration to save space for the labels.
95///
96/// If [decoration] is non-null (which is the default), the text field requires
97/// one of its ancestors to be a [Material] widget.
98///
99/// To integrate the [TextField] into a [Form] with other [FormField] widgets,
100/// consider using [TextFormField].
101///
102/// {@template flutter.material.textfield.wantKeepAlive}
103/// When the widget has focus, it will prevent itself from disposing via its
104/// underlying [EditableText]'s [AutomaticKeepAliveClientMixin.wantKeepAlive] in
105/// order to avoid losing the selection. Removing the focus will allow it to be
106/// disposed.
107/// {@endtemplate}
108///
109/// Remember to call [TextEditingController.dispose] on the [TextEditingController]
110/// when it is no longer needed. This will ensure we discard any resources used
111/// by the object.
112///
113/// If this field is part of a scrolling container that lazily constructs its
114/// children, like a [ListView] or a [CustomScrollView], then a [controller]
115/// should be specified. The controller's lifetime should be managed by a
116/// stateful widget ancestor of the scrolling container.
117///
118/// ## Obscured Input
119///
120/// {@tool dartpad}
121/// This example shows how to create a [TextField] that will obscure input. The
122/// [InputDecoration] surrounds the field in a border using [OutlineInputBorder]
123/// and adds a label.
124///
125/// ** See code in examples/api/lib/material/text_field/text_field.0.dart **
126/// {@end-tool}
127///
128/// ## Reading values
129///
130/// A common way to read a value from a TextField is to use the [onSubmitted]
131/// callback. This callback is applied to the text field's current value when
132/// the user finishes editing.
133///
134/// {@tool dartpad}
135/// This sample shows how to get a value from a TextField via the [onSubmitted]
136/// callback.
137///
138/// ** See code in examples/api/lib/material/text_field/text_field.1.dart **
139/// {@end-tool}
140///
141/// {@macro flutter.widgets.EditableText.lifeCycle}
142///
143/// For most applications the [onSubmitted] callback will be sufficient for
144/// reacting to user input.
145///
146/// The [onEditingComplete] callback also runs when the user finishes editing.
147/// It's different from [onSubmitted] because it has a default value which
148/// updates the text controller and yields the keyboard focus. Applications that
149/// require different behavior can override the default [onEditingComplete]
150/// callback.
151///
152/// Keep in mind you can also always read the current string from a TextField's
153/// [TextEditingController] using [TextEditingController.text].
154///
155/// ## Handling emojis and other complex characters
156/// {@macro flutter.widgets.EditableText.onChanged}
157///
158/// In the live Dartpad example above, try typing the emoji 👨‍👩‍👦
159/// into the field and submitting. Because the example code measures the length
160/// with `value.characters.length`, the emoji is correctly counted as a single
161/// character.
162///
163/// {@macro flutter.widgets.editableText.showCaretOnScreen}
164///
165/// {@macro flutter.widgets.editableText.accessibility}
166///
167/// {@tool dartpad}
168/// This sample shows how to style a text field to match a filled or outlined
169/// Material Design 3 text field.
170///
171/// ** See code in examples/api/lib/material/text_field/text_field.2.dart **
172/// {@end-tool}
173///
174/// ## Scrolling Considerations
175///
176/// If this [TextField] is not a descendant of [Scaffold] and is being used
177/// within a [Scrollable] or nested [Scrollable]s, consider placing a
178/// [ScrollNotificationObserver] above the root [Scrollable] that contains this
179/// [TextField] to ensure proper scroll coordination for [TextField] and its
180/// components like [TextSelectionOverlay].
181///
182/// See also:
183///
184/// * [TextFormField], which integrates with the [Form] widget.
185/// * [InputDecorator], which shows the labels and other visual elements that
186/// surround the actual text editing widget.
187/// * [EditableText], which is the raw text editing control at the heart of a
188/// [TextField]. The [EditableText] widget is rarely used directly unless
189/// you are implementing an entirely different design language, such as
190/// Cupertino.
191/// * <https://material.io/design/components/text-fields.html>
192/// * Cookbook: [Create and style a text field](https://docs.flutter.dev/cookbook/forms/text-input)
193/// * Cookbook: [Handle changes to a text field](https://docs.flutter.dev/cookbook/forms/text-field-changes)
194/// * Cookbook: [Retrieve the value of a text field](https://docs.flutter.dev/cookbook/forms/retrieve-input)
195/// * Cookbook: [Focus and text fields](https://docs.flutter.dev/cookbook/forms/focus)
196class TextField extends StatefulWidget {
197 /// Creates a Material Design text field.
198 ///
199 /// If [decoration] is non-null (which is the default), the text field requires
200 /// one of its ancestors to be a [Material] widget.
201 ///
202 /// To remove the decoration entirely (including the extra padding introduced
203 /// by the decoration to save space for the labels), set the [decoration] to
204 /// null.
205 ///
206 /// The [maxLines] property can be set to null to remove the restriction on
207 /// the number of lines. By default, it is one, meaning this is a single-line
208 /// text field. [maxLines] must not be zero.
209 ///
210 /// The [maxLength] property is set to null by default, which means the
211 /// number of characters allowed in the text field is not restricted. If
212 /// [maxLength] is set a character counter will be displayed below the
213 /// field showing how many characters have been entered. If the value is
214 /// set to a positive integer it will also display the maximum allowed
215 /// number of characters to be entered. If the value is set to
216 /// [TextField.noMaxLength] then only the current length is displayed.
217 ///
218 /// After [maxLength] characters have been input, additional input
219 /// is ignored, unless [maxLengthEnforcement] is set to
220 /// [MaxLengthEnforcement.none].
221 /// The text field enforces the length with a [LengthLimitingTextInputFormatter],
222 /// which is evaluated after the supplied [inputFormatters], if any.
223 /// The [maxLength] value must be either null or greater than zero.
224 ///
225 /// If [maxLengthEnforcement] is set to [MaxLengthEnforcement.none], then more
226 /// than [maxLength] characters may be entered, and the error counter and
227 /// divider will switch to the [decoration].errorStyle when the limit is
228 /// exceeded.
229 ///
230 /// The text cursor is not shown if [showCursor] is false or if [showCursor]
231 /// is null (the default) and [readOnly] is true.
232 ///
233 /// The [selectionHeightStyle] and [selectionWidthStyle] properties allow
234 /// changing the shape of the selection highlighting. These properties default
235 /// to [ui.BoxHeightStyle.tight] and [ui.BoxWidthStyle.tight], respectively.
236 ///
237 /// See also:
238 ///
239 /// * [maxLength], which discusses the precise meaning of "number of
240 /// characters" and how it may differ from the intuitive meaning.
241 const TextField({
242 super.key,
243 this.groupId = EditableText,
244 this.controller,
245 this.focusNode,
246 this.undoController,
247 this.decoration = const InputDecoration(),
248 TextInputType? keyboardType,
249 this.textInputAction,
250 this.textCapitalization = TextCapitalization.none,
251 this.style,
252 this.strutStyle,
253 this.textAlign = TextAlign.start,
254 this.textAlignVertical,
255 this.textDirection,
256 this.readOnly = false,
257 @Deprecated(
258 'Use `contextMenuBuilder` instead. '
259 'This feature was deprecated after v3.3.0-0.5.pre.',
260 )
261 this.toolbarOptions,
262 this.showCursor,
263 this.autofocus = false,
264 this.statesController,
265 this.obscuringCharacter = '•',
266 this.obscureText = false,
267 this.autocorrect = true,
268 SmartDashesType? smartDashesType,
269 SmartQuotesType? smartQuotesType,
270 this.enableSuggestions = true,
271 this.maxLines = 1,
272 this.minLines,
273 this.expands = false,
274 this.maxLength,
275 this.maxLengthEnforcement,
276 this.onChanged,
277 this.onEditingComplete,
278 this.onSubmitted,
279 this.onAppPrivateCommand,
280 this.inputFormatters,
281 this.enabled,
282 this.ignorePointers,
283 this.cursorWidth = 2.0,
284 this.cursorHeight,
285 this.cursorRadius,
286 this.cursorOpacityAnimates,
287 this.cursorColor,
288 this.cursorErrorColor,
289 this.selectionHeightStyle = ui.BoxHeightStyle.tight,
290 this.selectionWidthStyle = ui.BoxWidthStyle.tight,
291 this.keyboardAppearance,
292 this.scrollPadding = const EdgeInsets.all(20.0),
293 this.dragStartBehavior = DragStartBehavior.start,
294 bool? enableInteractiveSelection,
295 this.selectionControls,
296 this.onTap,
297 this.onTapAlwaysCalled = false,
298 this.onTapOutside,
299 this.onTapUpOutside,
300 this.mouseCursor,
301 this.buildCounter,
302 this.scrollController,
303 this.scrollPhysics,
304 this.autofillHints = const <String>[],
305 this.contentInsertionConfiguration,
306 this.clipBehavior = Clip.hardEdge,
307 this.restorationId,
308 @Deprecated(
309 'Use `stylusHandwritingEnabled` instead. '
310 'This feature was deprecated after v3.27.0-0.2.pre.',
311 )
312 this.scribbleEnabled = true,
313 this.stylusHandwritingEnabled = EditableText.defaultStylusHandwritingEnabled,
314 this.enableIMEPersonalizedLearning = true,
315 this.contextMenuBuilder = _defaultContextMenuBuilder,
316 this.canRequestFocus = true,
317 this.spellCheckConfiguration,
318 this.magnifierConfiguration,
319 }) : assert(obscuringCharacter.length == 1),
320 smartDashesType =
321 smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled),
322 smartQuotesType =
323 smartQuotesType ?? (obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled),
324 assert(maxLines == null || maxLines > 0),
325 assert(minLines == null || minLines > 0),
326 assert(
327 (maxLines == null) || (minLines == null) || (maxLines >= minLines),
328 "minLines can't be greater than maxLines",
329 ),
330 assert(
331 !expands || (maxLines == null && minLines == null),
332 'minLines and maxLines must be null when expands is true.',
333 ),
334 assert(!obscureText || maxLines == 1, 'Obscured fields cannot be multiline.'),
335 assert(maxLength == null || maxLength == TextField.noMaxLength || maxLength > 0),
336 // Assert the following instead of setting it directly to avoid surprising the user by silently changing the value they set.
337 assert(
338 !identical(textInputAction, TextInputAction.newline) ||
339 maxLines == 1 ||
340 !identical(keyboardType, TextInputType.text),
341 'Use keyboardType TextInputType.multiline when using TextInputAction.newline on a multiline TextField.',
342 ),
343 keyboardType =
344 keyboardType ?? (maxLines == 1 ? TextInputType.text : TextInputType.multiline),
345 enableInteractiveSelection = enableInteractiveSelection ?? (!readOnly || !obscureText);
346
347 /// The configuration for the magnifier of this text field.
348 ///
349 /// By default, builds a [CupertinoTextMagnifier] on iOS and [TextMagnifier]
350 /// on Android, and builds nothing on all other platforms. To suppress the
351 /// magnifier, consider passing [TextMagnifierConfiguration.disabled].
352 ///
353 /// {@macro flutter.widgets.magnifier.intro}
354 ///
355 /// {@tool dartpad}
356 /// This sample demonstrates how to customize the magnifier that this text field uses.
357 ///
358 /// ** See code in examples/api/lib/widgets/text_magnifier/text_magnifier.0.dart **
359 /// {@end-tool}
360 final TextMagnifierConfiguration? magnifierConfiguration;
361
362 /// {@macro flutter.widgets.editableText.groupId}
363 final Object groupId;
364
365 /// Controls the text being edited.
366 ///
367 /// If null, this widget will create its own [TextEditingController].
368 final TextEditingController? controller;
369
370 /// Defines the keyboard focus for this widget.
371 ///
372 /// The [focusNode] is a long-lived object that's typically managed by a
373 /// [StatefulWidget] parent. See [FocusNode] for more information.
374 ///
375 /// To give the keyboard focus to this widget, provide a [focusNode] and then
376 /// use the current [FocusScope] to request the focus:
377 ///
378 /// ```dart
379 /// FocusScope.of(context).requestFocus(myFocusNode);
380 /// ```
381 ///
382 /// This happens automatically when the widget is tapped.
383 ///
384 /// To be notified when the widget gains or loses the focus, add a listener
385 /// to the [focusNode]:
386 ///
387 /// ```dart
388 /// myFocusNode.addListener(() { print(myFocusNode.hasFocus); });
389 /// ```
390 ///
391 /// If null, this widget will create its own [FocusNode].
392 ///
393 /// ## Keyboard
394 ///
395 /// Requesting the focus will typically cause the keyboard to be shown
396 /// if it's not showing already.
397 ///
398 /// On Android, the user can hide the keyboard - without changing the focus -
399 /// with the system back button. They can restore the keyboard's visibility
400 /// by tapping on a text field. The user might hide the keyboard and
401 /// switch to a physical keyboard, or they might just need to get it
402 /// out of the way for a moment, to expose something it's
403 /// obscuring. In this case requesting the focus again will not
404 /// cause the focus to change, and will not make the keyboard visible.
405 ///
406 /// This widget builds an [EditableText] and will ensure that the keyboard is
407 /// showing when it is tapped by calling [EditableTextState.requestKeyboard()].
408 final FocusNode? focusNode;
409
410 /// The decoration to show around the text field.
411 ///
412 /// By default, draws a horizontal line under the text field but can be
413 /// configured to show an icon, label, hint text, and error text.
414 ///
415 /// Specify null to remove the decoration entirely (including the
416 /// extra padding introduced by the decoration to save space for the labels).
417 final InputDecoration? decoration;
418
419 /// {@macro flutter.widgets.editableText.keyboardType}
420 final TextInputType keyboardType;
421
422 /// {@template flutter.widgets.TextField.textInputAction}
423 /// The type of action button to use for the keyboard.
424 ///
425 /// Defaults to [TextInputAction.newline] if [keyboardType] is
426 /// [TextInputType.multiline] and [TextInputAction.done] otherwise.
427 /// {@endtemplate}
428 final TextInputAction? textInputAction;
429
430 /// {@macro flutter.widgets.editableText.textCapitalization}
431 final TextCapitalization textCapitalization;
432
433 /// The style to use for the text being edited.
434 ///
435 /// This text style is also used as the base style for the [decoration].
436 ///
437 /// If null, [TextTheme.bodyLarge] will be used. When the text field is disabled,
438 /// [TextTheme.bodyLarge] with an opacity of 0.38 will be used instead.
439 ///
440 /// If null and [ThemeData.useMaterial3] is false, [TextTheme.titleMedium] will
441 /// be used. When the text field is disabled, [TextTheme.titleMedium] with
442 /// [ThemeData.disabledColor] will be used instead.
443 final TextStyle? style;
444
445 /// {@macro flutter.widgets.editableText.strutStyle}
446 final StrutStyle? strutStyle;
447
448 /// {@macro flutter.widgets.editableText.textAlign}
449 final TextAlign textAlign;
450
451 /// {@macro flutter.material.InputDecorator.textAlignVertical}
452 final TextAlignVertical? textAlignVertical;
453
454 /// {@macro flutter.widgets.editableText.textDirection}
455 final TextDirection? textDirection;
456
457 /// {@macro flutter.widgets.editableText.autofocus}
458 final bool autofocus;
459
460 /// Represents the interactive "state" of this widget in terms of a set of
461 /// [WidgetState]s, including [WidgetState.disabled], [WidgetState.hovered],
462 /// [WidgetState.error], and [WidgetState.focused].
463 ///
464 /// Classes based on this one can provide their own
465 /// [WidgetStatesController] to which they've added listeners.
466 /// They can also update the controller's [WidgetStatesController.value]
467 /// however, this may only be done when it's safe to call
468 /// [State.setState], like in an event handler.
469 ///
470 /// The controller's [WidgetStatesController.value] represents the set of
471 /// states that a widget's visual properties, typically [WidgetStateProperty]
472 /// values, are resolved against. It is _not_ the intrinsic state of the widget.
473 /// The widget is responsible for ensuring that the controller's
474 /// [WidgetStatesController.value] tracks its intrinsic state. For example
475 /// one cannot request the keyboard focus for a widget by adding [WidgetState.focused]
476 /// to its controller. When the widget gains the or loses the focus it will
477 /// [WidgetStatesController.update] its controller's [WidgetStatesController.value]
478 /// and notify listeners of the change.
479 final MaterialStatesController? statesController;
480
481 /// {@macro flutter.widgets.editableText.obscuringCharacter}
482 final String obscuringCharacter;
483
484 /// {@macro flutter.widgets.editableText.obscureText}
485 final bool obscureText;
486
487 /// {@macro flutter.widgets.editableText.autocorrect}
488 final bool autocorrect;
489
490 /// {@macro flutter.services.TextInputConfiguration.smartDashesType}
491 final SmartDashesType smartDashesType;
492
493 /// {@macro flutter.services.TextInputConfiguration.smartQuotesType}
494 final SmartQuotesType smartQuotesType;
495
496 /// {@macro flutter.services.TextInputConfiguration.enableSuggestions}
497 final bool enableSuggestions;
498
499 /// {@macro flutter.widgets.editableText.maxLines}
500 /// * [expands], which determines whether the field should fill the height of
501 /// its parent.
502 final int? maxLines;
503
504 /// {@macro flutter.widgets.editableText.minLines}
505 /// * [expands], which determines whether the field should fill the height of
506 /// its parent.
507 final int? minLines;
508
509 /// {@macro flutter.widgets.editableText.expands}
510 final bool expands;
511
512 /// {@macro flutter.widgets.editableText.readOnly}
513 final bool readOnly;
514
515 /// Configuration of toolbar options.
516 ///
517 /// If not set, select all and paste will default to be enabled. Copy and cut
518 /// will be disabled if [obscureText] is true. If [readOnly] is true,
519 /// paste and cut will be disabled regardless.
520 @Deprecated(
521 'Use `contextMenuBuilder` instead. '
522 'This feature was deprecated after v3.3.0-0.5.pre.',
523 )
524 final ToolbarOptions? toolbarOptions;
525
526 /// {@macro flutter.widgets.editableText.showCursor}
527 final bool? showCursor;
528
529 /// If [maxLength] is set to this value, only the "current input length"
530 /// part of the character counter is shown.
531 static const int noMaxLength = -1;
532
533 /// The maximum number of characters (Unicode grapheme clusters) to allow in
534 /// the text field.
535 ///
536 /// If set, a character counter will be displayed below the
537 /// field showing how many characters have been entered. If set to a number
538 /// greater than 0, it will also display the maximum number allowed. If set
539 /// to [TextField.noMaxLength] then only the current character count is displayed.
540 ///
541 /// After [maxLength] characters have been input, additional input
542 /// is ignored, unless [maxLengthEnforcement] is set to
543 /// [MaxLengthEnforcement.none].
544 ///
545 /// The text field enforces the length with a [LengthLimitingTextInputFormatter],
546 /// which is evaluated after the supplied [inputFormatters], if any.
547 ///
548 /// This value must be either null, [TextField.noMaxLength], or greater than 0.
549 /// If null (the default) then there is no limit to the number of characters
550 /// that can be entered. If set to [TextField.noMaxLength], then no limit will
551 /// be enforced, but the number of characters entered will still be displayed.
552 ///
553 /// Whitespace characters (e.g. newline, space, tab) are included in the
554 /// character count.
555 ///
556 /// If [maxLengthEnforcement] is [MaxLengthEnforcement.none], then more than
557 /// [maxLength] characters may be entered, but the error counter and divider
558 /// will switch to the [decoration]'s [InputDecoration.errorStyle] when the
559 /// limit is exceeded.
560 ///
561 /// {@macro flutter.services.lengthLimitingTextInputFormatter.maxLength}
562 final int? maxLength;
563
564 /// Determines how the [maxLength] limit should be enforced.
565 ///
566 /// {@macro flutter.services.textFormatter.effectiveMaxLengthEnforcement}
567 ///
568 /// {@macro flutter.services.textFormatter.maxLengthEnforcement}
569 final MaxLengthEnforcement? maxLengthEnforcement;
570
571 /// {@macro flutter.widgets.editableText.onChanged}
572 ///
573 /// See also:
574 ///
575 /// * [inputFormatters], which are called before [onChanged]
576 /// runs and can validate and change ("format") the input value.
577 /// * [onEditingComplete], [onSubmitted]:
578 /// which are more specialized input change notifications.
579 final ValueChanged<String>? onChanged;
580
581 /// {@macro flutter.widgets.editableText.onEditingComplete}
582 final VoidCallback? onEditingComplete;
583
584 /// {@macro flutter.widgets.editableText.onSubmitted}
585 ///
586 /// See also:
587 ///
588 /// * [TextInputAction.next] and [TextInputAction.previous], which
589 /// automatically shift the focus to the next/previous focusable item when
590 /// the user is done editing.
591 final ValueChanged<String>? onSubmitted;
592
593 /// {@macro flutter.widgets.editableText.onAppPrivateCommand}
594 final AppPrivateCommandCallback? onAppPrivateCommand;
595
596 /// {@macro flutter.widgets.editableText.inputFormatters}
597 final List<TextInputFormatter>? inputFormatters;
598
599 /// If false the text field is "disabled": it ignores taps and its
600 /// [decoration] is rendered in grey.
601 ///
602 /// If non-null this property overrides the [decoration]'s
603 /// [InputDecoration.enabled] property.
604 final bool? enabled;
605
606 /// Determines whether this widget ignores pointer events.
607 ///
608 /// Defaults to null, and when null, does nothing.
609 final bool? ignorePointers;
610
611 /// {@macro flutter.widgets.editableText.cursorWidth}
612 final double cursorWidth;
613
614 /// {@macro flutter.widgets.editableText.cursorHeight}
615 final double? cursorHeight;
616
617 /// {@macro flutter.widgets.editableText.cursorRadius}
618 final Radius? cursorRadius;
619
620 /// {@macro flutter.widgets.editableText.cursorOpacityAnimates}
621 final bool? cursorOpacityAnimates;
622
623 /// The color of the cursor.
624 ///
625 /// The cursor indicates the current location of text insertion point in
626 /// the field.
627 ///
628 /// If this is null it will default to the ambient
629 /// [DefaultSelectionStyle.cursorColor]. If that is null, and the
630 /// [ThemeData.platform] is [TargetPlatform.iOS] or [TargetPlatform.macOS]
631 /// it will use [CupertinoThemeData.primaryColor]. Otherwise it will use
632 /// the value of [ColorScheme.primary] of [ThemeData.colorScheme].
633 final Color? cursorColor;
634
635 /// The color of the cursor when the [InputDecorator] is showing an error.
636 ///
637 /// If this is null it will default to [TextStyle.color] of
638 /// [InputDecoration.errorStyle]. If that is null, it will use
639 /// [ColorScheme.error] of [ThemeData.colorScheme].
640 final Color? cursorErrorColor;
641
642 /// Controls how tall the selection highlight boxes are computed to be.
643 ///
644 /// See [ui.BoxHeightStyle] for details on available styles.
645 final ui.BoxHeightStyle selectionHeightStyle;
646
647 /// Controls how wide the selection highlight boxes are computed to be.
648 ///
649 /// See [ui.BoxWidthStyle] for details on available styles.
650 final ui.BoxWidthStyle selectionWidthStyle;
651
652 /// The appearance of the keyboard.
653 ///
654 /// This setting is only honored on iOS devices.
655 ///
656 /// If unset, defaults to [ThemeData.brightness].
657 final Brightness? keyboardAppearance;
658
659 /// {@macro flutter.widgets.editableText.scrollPadding}
660 final EdgeInsets scrollPadding;
661
662 /// {@macro flutter.widgets.editableText.enableInteractiveSelection}
663 final bool enableInteractiveSelection;
664
665 /// {@macro flutter.widgets.editableText.selectionControls}
666 final TextSelectionControls? selectionControls;
667
668 /// {@macro flutter.widgets.scrollable.dragStartBehavior}
669 final DragStartBehavior dragStartBehavior;
670
671 /// {@macro flutter.widgets.editableText.selectionEnabled}
672 bool get selectionEnabled => enableInteractiveSelection;
673
674 /// {@template flutter.material.textfield.onTap}
675 /// Called for the first tap in a series of taps.
676 ///
677 /// The text field builds a [GestureDetector] to handle input events like tap,
678 /// to trigger focus requests, to move the caret, adjust the selection, etc.
679 /// Handling some of those events by wrapping the text field with a competing
680 /// GestureDetector is problematic.
681 ///
682 /// To unconditionally handle taps, without interfering with the text field's
683 /// internal gesture detector, provide this callback.
684 ///
685 /// If the text field is created with [enabled] false, taps will not be
686 /// recognized.
687 ///
688 /// To be notified when the text field gains or loses the focus, provide a
689 /// [focusNode] and add a listener to that.
690 ///
691 /// To listen to arbitrary pointer events without competing with the
692 /// text field's internal gesture detector, use a [Listener].
693 /// {@endtemplate}
694 ///
695 /// If [onTapAlwaysCalled] is enabled, this will also be called for consecutive
696 /// taps.
697 final GestureTapCallback? onTap;
698
699 /// Whether [onTap] should be called for every tap.
700 ///
701 /// Defaults to false, so [onTap] is only called for each distinct tap. When
702 /// enabled, [onTap] is called for every tap including consecutive taps.
703 final bool onTapAlwaysCalled;
704
705 /// {@macro flutter.widgets.editableText.onTapOutside}
706 ///
707 /// {@tool dartpad}
708 /// This example shows how to use a `TextFieldTapRegion` to wrap a set of
709 /// "spinner" buttons that increment and decrement a value in the [TextField]
710 /// without causing the text field to lose keyboard focus.
711 ///
712 /// This example includes a generic `SpinnerField<T>` class that you can copy
713 /// into your own project and customize.
714 ///
715 /// ** See code in examples/api/lib/widgets/tap_region/text_field_tap_region.0.dart **
716 /// {@end-tool}
717 ///
718 /// See also:
719 ///
720 /// * [TapRegion] for how the region group is determined.
721 final TapRegionCallback? onTapOutside;
722
723 /// {@macro flutter.widgets.editableText.onTapUpOutside}
724 final TapRegionUpCallback? onTapUpOutside;
725
726 /// The cursor for a mouse pointer when it enters or is hovering over the
727 /// widget.
728 ///
729 /// If [mouseCursor] is a [WidgetStateMouseCursor],
730 /// [WidgetStateProperty.resolve] is used for the following [WidgetState]s:
731 ///
732 /// * [WidgetState.error].
733 /// * [WidgetState.hovered].
734 /// * [WidgetState.focused].
735 /// * [WidgetState.disabled].
736 ///
737 /// If this property is null, [WidgetStateMouseCursor.textable] will be used.
738 ///
739 /// The [mouseCursor] is the only property of [TextField] that controls the
740 /// appearance of the mouse pointer. All other properties related to "cursor"
741 /// stand for the text cursor, which is usually a blinking vertical line at
742 /// the editing position.
743 final MouseCursor? mouseCursor;
744
745 /// Callback that generates a custom [InputDecoration.counter] widget.
746 ///
747 /// See [InputCounterWidgetBuilder] for an explanation of the passed in
748 /// arguments. The returned widget will be placed below the line in place of
749 /// the default widget built when [InputDecoration.counterText] is specified.
750 ///
751 /// The returned widget will be wrapped in a [Semantics] widget for
752 /// accessibility, but it also needs to be accessible itself. For example,
753 /// if returning a Text widget, set the [Text.semanticsLabel] property.
754 ///
755 /// {@tool snippet}
756 /// ```dart
757 /// Widget counter(
758 /// BuildContext context,
759 /// {
760 /// required int currentLength,
761 /// required int? maxLength,
762 /// required bool isFocused,
763 /// }
764 /// ) {
765 /// return Text(
766 /// '$currentLength of $maxLength characters',
767 /// semanticsLabel: 'character count',
768 /// );
769 /// }
770 /// ```
771 /// {@end-tool}
772 ///
773 /// If buildCounter returns null, then no counter and no Semantics widget will
774 /// be created at all.
775 final InputCounterWidgetBuilder? buildCounter;
776
777 /// {@macro flutter.widgets.editableText.scrollPhysics}
778 final ScrollPhysics? scrollPhysics;
779
780 /// {@macro flutter.widgets.editableText.scrollController}
781 final ScrollController? scrollController;
782
783 /// {@macro flutter.widgets.editableText.autofillHints}
784 /// {@macro flutter.services.AutofillConfiguration.autofillHints}
785 final Iterable<String>? autofillHints;
786
787 /// {@macro flutter.material.Material.clipBehavior}
788 ///
789 /// Defaults to [Clip.hardEdge].
790 final Clip clipBehavior;
791
792 /// {@template flutter.material.textfield.restorationId}
793 /// Restoration ID to save and restore the state of the text field.
794 ///
795 /// If non-null, the text field will persist and restore its current scroll
796 /// offset and - if no [controller] has been provided - the content of the
797 /// text field. If a [controller] has been provided, it is the responsibility
798 /// of the owner of that controller to persist and restore it, e.g. by using
799 /// a [RestorableTextEditingController].
800 ///
801 /// The state of this widget is persisted in a [RestorationBucket] claimed
802 /// from the surrounding [RestorationScope] using the provided restoration ID.
803 ///
804 /// See also:
805 ///
806 /// * [RestorationManager], which explains how state restoration works in
807 /// Flutter.
808 /// {@endtemplate}
809 final String? restorationId;
810
811 /// {@macro flutter.widgets.editableText.scribbleEnabled}
812 @Deprecated(
813 'Use `stylusHandwritingEnabled` instead. '
814 'This feature was deprecated after v3.27.0-0.2.pre.',
815 )
816 final bool scribbleEnabled;
817
818 /// {@macro flutter.widgets.editableText.stylusHandwritingEnabled}
819 final bool stylusHandwritingEnabled;
820
821 /// {@macro flutter.services.TextInputConfiguration.enableIMEPersonalizedLearning}
822 final bool enableIMEPersonalizedLearning;
823
824 /// {@macro flutter.widgets.editableText.contentInsertionConfiguration}
825 final ContentInsertionConfiguration? contentInsertionConfiguration;
826
827 /// {@macro flutter.widgets.EditableText.contextMenuBuilder}
828 ///
829 /// If not provided, will build a default menu based on the platform.
830 ///
831 /// See also:
832 ///
833 /// * [AdaptiveTextSelectionToolbar], which is built by default.
834 /// * [BrowserContextMenu], which allows the browser's context menu on web to
835 /// be disabled and Flutter-rendered context menus to appear.
836 final EditableTextContextMenuBuilder? contextMenuBuilder;
837
838 /// Determine whether this text field can request the primary focus.
839 ///
840 /// Defaults to true. If false, the text field will not request focus
841 /// when tapped, or when its context menu is displayed. If false it will not
842 /// be possible to move the focus to the text field with tab key.
843 final bool canRequestFocus;
844
845 /// {@macro flutter.widgets.undoHistory.controller}
846 final UndoHistoryController? undoController;
847
848 static Widget _defaultContextMenuBuilder(
849 BuildContext context,
850 EditableTextState editableTextState,
851 ) {
852 if (defaultTargetPlatform == TargetPlatform.iOS && SystemContextMenu.isSupported(context)) {
853 return SystemContextMenu.editableText(editableTextState: editableTextState);
854 }
855 return AdaptiveTextSelectionToolbar.editableText(editableTextState: editableTextState);
856 }
857
858 /// {@macro flutter.widgets.EditableText.spellCheckConfiguration}
859 ///
860 /// If [SpellCheckConfiguration.misspelledTextStyle] is not specified in this
861 /// configuration, then [materialMisspelledTextStyle] is used by default.
862 final SpellCheckConfiguration? spellCheckConfiguration;
863
864 /// The [TextStyle] used to indicate misspelled words in the Material style.
865 ///
866 /// See also:
867 /// * [SpellCheckConfiguration.misspelledTextStyle], the style configured to
868 /// mark misspelled words with.
869 /// * [CupertinoTextField.cupertinoMisspelledTextStyle], the style configured
870 /// to mark misspelled words with in the Cupertino style.
871 static const TextStyle materialMisspelledTextStyle = TextStyle(
872 decoration: TextDecoration.underline,
873 decorationColor: Colors.red,
874 decorationStyle: TextDecorationStyle.wavy,
875 );
876
877 /// Default builder for [TextField]'s spell check suggestions toolbar.
878 ///
879 /// On Apple platforms, builds an iOS-style toolbar. Everywhere else, builds
880 /// an Android-style toolbar.
881 ///
882 /// See also:
883 /// * [spellCheckConfiguration], where this is typically specified for
884 /// [TextField].
885 /// * [SpellCheckConfiguration.spellCheckSuggestionsToolbarBuilder], the
886 /// parameter for which this is the default value for [TextField].
887 /// * [CupertinoTextField.defaultSpellCheckSuggestionsToolbarBuilder], which
888 /// is like this but specifies the default for [CupertinoTextField].
889 @visibleForTesting
890 static Widget defaultSpellCheckSuggestionsToolbarBuilder(
891 BuildContext context,
892 EditableTextState editableTextState,
893 ) {
894 switch (defaultTargetPlatform) {
895 case TargetPlatform.iOS:
896 case TargetPlatform.macOS:
897 return CupertinoSpellCheckSuggestionsToolbar.editableText(
898 editableTextState: editableTextState,
899 );
900 case TargetPlatform.android:
901 case TargetPlatform.fuchsia:
902 case TargetPlatform.linux:
903 case TargetPlatform.windows:
904 return SpellCheckSuggestionsToolbar.editableText(editableTextState: editableTextState);
905 }
906 }
907
908 /// Returns a new [SpellCheckConfiguration] where the given configuration has
909 /// had any missing values replaced with their defaults for the Android
910 /// platform.
911 static SpellCheckConfiguration inferAndroidSpellCheckConfiguration(
912 SpellCheckConfiguration? configuration,
913 ) {
914 if (configuration == null || configuration == const SpellCheckConfiguration.disabled()) {
915 return const SpellCheckConfiguration.disabled();
916 }
917 return configuration.copyWith(
918 misspelledTextStyle:
919 configuration.misspelledTextStyle ?? TextField.materialMisspelledTextStyle,
920 spellCheckSuggestionsToolbarBuilder:
921 configuration.spellCheckSuggestionsToolbarBuilder ??
922 TextField.defaultSpellCheckSuggestionsToolbarBuilder,
923 );
924 }
925
926 @override
927 State<TextField> createState() => _TextFieldState();
928
929 @override
930 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
931 super.debugFillProperties(properties);
932 properties.add(
933 DiagnosticsProperty<TextEditingController>('controller', controller, defaultValue: null),
934 );
935 properties.add(DiagnosticsProperty<FocusNode>('focusNode', focusNode, defaultValue: null));
936 properties.add(
937 DiagnosticsProperty<UndoHistoryController>(
938 'undoController',
939 undoController,
940 defaultValue: null,
941 ),
942 );
943 properties.add(DiagnosticsProperty<bool>('enabled', enabled, defaultValue: null));
944 properties.add(
945 DiagnosticsProperty<InputDecoration>(
946 'decoration',
947 decoration,
948 defaultValue: const InputDecoration(),
949 ),
950 );
951 properties.add(
952 DiagnosticsProperty<TextInputType>(
953 'keyboardType',
954 keyboardType,
955 defaultValue: TextInputType.text,
956 ),
957 );
958 properties.add(DiagnosticsProperty<TextStyle>('style', style, defaultValue: null));
959 properties.add(DiagnosticsProperty<bool>('autofocus', autofocus, defaultValue: false));
960 properties.add(
961 DiagnosticsProperty<String>('obscuringCharacter', obscuringCharacter, defaultValue: '•'),
962 );
963 properties.add(DiagnosticsProperty<bool>('obscureText', obscureText, defaultValue: false));
964 properties.add(DiagnosticsProperty<bool>('autocorrect', autocorrect, defaultValue: true));
965 properties.add(
966 EnumProperty<SmartDashesType>(
967 'smartDashesType',
968 smartDashesType,
969 defaultValue: obscureText ? SmartDashesType.disabled : SmartDashesType.enabled,
970 ),
971 );
972 properties.add(
973 EnumProperty<SmartQuotesType>(
974 'smartQuotesType',
975 smartQuotesType,
976 defaultValue: obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled,
977 ),
978 );
979 properties.add(
980 DiagnosticsProperty<bool>('enableSuggestions', enableSuggestions, defaultValue: true),
981 );
982 properties.add(IntProperty('maxLines', maxLines, defaultValue: 1));
983 properties.add(IntProperty('minLines', minLines, defaultValue: null));
984 properties.add(DiagnosticsProperty<bool>('expands', expands, defaultValue: false));
985 properties.add(IntProperty('maxLength', maxLength, defaultValue: null));
986 properties.add(
987 EnumProperty<MaxLengthEnforcement>(
988 'maxLengthEnforcement',
989 maxLengthEnforcement,
990 defaultValue: null,
991 ),
992 );
993 properties.add(
994 EnumProperty<TextInputAction>('textInputAction', textInputAction, defaultValue: null),
995 );
996 properties.add(
997 EnumProperty<TextCapitalization>(
998 'textCapitalization',
999 textCapitalization,
1000 defaultValue: TextCapitalization.none,
1001 ),
1002 );
1003 properties.add(EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: TextAlign.start));
1004 properties.add(
1005 DiagnosticsProperty<TextAlignVertical>(
1006 'textAlignVertical',
1007 textAlignVertical,
1008 defaultValue: null,
1009 ),
1010 );
1011 properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
1012 properties.add(DoubleProperty('cursorWidth', cursorWidth, defaultValue: 2.0));
1013 properties.add(DoubleProperty('cursorHeight', cursorHeight, defaultValue: null));
1014 properties.add(DiagnosticsProperty<Radius>('cursorRadius', cursorRadius, defaultValue: null));
1015 properties.add(
1016 DiagnosticsProperty<bool>('cursorOpacityAnimates', cursorOpacityAnimates, defaultValue: null),
1017 );
1018 properties.add(ColorProperty('cursorColor', cursorColor, defaultValue: null));
1019 properties.add(ColorProperty('cursorErrorColor', cursorErrorColor, defaultValue: null));
1020 properties.add(
1021 DiagnosticsProperty<Brightness>('keyboardAppearance', keyboardAppearance, defaultValue: null),
1022 );
1023 properties.add(
1024 DiagnosticsProperty<EdgeInsetsGeometry>(
1025 'scrollPadding',
1026 scrollPadding,
1027 defaultValue: const EdgeInsets.all(20.0),
1028 ),
1029 );
1030 properties.add(
1031 FlagProperty(
1032 'selectionEnabled',
1033 value: selectionEnabled,
1034 defaultValue: true,
1035 ifFalse: 'selection disabled',
1036 ),
1037 );
1038 properties.add(
1039 DiagnosticsProperty<TextSelectionControls>(
1040 'selectionControls',
1041 selectionControls,
1042 defaultValue: null,
1043 ),
1044 );
1045 properties.add(
1046 DiagnosticsProperty<ScrollController>(
1047 'scrollController',
1048 scrollController,
1049 defaultValue: null,
1050 ),
1051 );
1052 properties.add(
1053 DiagnosticsProperty<ScrollPhysics>('scrollPhysics', scrollPhysics, defaultValue: null),
1054 );
1055 properties.add(
1056 DiagnosticsProperty<Clip>('clipBehavior', clipBehavior, defaultValue: Clip.hardEdge),
1057 );
1058 properties.add(
1059 DiagnosticsProperty<bool>('scribbleEnabled', scribbleEnabled, defaultValue: true),
1060 );
1061 properties.add(
1062 DiagnosticsProperty<bool>(
1063 'stylusHandwritingEnabled',
1064 stylusHandwritingEnabled,
1065 defaultValue: EditableText.defaultStylusHandwritingEnabled,
1066 ),
1067 );
1068 properties.add(
1069 DiagnosticsProperty<bool>(
1070 'enableIMEPersonalizedLearning',
1071 enableIMEPersonalizedLearning,
1072 defaultValue: true,
1073 ),
1074 );
1075 properties.add(
1076 DiagnosticsProperty<SpellCheckConfiguration>(
1077 'spellCheckConfiguration',
1078 spellCheckConfiguration,
1079 defaultValue: null,
1080 ),
1081 );
1082 properties.add(
1083 DiagnosticsProperty<List<String>>(
1084 'contentCommitMimeTypes',
1085 contentInsertionConfiguration?.allowedMimeTypes ?? const <String>[],
1086 defaultValue:
1087 contentInsertionConfiguration == null
1088 ? const <String>[]
1089 : kDefaultContentInsertionMimeTypes,
1090 ),
1091 );
1092 }
1093}
1094
1095class _TextFieldState extends State<TextField>
1096 with RestorationMixin
1097 implements TextSelectionGestureDetectorBuilderDelegate, AutofillClient {
1098 RestorableTextEditingController? _controller;
1099 TextEditingController get _effectiveController => widget.controller ?? _controller!.value;
1100
1101 FocusNode? _focusNode;
1102 FocusNode get _effectiveFocusNode => widget.focusNode ?? (_focusNode ??= FocusNode());
1103
1104 MaxLengthEnforcement get _effectiveMaxLengthEnforcement =>
1105 widget.maxLengthEnforcement ??
1106 LengthLimitingTextInputFormatter.getDefaultMaxLengthEnforcement(Theme.of(context).platform);
1107
1108 bool _isHovering = false;
1109
1110 bool get needsCounter =>
1111 widget.maxLength != null &&
1112 widget.decoration != null &&
1113 widget.decoration!.counterText == null;
1114
1115 bool _showSelectionHandles = false;
1116
1117 late _TextFieldSelectionGestureDetectorBuilder _selectionGestureDetectorBuilder;
1118
1119 // API for TextSelectionGestureDetectorBuilderDelegate.
1120 @override
1121 late bool forcePressEnabled;
1122
1123 @override
1124 final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>();
1125
1126 @override
1127 bool get selectionEnabled => widget.selectionEnabled && _isEnabled;
1128 // End of API for TextSelectionGestureDetectorBuilderDelegate.
1129
1130 bool get _isEnabled => widget.enabled ?? widget.decoration?.enabled ?? true;
1131
1132 int get _currentLength => _effectiveController.value.text.characters.length;
1133
1134 bool get _hasIntrinsicError =>
1135 widget.maxLength != null &&
1136 widget.maxLength! > 0 &&
1137 (widget.controller == null
1138 ? !restorePending && _effectiveController.value.text.characters.length > widget.maxLength!
1139 : _effectiveController.value.text.characters.length > widget.maxLength!);
1140
1141 bool get _hasError =>
1142 widget.decoration?.errorText != null ||
1143 widget.decoration?.error != null ||
1144 _hasIntrinsicError;
1145
1146 Color get _errorColor =>
1147 widget.cursorErrorColor ??
1148 _getEffectiveDecoration().errorStyle?.color ??
1149 Theme.of(context).colorScheme.error;
1150
1151 InputDecoration _getEffectiveDecoration() {
1152 final MaterialLocalizations localizations = MaterialLocalizations.of(context);
1153 final ThemeData themeData = Theme.of(context);
1154 final InputDecoration effectiveDecoration = (widget.decoration ?? const InputDecoration())
1155 .applyDefaults(themeData.inputDecorationTheme)
1156 .copyWith(
1157 enabled: _isEnabled,
1158 hintMaxLines: widget.decoration?.hintMaxLines ?? widget.maxLines,
1159 );
1160
1161 // No need to build anything if counter or counterText were given directly.
1162 if (effectiveDecoration.counter != null || effectiveDecoration.counterText != null) {
1163 return effectiveDecoration;
1164 }
1165
1166 // If buildCounter was provided, use it to generate a counter widget.
1167 Widget? counter;
1168 final int currentLength = _currentLength;
1169 if (effectiveDecoration.counter == null &&
1170 effectiveDecoration.counterText == null &&
1171 widget.buildCounter != null) {
1172 final bool isFocused = _effectiveFocusNode.hasFocus;
1173 final Widget? builtCounter = widget.buildCounter!(
1174 context,
1175 currentLength: currentLength,
1176 maxLength: widget.maxLength,
1177 isFocused: isFocused,
1178 );
1179 // If buildCounter returns null, don't add a counter widget to the field.
1180 if (builtCounter != null) {
1181 counter = Semantics(container: true, liveRegion: isFocused, child: builtCounter);
1182 }
1183 return effectiveDecoration.copyWith(counter: counter);
1184 }
1185
1186 if (widget.maxLength == null) {
1187 return effectiveDecoration;
1188 } // No counter widget
1189
1190 String counterText = '$currentLength';
1191 String semanticCounterText = '';
1192
1193 // Handle a real maxLength (positive number)
1194 if (widget.maxLength! > 0) {
1195 // Show the maxLength in the counter
1196 counterText += '/${widget.maxLength}';
1197 final int remaining = (widget.maxLength! - currentLength).clamp(0, widget.maxLength!);
1198 semanticCounterText = localizations.remainingTextFieldCharacterCount(remaining);
1199 }
1200
1201 if (_hasIntrinsicError) {
1202 return effectiveDecoration.copyWith(
1203 errorText: effectiveDecoration.errorText ?? '',
1204 counterStyle:
1205 effectiveDecoration.errorStyle ??
1206 (themeData.useMaterial3
1207 ? _m3CounterErrorStyle(context)
1208 : _m2CounterErrorStyle(context)),
1209 counterText: counterText,
1210 semanticCounterText: semanticCounterText,
1211 );
1212 }
1213
1214 return effectiveDecoration.copyWith(
1215 counterText: counterText,
1216 semanticCounterText: semanticCounterText,
1217 );
1218 }
1219
1220 @override
1221 void initState() {
1222 super.initState();
1223 _selectionGestureDetectorBuilder = _TextFieldSelectionGestureDetectorBuilder(state: this);
1224 if (widget.controller == null) {
1225 _createLocalController();
1226 }
1227 _effectiveFocusNode.canRequestFocus = widget.canRequestFocus && _isEnabled;
1228 _effectiveFocusNode.addListener(_handleFocusChanged);
1229 _initStatesController();
1230 }
1231
1232 bool get _canRequestFocus {
1233 final NavigationMode mode =
1234 MediaQuery.maybeNavigationModeOf(context) ?? NavigationMode.traditional;
1235 return switch (mode) {
1236 NavigationMode.traditional => widget.canRequestFocus && _isEnabled,
1237 NavigationMode.directional => true,
1238 };
1239 }
1240
1241 @override
1242 void didChangeDependencies() {
1243 super.didChangeDependencies();
1244 _effectiveFocusNode.canRequestFocus = _canRequestFocus;
1245 }
1246
1247 @override
1248 void didUpdateWidget(TextField oldWidget) {
1249 super.didUpdateWidget(oldWidget);
1250 if (widget.controller == null && oldWidget.controller != null) {
1251 _createLocalController(oldWidget.controller!.value);
1252 } else if (widget.controller != null && oldWidget.controller == null) {
1253 unregisterFromRestoration(_controller!);
1254 _controller!.dispose();
1255 _controller = null;
1256 }
1257
1258 if (widget.focusNode != oldWidget.focusNode) {
1259 (oldWidget.focusNode ?? _focusNode)?.removeListener(_handleFocusChanged);
1260 (widget.focusNode ?? _focusNode)?.addListener(_handleFocusChanged);
1261 }
1262
1263 _effectiveFocusNode.canRequestFocus = _canRequestFocus;
1264
1265 if (_effectiveFocusNode.hasFocus && widget.readOnly != oldWidget.readOnly && _isEnabled) {
1266 if (_effectiveController.selection.isCollapsed) {
1267 _showSelectionHandles = !widget.readOnly;
1268 }
1269 }
1270
1271 if (widget.statesController == oldWidget.statesController) {
1272 _statesController.update(MaterialState.disabled, !_isEnabled);
1273 _statesController.update(MaterialState.hovered, _isHovering);
1274 _statesController.update(MaterialState.focused, _effectiveFocusNode.hasFocus);
1275 _statesController.update(MaterialState.error, _hasError);
1276 } else {
1277 oldWidget.statesController?.removeListener(_handleStatesControllerChange);
1278 if (widget.statesController != null) {
1279 _internalStatesController?.dispose();
1280 _internalStatesController = null;
1281 }
1282 _initStatesController();
1283 }
1284 }
1285
1286 @override
1287 void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
1288 if (_controller != null) {
1289 _registerController();
1290 }
1291 }
1292
1293 void _registerController() {
1294 assert(_controller != null);
1295 registerForRestoration(_controller!, 'controller');
1296 }
1297
1298 void _createLocalController([TextEditingValue? value]) {
1299 assert(_controller == null);
1300 _controller =
1301 value == null
1302 ? RestorableTextEditingController()
1303 : RestorableTextEditingController.fromValue(value);
1304 if (!restorePending) {
1305 _registerController();
1306 }
1307 }
1308
1309 @override
1310 String? get restorationId => widget.restorationId;
1311
1312 @override
1313 void dispose() {
1314 _effectiveFocusNode.removeListener(_handleFocusChanged);
1315 _focusNode?.dispose();
1316 _controller?.dispose();
1317 _statesController.removeListener(_handleStatesControllerChange);
1318 _internalStatesController?.dispose();
1319 super.dispose();
1320 }
1321
1322 EditableTextState? get _editableText => editableTextKey.currentState;
1323
1324 void _requestKeyboard() {
1325 _editableText?.requestKeyboard();
1326 }
1327
1328 bool _shouldShowSelectionHandles(SelectionChangedCause? cause) {
1329 // When the text field is activated by something that doesn't trigger the
1330 // selection overlay, we shouldn't show the handles either.
1331 if (!_selectionGestureDetectorBuilder.shouldShowSelectionToolbar) {
1332 return false;
1333 }
1334
1335 if (cause == SelectionChangedCause.keyboard) {
1336 return false;
1337 }
1338
1339 if (widget.readOnly && _effectiveController.selection.isCollapsed) {
1340 return false;
1341 }
1342
1343 if (!_isEnabled) {
1344 return false;
1345 }
1346
1347 if (cause == SelectionChangedCause.longPress ||
1348 cause == SelectionChangedCause.stylusHandwriting) {
1349 return true;
1350 }
1351
1352 if (_effectiveController.text.isNotEmpty) {
1353 return true;
1354 }
1355
1356 return false;
1357 }
1358
1359 void _handleFocusChanged() {
1360 setState(() {
1361 // Rebuild the widget on focus change to show/hide the text selection
1362 // highlight.
1363 });
1364 _statesController.update(MaterialState.focused, _effectiveFocusNode.hasFocus);
1365 }
1366
1367 void _handleSelectionChanged(TextSelection selection, SelectionChangedCause? cause) {
1368 final bool willShowSelectionHandles = _shouldShowSelectionHandles(cause);
1369 if (willShowSelectionHandles != _showSelectionHandles) {
1370 setState(() {
1371 _showSelectionHandles = willShowSelectionHandles;
1372 });
1373 }
1374
1375 switch (Theme.of(context).platform) {
1376 case TargetPlatform.iOS:
1377 case TargetPlatform.macOS:
1378 case TargetPlatform.linux:
1379 case TargetPlatform.windows:
1380 case TargetPlatform.fuchsia:
1381 case TargetPlatform.android:
1382 if (cause == SelectionChangedCause.longPress) {
1383 _editableText?.bringIntoView(selection.extent);
1384 }
1385 }
1386
1387 switch (Theme.of(context).platform) {
1388 case TargetPlatform.iOS:
1389 case TargetPlatform.fuchsia:
1390 case TargetPlatform.android:
1391 break;
1392 case TargetPlatform.macOS:
1393 case TargetPlatform.linux:
1394 case TargetPlatform.windows:
1395 if (cause == SelectionChangedCause.drag) {
1396 _editableText?.hideToolbar();
1397 }
1398 }
1399 }
1400
1401 /// Toggle the toolbar when a selection handle is tapped.
1402 void _handleSelectionHandleTapped() {
1403 if (_effectiveController.selection.isCollapsed) {
1404 _editableText!.toggleToolbar();
1405 }
1406 }
1407
1408 void _handleHover(bool hovering) {
1409 if (hovering != _isHovering) {
1410 setState(() {
1411 _isHovering = hovering;
1412 });
1413 _statesController.update(MaterialState.hovered, _isHovering);
1414 }
1415 }
1416
1417 // Material states controller.
1418 MaterialStatesController? _internalStatesController;
1419
1420 void _handleStatesControllerChange() {
1421 // Force a rebuild to resolve MaterialStateProperty properties.
1422 setState(() {});
1423 }
1424
1425 MaterialStatesController get _statesController =>
1426 widget.statesController ?? _internalStatesController!;
1427
1428 void _initStatesController() {
1429 if (widget.statesController == null) {
1430 _internalStatesController = MaterialStatesController();
1431 }
1432 _statesController.update(MaterialState.disabled, !_isEnabled);
1433 _statesController.update(MaterialState.hovered, _isHovering);
1434 _statesController.update(MaterialState.focused, _effectiveFocusNode.hasFocus);
1435 _statesController.update(MaterialState.error, _hasError);
1436 _statesController.addListener(_handleStatesControllerChange);
1437 }
1438
1439 // AutofillClient implementation start.
1440 @override
1441 String get autofillId => _editableText!.autofillId;
1442
1443 @override
1444 void autofill(TextEditingValue newEditingValue) => _editableText!.autofill(newEditingValue);
1445
1446 @override
1447 TextInputConfiguration get textInputConfiguration {
1448 final List<String>? autofillHints = widget.autofillHints?.toList(growable: false);
1449 final AutofillConfiguration autofillConfiguration =
1450 autofillHints != null
1451 ? AutofillConfiguration(
1452 uniqueIdentifier: autofillId,
1453 autofillHints: autofillHints,
1454 currentEditingValue: _effectiveController.value,
1455 hintText: (widget.decoration ?? const InputDecoration()).hintText,
1456 )
1457 : AutofillConfiguration.disabled;
1458
1459 return _editableText!.textInputConfiguration.copyWith(
1460 autofillConfiguration: autofillConfiguration,
1461 );
1462 }
1463 // AutofillClient implementation end.
1464
1465 TextStyle _getInputStyleForState(TextStyle style) {
1466 final ThemeData theme = Theme.of(context);
1467 final TextStyle stateStyle = MaterialStateProperty.resolveAs(
1468 theme.useMaterial3 ? _m3StateInputStyle(context)! : _m2StateInputStyle(context)!,
1469 _statesController.value,
1470 );
1471 final TextStyle providedStyle = MaterialStateProperty.resolveAs(style, _statesController.value);
1472 return providedStyle.merge(stateStyle);
1473 }
1474
1475 @override
1476 Widget build(BuildContext context) {
1477 assert(debugCheckHasMaterial(context));
1478 assert(debugCheckHasMaterialLocalizations(context));
1479 assert(debugCheckHasDirectionality(context));
1480 assert(
1481 !(widget.style != null &&
1482 !widget.style!.inherit &&
1483 (widget.style!.fontSize == null || widget.style!.textBaseline == null)),
1484 'inherit false style must supply fontSize and textBaseline',
1485 );
1486
1487 final ThemeData theme = Theme.of(context);
1488 final DefaultSelectionStyle selectionStyle = DefaultSelectionStyle.of(context);
1489 final TextStyle? providedStyle = MaterialStateProperty.resolveAs(
1490 widget.style,
1491 _statesController.value,
1492 );
1493 final TextStyle style = _getInputStyleForState(
1494 theme.useMaterial3 ? _m3InputStyle(context) : theme.textTheme.titleMedium!,
1495 ).merge(providedStyle);
1496 final Brightness keyboardAppearance = widget.keyboardAppearance ?? theme.brightness;
1497 final TextEditingController controller = _effectiveController;
1498 final FocusNode focusNode = _effectiveFocusNode;
1499 final List<TextInputFormatter> formatters = <TextInputFormatter>[
1500 ...?widget.inputFormatters,
1501 if (widget.maxLength != null)
1502 LengthLimitingTextInputFormatter(
1503 widget.maxLength,
1504 maxLengthEnforcement: _effectiveMaxLengthEnforcement,
1505 ),
1506 ];
1507
1508 // Set configuration as disabled if not otherwise specified. If specified,
1509 // ensure that configuration uses the correct style for misspelled words for
1510 // the current platform, unless a custom style is specified.
1511 final SpellCheckConfiguration spellCheckConfiguration;
1512 switch (defaultTargetPlatform) {
1513 case TargetPlatform.iOS:
1514 case TargetPlatform.macOS:
1515 spellCheckConfiguration = CupertinoTextField.inferIOSSpellCheckConfiguration(
1516 widget.spellCheckConfiguration,
1517 );
1518 case TargetPlatform.android:
1519 case TargetPlatform.fuchsia:
1520 case TargetPlatform.linux:
1521 case TargetPlatform.windows:
1522 spellCheckConfiguration = TextField.inferAndroidSpellCheckConfiguration(
1523 widget.spellCheckConfiguration,
1524 );
1525 }
1526
1527 TextSelectionControls? textSelectionControls = widget.selectionControls;
1528 final bool paintCursorAboveText;
1529 bool? cursorOpacityAnimates = widget.cursorOpacityAnimates;
1530 Offset? cursorOffset;
1531 final Color cursorColor;
1532 final Color selectionColor;
1533 Color? autocorrectionTextRectColor;
1534 Radius? cursorRadius = widget.cursorRadius;
1535 VoidCallback? handleDidGainAccessibilityFocus;
1536 VoidCallback? handleDidLoseAccessibilityFocus;
1537
1538 switch (theme.platform) {
1539 case TargetPlatform.iOS:
1540 final CupertinoThemeData cupertinoTheme = CupertinoTheme.of(context);
1541 forcePressEnabled = true;
1542 textSelectionControls ??= cupertinoTextSelectionHandleControls;
1543 paintCursorAboveText = true;
1544 cursorOpacityAnimates ??= true;
1545 cursorColor =
1546 _hasError
1547 ? _errorColor
1548 : widget.cursorColor ?? selectionStyle.cursorColor ?? cupertinoTheme.primaryColor;
1549 selectionColor =
1550 selectionStyle.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40);
1551 cursorRadius ??= const Radius.circular(2.0);
1552 cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.devicePixelRatioOf(context), 0);
1553 autocorrectionTextRectColor = selectionColor;
1554
1555 case TargetPlatform.macOS:
1556 final CupertinoThemeData cupertinoTheme = CupertinoTheme.of(context);
1557 forcePressEnabled = false;
1558 textSelectionControls ??= cupertinoDesktopTextSelectionHandleControls;
1559 paintCursorAboveText = true;
1560 cursorOpacityAnimates ??= false;
1561 cursorColor =
1562 _hasError
1563 ? _errorColor
1564 : widget.cursorColor ?? selectionStyle.cursorColor ?? cupertinoTheme.primaryColor;
1565 selectionColor =
1566 selectionStyle.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40);
1567 cursorRadius ??= const Radius.circular(2.0);
1568 cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.devicePixelRatioOf(context), 0);
1569 handleDidGainAccessibilityFocus = () {
1570 // Automatically activate the TextField when it receives accessibility focus.
1571 if (!_effectiveFocusNode.hasFocus && _effectiveFocusNode.canRequestFocus) {
1572 _effectiveFocusNode.requestFocus();
1573 }
1574 };
1575 handleDidLoseAccessibilityFocus = () {
1576 _effectiveFocusNode.unfocus();
1577 };
1578
1579 case TargetPlatform.android:
1580 case TargetPlatform.fuchsia:
1581 forcePressEnabled = false;
1582 textSelectionControls ??= materialTextSelectionHandleControls;
1583 paintCursorAboveText = false;
1584 cursorOpacityAnimates ??= false;
1585 cursorColor =
1586 _hasError
1587 ? _errorColor
1588 : widget.cursorColor ?? selectionStyle.cursorColor ?? theme.colorScheme.primary;
1589 selectionColor =
1590 selectionStyle.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
1591
1592 case TargetPlatform.linux:
1593 forcePressEnabled = false;
1594 textSelectionControls ??= desktopTextSelectionHandleControls;
1595 paintCursorAboveText = false;
1596 cursorOpacityAnimates ??= false;
1597 cursorColor =
1598 _hasError
1599 ? _errorColor
1600 : widget.cursorColor ?? selectionStyle.cursorColor ?? theme.colorScheme.primary;
1601 selectionColor =
1602 selectionStyle.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
1603 handleDidGainAccessibilityFocus = () {
1604 // Automatically activate the TextField when it receives accessibility focus.
1605 if (!_effectiveFocusNode.hasFocus && _effectiveFocusNode.canRequestFocus) {
1606 _effectiveFocusNode.requestFocus();
1607 }
1608 };
1609 handleDidLoseAccessibilityFocus = () {
1610 _effectiveFocusNode.unfocus();
1611 };
1612
1613 case TargetPlatform.windows:
1614 forcePressEnabled = false;
1615 textSelectionControls ??= desktopTextSelectionHandleControls;
1616 paintCursorAboveText = false;
1617 cursorOpacityAnimates ??= false;
1618 cursorColor =
1619 _hasError
1620 ? _errorColor
1621 : widget.cursorColor ?? selectionStyle.cursorColor ?? theme.colorScheme.primary;
1622 selectionColor =
1623 selectionStyle.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
1624 handleDidGainAccessibilityFocus = () {
1625 // Automatically activate the TextField when it receives accessibility focus.
1626 if (!_effectiveFocusNode.hasFocus && _effectiveFocusNode.canRequestFocus) {
1627 _effectiveFocusNode.requestFocus();
1628 }
1629 };
1630 handleDidLoseAccessibilityFocus = () {
1631 _effectiveFocusNode.unfocus();
1632 };
1633 }
1634
1635 Widget child = RepaintBoundary(
1636 child: UnmanagedRestorationScope(
1637 bucket: bucket,
1638 child: EditableText(
1639 key: editableTextKey,
1640 readOnly: widget.readOnly || !_isEnabled,
1641 toolbarOptions: widget.toolbarOptions,
1642 showCursor: widget.showCursor,
1643 showSelectionHandles: _showSelectionHandles,
1644 controller: controller,
1645 focusNode: focusNode,
1646 undoController: widget.undoController,
1647 keyboardType: widget.keyboardType,
1648 textInputAction: widget.textInputAction,
1649 textCapitalization: widget.textCapitalization,
1650 style: style,
1651 strutStyle: widget.strutStyle,
1652 textAlign: widget.textAlign,
1653 textDirection: widget.textDirection,
1654 autofocus: widget.autofocus,
1655 obscuringCharacter: widget.obscuringCharacter,
1656 obscureText: widget.obscureText,
1657 autocorrect: widget.autocorrect,
1658 smartDashesType: widget.smartDashesType,
1659 smartQuotesType: widget.smartQuotesType,
1660 enableSuggestions: widget.enableSuggestions,
1661 maxLines: widget.maxLines,
1662 minLines: widget.minLines,
1663 expands: widget.expands,
1664 // Only show the selection highlight when the text field is focused.
1665 selectionColor: focusNode.hasFocus ? selectionColor : null,
1666 selectionControls: widget.selectionEnabled ? textSelectionControls : null,
1667 onChanged: widget.onChanged,
1668 onSelectionChanged: _handleSelectionChanged,
1669 onEditingComplete: widget.onEditingComplete,
1670 onSubmitted: widget.onSubmitted,
1671 onAppPrivateCommand: widget.onAppPrivateCommand,
1672 groupId: widget.groupId,
1673 onSelectionHandleTapped: _handleSelectionHandleTapped,
1674 onTapOutside: widget.onTapOutside,
1675 onTapUpOutside: widget.onTapUpOutside,
1676 inputFormatters: formatters,
1677 rendererIgnoresPointer: true,
1678 mouseCursor: MouseCursor.defer, // TextField will handle the cursor
1679 cursorWidth: widget.cursorWidth,
1680 cursorHeight: widget.cursorHeight,
1681 cursorRadius: cursorRadius,
1682 cursorColor: cursorColor,
1683 selectionHeightStyle: widget.selectionHeightStyle,
1684 selectionWidthStyle: widget.selectionWidthStyle,
1685 cursorOpacityAnimates: cursorOpacityAnimates,
1686 cursorOffset: cursorOffset,
1687 paintCursorAboveText: paintCursorAboveText,
1688 backgroundCursorColor: CupertinoColors.inactiveGray,
1689 scrollPadding: widget.scrollPadding,
1690 keyboardAppearance: keyboardAppearance,
1691 enableInteractiveSelection: widget.enableInteractiveSelection,
1692 dragStartBehavior: widget.dragStartBehavior,
1693 scrollController: widget.scrollController,
1694 scrollPhysics: widget.scrollPhysics,
1695 autofillClient: this,
1696 autocorrectionTextRectColor: autocorrectionTextRectColor,
1697 clipBehavior: widget.clipBehavior,
1698 restorationId: 'editable',
1699 scribbleEnabled: widget.scribbleEnabled,
1700 stylusHandwritingEnabled: widget.stylusHandwritingEnabled,
1701 enableIMEPersonalizedLearning: widget.enableIMEPersonalizedLearning,
1702 contentInsertionConfiguration: widget.contentInsertionConfiguration,
1703 contextMenuBuilder: widget.contextMenuBuilder,
1704 spellCheckConfiguration: spellCheckConfiguration,
1705 magnifierConfiguration:
1706 widget.magnifierConfiguration ?? TextMagnifier.adaptiveMagnifierConfiguration,
1707 ),
1708 ),
1709 );
1710
1711 if (widget.decoration != null) {
1712 child = AnimatedBuilder(
1713 animation: Listenable.merge(<Listenable>[focusNode, controller]),
1714 builder: (BuildContext context, Widget? child) {
1715 return InputDecorator(
1716 decoration: _getEffectiveDecoration(),
1717 baseStyle: widget.style,
1718 textAlign: widget.textAlign,
1719 textAlignVertical: widget.textAlignVertical,
1720 isHovering: _isHovering,
1721 isFocused: focusNode.hasFocus,
1722 isEmpty: controller.value.text.isEmpty,
1723 expands: widget.expands,
1724 child: child,
1725 );
1726 },
1727 child: child,
1728 );
1729 }
1730 final MouseCursor effectiveMouseCursor = MaterialStateProperty.resolveAs<MouseCursor>(
1731 widget.mouseCursor ?? MaterialStateMouseCursor.textable,
1732 _statesController.value,
1733 );
1734
1735 final int? semanticsMaxValueLength;
1736 if (_effectiveMaxLengthEnforcement != MaxLengthEnforcement.none &&
1737 widget.maxLength != null &&
1738 widget.maxLength! > 0) {
1739 semanticsMaxValueLength = widget.maxLength;
1740 } else {
1741 semanticsMaxValueLength = null;
1742 }
1743
1744 return MouseRegion(
1745 cursor: effectiveMouseCursor,
1746 onEnter: (PointerEnterEvent event) => _handleHover(true),
1747 onExit: (PointerExitEvent event) => _handleHover(false),
1748 child: TextFieldTapRegion(
1749 child: IgnorePointer(
1750 ignoring: widget.ignorePointers ?? !_isEnabled,
1751 child: AnimatedBuilder(
1752 animation: controller, // changes the _currentLength
1753 builder: (BuildContext context, Widget? child) {
1754 return Semantics(
1755 enabled: _isEnabled,
1756 maxValueLength: semanticsMaxValueLength,
1757 currentValueLength: _currentLength,
1758 onTap:
1759 widget.readOnly
1760 ? null
1761 : () {
1762 if (!_effectiveController.selection.isValid) {
1763 _effectiveController.selection = TextSelection.collapsed(
1764 offset: _effectiveController.text.length,
1765 );
1766 }
1767 _requestKeyboard();
1768 },
1769 onDidGainAccessibilityFocus: handleDidGainAccessibilityFocus,
1770 onDidLoseAccessibilityFocus: handleDidLoseAccessibilityFocus,
1771 onFocus:
1772 _isEnabled
1773 ? () {
1774 assert(
1775 _effectiveFocusNode.canRequestFocus,
1776 'Received SemanticsAction.focus from the engine. However, the FocusNode '
1777 'of this text field cannot gain focus. This likely indicates a bug. '
1778 'If this text field cannot be focused (e.g. because it is not '
1779 'enabled), then its corresponding semantics node must be configured '
1780 'such that the assistive technology cannot request focus on it.',
1781 );
1782
1783 if (_effectiveFocusNode.canRequestFocus &&
1784 !_effectiveFocusNode.hasFocus) {
1785 _effectiveFocusNode.requestFocus();
1786 } else if (!widget.readOnly) {
1787 // If the platform requested focus, that means that previously the
1788 // platform believed that the text field did not have focus (even
1789 // though Flutter's widget system believed otherwise). This likely
1790 // means that the on-screen keyboard is hidden, or more generally,
1791 // there is no current editing session in this field. To correct
1792 // that, keyboard must be requested.
1793 //
1794 // A concrete scenario where this can happen is when the user
1795 // dismisses the keyboard on the web. The editing session is
1796 // closed by the engine, but the text field widget stays focused
1797 // in the framework.
1798 _requestKeyboard();
1799 }
1800 }
1801 : null,
1802 child: child,
1803 );
1804 },
1805 child: _selectionGestureDetectorBuilder.buildGestureDetector(
1806 behavior: HitTestBehavior.translucent,
1807 child: child,
1808 ),
1809 ),
1810 ),
1811 ),
1812 );
1813 }
1814}
1815
1816TextStyle? _m2StateInputStyle(BuildContext context) =>
1817 MaterialStateTextStyle.resolveWith((Set<MaterialState> states) {
1818 final ThemeData theme = Theme.of(context);
1819 if (states.contains(MaterialState.disabled)) {
1820 return TextStyle(color: theme.disabledColor);
1821 }
1822 return TextStyle(color: theme.textTheme.titleMedium?.color);
1823 });
1824
1825TextStyle _m2CounterErrorStyle(BuildContext context) =>
1826 Theme.of(context).textTheme.bodySmall!.copyWith(color: Theme.of(context).colorScheme.error);
1827
1828// BEGIN GENERATED TOKEN PROPERTIES - TextField
1829
1830// Do not edit by hand. The code between the "BEGIN GENERATED" and
1831// "END GENERATED" comments are generated from data in the Material
1832// Design token database by the script:
1833// dev/tools/gen_defaults/bin/gen_defaults.dart.
1834
1835// dart format off
1836TextStyle? _m3StateInputStyle(BuildContext context) => MaterialStateTextStyle.resolveWith((Set<MaterialState> states) {
1837 if (states.contains(MaterialState.disabled)) {
1838 return TextStyle(color: Theme.of(context).textTheme.bodyLarge!.color?.withOpacity(0.38));
1839 }
1840 return TextStyle(color: Theme.of(context).textTheme.bodyLarge!.color);
1841});
1842
1843TextStyle _m3InputStyle(BuildContext context) => Theme.of(context).textTheme.bodyLarge!;
1844
1845TextStyle _m3CounterErrorStyle(BuildContext context) =>
1846 Theme.of(context).textTheme.bodySmall!.copyWith(color: Theme.of(context).colorScheme.error);
1847// dart format on
1848
1849// END GENERATED TOKEN PROPERTIES - TextField
1850

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com