import 'dart:ui' as ui show BoxHeightStyle, BoxWidthStyle; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class PasswordTextField extends StatefulWidget { const PasswordTextField({ super.key, this.visibleIcon = Icons.remove_red_eye_outlined, this.inVisibleIcon = Icons.remove_red_eye_outlined, this.initialObscurity = false, this.controller, this.focusNode, this.undoController, this.decoration = const InputDecoration(), this.keyboardType, this.textInputAction, this.textCapitalization = TextCapitalization.none, this.style, this.strutStyle, this.textAlign = TextAlign.start, this.textAlignVertical, this.textDirection, this.readOnly = false, this.showCursor, this.autofocus = false, this.obscuringCharacter = '•', this.autocorrect = true, 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, 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.onTapOutside, this.mouseCursor, this.buildCounter, this.scrollController, this.scrollPhysics, this.autofillHints = const [], this.contentInsertionConfiguration, this.clipBehavior = Clip.hardEdge, this.restorationId, this.scribbleEnabled = true, this.enableIMEPersonalizedLearning = true, this.contextMenuBuilder, this.canRequestFocus = true, this.spellCheckConfiguration, this.magnifierConfiguration, }); /// Displaying toggle icon(show). /// /// Defaults is [Icons.visibility]. final IconData visibleIcon; /// Displaying toggle icon(hide). /// /// Defaults is [Icons.visibility_off]. final IconData inVisibleIcon; /// The obscure feature is enabled by default. /// /// Default is [false]. final bool initialObscurity; /// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.intro} /// /// {@macro flutter.widgets.magnifier.intro} /// /// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.details} /// /// By default, builds a [CupertinoTextMagnifier] on iOS and [TextMagnifier] /// on Android, and builds nothing on all other platforms. If it is desired to /// suppress the magnifier, consider passing [TextMagnifierConfiguration.disabled]. /// /// {@tool dartpad} /// This sample demonstrates how to customize the magnifier that this text field uses. /// /// ** See code in examples/api/lib/widgets/text_magnifier/text_magnifier.0.dart ** /// {@end-tool} final TextMagnifierConfiguration? magnifierConfiguration; /// Controls the text being edited. /// /// If null, this widget will create its own [TextEditingController]. final TextEditingController? controller; /// Defines the keyboard focus for this widget. /// /// The [focusNode] is a long-lived object that's typically managed by a /// [StatefulWidget] parent. See [FocusNode] for more information. /// /// To give the keyboard focus to this widget, provide a [focusNode] and then /// use the current [FocusScope] to request the focus: /// /// ```dart /// FocusScope.of(context).requestFocus(myFocusNode); /// ``` /// /// This happens automatically when the widget is tapped. /// /// To be notified when the widget gains or loses the focus, add a listener /// to the [focusNode]: /// /// ```dart /// myFocusNode.addListener(() { print(myFocusNode.hasFocus); }); /// ``` /// /// If null, this widget will create its own [FocusNode]. /// /// ## Keyboard /// /// Requesting the focus will typically cause the keyboard to be shown /// if it's not showing already. /// /// On Android, the user can hide the keyboard - without changing the focus - /// with the system back button. They can restore the keyboard's visibility /// by tapping on a text field. The user might hide the keyboard and /// switch to a physical keyboard, or they might just need to get it /// out of the way for a moment, to expose something it's /// obscuring. In this case requesting the focus again will not /// cause the focus to change, and will not make the keyboard visible. /// /// This widget builds an [EditableText] and will ensure that the keyboard is /// showing when it is tapped by calling [EditableTextState.requestKeyboard()]. final FocusNode? focusNode; /// The decoration to show around the text field. /// /// By default, draws a horizontal line under the text field but can be /// configured to show an icon, label, hint text, and error text. /// /// Specify null to remove the decoration entirely (including the /// extra padding introduced by the decoration to save space for the labels). final InputDecoration decoration; /// {@macro flutter.widgets.editableText.keyboardType} final TextInputType? keyboardType; /// The type of action button to use for the keyboard. /// /// Defaults to [TextInputAction.newline] if [keyboardType] is /// [TextInputType.multiline] and [TextInputAction.done] otherwise. final TextInputAction? textInputAction; /// {@macro flutter.widgets.editableText.textCapitalization} final TextCapitalization textCapitalization; /// The style to use for the text being edited. /// /// This text style is also used as the base style for the [decoration]. /// /// If null, defaults to the `titleMedium` text style from the current [Theme]. final TextStyle? style; /// {@macro flutter.widgets.editableText.strutStyle} final StrutStyle? strutStyle; /// {@macro flutter.widgets.editableText.textAlign} final TextAlign textAlign; /// {@macro flutter.material.InputDecorator.textAlignVertical} final TextAlignVertical? textAlignVertical; /// {@macro flutter.widgets.editableText.textDirection} final TextDirection? textDirection; /// {@macro flutter.widgets.editableText.autofocus} final bool autofocus; /// {@macro flutter.widgets.editableText.obscuringCharacter} final String obscuringCharacter; /// {@macro flutter.widgets.editableText.autocorrect} final bool autocorrect; /// {@macro flutter.services.TextInputConfiguration.enableSuggestions} final bool enableSuggestions; /// {@macro flutter.widgets.editableText.maxLines} /// * [expands], which determines whether the field should fill the height of /// its parent. final int? maxLines; /// {@macro flutter.widgets.editableText.minLines} /// * [expands], which determines whether the field should fill the height of /// its parent. final int? minLines; /// {@macro flutter.widgets.editableText.expands} final bool expands; /// {@macro flutter.widgets.editableText.readOnly} final bool readOnly; /// {@macro flutter.widgets.editableText.showCursor} final bool? showCursor; /// The maximum number of characters (Unicode grapheme clusters) to allow in /// the text field. /// /// If set, a character counter will be displayed below the /// field showing how many characters have been entered. If set to a number /// greater than 0, it will also display the maximum number allowed. If set /// to [TextField.noMaxLength] then only the current character count is displayed. /// /// After [maxLength] characters have been input, additional input /// is ignored, unless [maxLengthEnforcement] is set to /// [MaxLengthEnforcement.none]. /// /// The text field enforces the length with a [LengthLimitingTextInputFormatter], /// which is evaluated after the supplied [inputFormatters], if any. /// /// This value must be either null, [TextField.noMaxLength], or greater than 0. /// If null (the default) then there is no limit to the number of characters /// that can be entered. If set to [TextField.noMaxLength], then no limit will /// be enforced, but the number of characters entered will still be displayed. /// /// Whitespace characters (e.g. newline, space, tab) are included in the /// character count. /// /// If [maxLengthEnforcement] is [MaxLengthEnforcement.none], then more than /// [maxLength] characters may be entered, but the error counter and divider /// will switch to the [decoration]'s [InputDecoration.errorStyle] when the /// limit is exceeded. /// /// {@macro flutter.services.lengthLimitingTextInputFormatter.maxLength} final int? maxLength; /// Determines how the [maxLength] limit should be enforced. /// /// {@macro flutter.services.textFormatter.effectiveMaxLengthEnforcement} /// /// {@macro flutter.services.textFormatter.maxLengthEnforcement} final MaxLengthEnforcement? maxLengthEnforcement; /// {@macro flutter.widgets.editableText.onChanged} /// /// See also: /// /// * [inputFormatters], which are called before [onChanged] /// runs and can validate and change ("format") the input value. /// * [onEditingComplete], [onSubmitted]: /// which are more specialized input change notifications. final ValueChanged? onChanged; /// {@macro flutter.widgets.editableText.onEditingComplete} final VoidCallback? onEditingComplete; /// {@macro flutter.widgets.editableText.onSubmitted} /// /// See also: /// /// * [TextInputAction.next] and [TextInputAction.previous], which /// automatically shift the focus to the next/previous focusable item when /// the user is done editing. final ValueChanged? onSubmitted; /// {@macro flutter.widgets.editableText.onAppPrivateCommand} final AppPrivateCommandCallback? onAppPrivateCommand; /// {@macro flutter.widgets.editableText.inputFormatters} final List? inputFormatters; /// If false the text field is "disabled": it ignores taps and its /// [decoration] is rendered in grey. /// /// If non-null this property overrides the [decoration]'s /// [InputDecoration.enabled] property. final bool? enabled; /// {@macro flutter.widgets.editableText.cursorWidth} final double cursorWidth; /// {@macro flutter.widgets.editableText.cursorHeight} final double? cursorHeight; /// {@macro flutter.widgets.editableText.cursorRadius} final Radius? cursorRadius; /// {@macro flutter.widgets.editableText.cursorOpacityAnimates} final bool? cursorOpacityAnimates; /// The color of the cursor. /// /// The cursor indicates the current location of text insertion point in /// the field. /// /// If this is null it will default to the ambient /// [DefaultSelectionStyle.cursorColor]. If that is null, and the /// [ThemeData.platform] is [TargetPlatform.iOS] or [TargetPlatform.macOS] /// it will use [CupertinoThemeData.primaryColor]. Otherwise it will use /// the value of [ColorScheme.primary] of [ThemeData.colorScheme]. final Color? cursorColor; /// Controls how tall the selection highlight boxes are computed to be. /// /// See [ui.BoxHeightStyle] for details on available styles. final ui.BoxHeightStyle selectionHeightStyle; /// Controls how wide the selection highlight boxes are computed to be. /// /// See [ui.BoxWidthStyle] for details on available styles. final ui.BoxWidthStyle selectionWidthStyle; /// The appearance of the keyboard. /// /// This setting is only honored on iOS devices. /// /// If unset, defaults to [ThemeData.brightness]. final Brightness? keyboardAppearance; /// {@macro flutter.widgets.editableText.scrollPadding} final EdgeInsets scrollPadding; /// {@macro flutter.widgets.editableText.enableInteractiveSelection} final bool enableInteractiveSelection; /// {@macro flutter.widgets.editableText.selectionControls} final TextSelectionControls? selectionControls; /// {@macro flutter.widgets.scrollable.dragStartBehavior} final DragStartBehavior dragStartBehavior; /// {@macro flutter.widgets.editableText.selectionEnabled} bool get selectionEnabled => enableInteractiveSelection; /// {@template flutter.material.textfield.onTap} /// Called for each distinct tap except for every second tap of a double tap. /// /// The text field builds a [GestureDetector] to handle input events like tap, /// to trigger focus requests, to move the caret, adjust the selection, etc. /// Handling some of those events by wrapping the text field with a competing /// GestureDetector is problematic. /// /// To unconditionally handle taps, without interfering with the text field's /// internal gesture detector, provide this callback. /// /// If the text field is created with [enabled] false, taps will not be /// recognized. /// /// To be notified when the text field gains or loses the focus, provide a /// [focusNode] and add a listener to that. /// /// To listen to arbitrary pointer events without competing with the /// text field's internal gesture detector, use a [Listener]. /// {@endtemplate} final GestureTapCallback? onTap; /// {@macro flutter.widgets.editableText.onTapOutside} /// /// {@tool dartpad} /// This example shows how to use a `TextFieldTapRegion` to wrap a set of /// "spinner" buttons that increment and decrement a value in the [TextField] /// without causing the text field to lose keyboard focus. /// /// This example includes a generic `SpinnerField` class that you can copy /// into your own project and customize. /// /// ** See code in examples/api/lib/widgets/tap_region/text_field_tap_region.0.dart ** /// {@end-tool} /// /// See also: /// /// * [TapRegion] for how the region group is determined. final TapRegionCallback? onTapOutside; /// The cursor for a mouse pointer when it enters or is hovering over the /// widget. /// /// If [mouseCursor] is a [MaterialStateProperty], /// [MaterialStateProperty.resolve] is used for the following [MaterialState]s: /// /// * [MaterialState.error]. /// * [MaterialState.hovered]. /// * [MaterialState.focused]. /// * [MaterialState.disabled]. /// /// If this property is null, [MaterialStateMouseCursor.textable] will be used. /// /// The [mouseCursor] is the only property of [TextField] that controls the /// appearance of the mouse pointer. All other properties related to "cursor" /// stand for the text cursor, which is usually a blinking vertical line at /// the editing position. final MouseCursor? mouseCursor; /// Callback that generates a custom [InputDecoration.counter] widget. /// /// See [InputCounterWidgetBuilder] for an explanation of the passed in /// arguments. The returned widget will be placed below the line in place of /// the default widget built when [InputDecoration.counterText] is specified. /// /// The returned widget will be wrapped in a [Semantics] widget for /// accessibility, but it also needs to be accessible itself. For example, /// if returning a Text widget, set the [Text.semanticsLabel] property. /// /// {@tool snippet} /// ```dart /// Widget counter( /// BuildContext context, /// { /// required int currentLength, /// required int? maxLength, /// required bool isFocused, /// } /// ) { /// return Text( /// '$currentLength of $maxLength characters', /// semanticsLabel: 'character count', /// ); /// } /// ``` /// {@end-tool} /// /// If buildCounter returns null, then no counter and no Semantics widget will /// be created at all. final InputCounterWidgetBuilder? buildCounter; /// {@macro flutter.widgets.editableText.scrollPhysics} final ScrollPhysics? scrollPhysics; /// {@macro flutter.widgets.editableText.scrollController} final ScrollController? scrollController; /// {@macro flutter.widgets.editableText.autofillHints} /// {@macro flutter.services.AutofillConfiguration.autofillHints} final Iterable? autofillHints; /// {@macro flutter.material.Material.clipBehavior} /// /// Defaults to [Clip.hardEdge]. final Clip clipBehavior; /// {@template flutter.material.textfield.restorationId} /// Restoration ID to save and restore the state of the text field. /// /// If non-null, the text field will persist and restore its current scroll /// offset and - if no [controller] has been provided - the content of the /// text field. If a [controller] has been provided, it is the responsibility /// of the owner of that controller to persist and restore it, e.g. by using /// a [RestorableTextEditingController]. /// /// The state of this widget is persisted in a [RestorationBucket] claimed /// from the surrounding [RestorationScope] using the provided restoration ID. /// /// See also: /// /// * [RestorationManager], which explains how state restoration works in /// Flutter. /// {@endtemplate} final String? restorationId; /// {@macro flutter.widgets.editableText.scribbleEnabled} final bool scribbleEnabled; /// {@macro flutter.services.TextInputConfiguration.enableIMEPersonalizedLearning} final bool enableIMEPersonalizedLearning; /// {@macro flutter.widgets.editableText.contentInsertionConfiguration} final ContentInsertionConfiguration? contentInsertionConfiguration; /// {@macro flutter.widgets.EditableText.contextMenuBuilder} /// /// If not provided, will build a default menu based on the platform. /// /// See also: /// /// * [AdaptiveTextSelectionToolbar], which is built by default. final EditableTextContextMenuBuilder? contextMenuBuilder; /// Determine whether this text field can request the primary focus. /// /// Defaults to true. If false, the text field will not request focus /// when tapped, or when its context menu is displayed. If false it will not /// be possible to move the focus to the text field with tab key. final bool canRequestFocus; /// {@macro flutter.widgets.undoHistory.controller} final UndoHistoryController? undoController; /// {@macro flutter.widgets.EditableText.spellCheckConfiguration} /// /// If [SpellCheckConfiguration.misspelledTextStyle] is not specified in this /// configuration, then [materialMisspelledTextStyle] is used by default. final SpellCheckConfiguration? spellCheckConfiguration; @override State createState() => _PasswordTextFieldState(); } class _PasswordTextFieldState extends State { late bool _obscure; @override void initState() { _obscure = widget.initialObscurity; super.initState(); } @override Widget build(BuildContext context) { return TextField( obscureText: _obscure, decoration: widget.decoration.copyWith( suffixIcon: IconButton( icon: Icon( _obscure ? widget.inVisibleIcon : widget.visibleIcon, size: 18, color: const Color(0xFFBBBBBB) ), onPressed: () { setState(() { _obscure = !_obscure; }); }, ), ), controller: widget.controller, focusNode: widget.focusNode, undoController: widget.undoController, keyboardType: widget.keyboardType, textInputAction: widget.textInputAction, textCapitalization: widget.textCapitalization, style: widget.style, strutStyle: widget.strutStyle, textAlign: widget.textAlign, textAlignVertical: widget.textAlignVertical, textDirection: widget.textDirection, readOnly: widget.readOnly, showCursor: widget.showCursor, autofocus: widget.autofocus, obscuringCharacter: widget.obscuringCharacter, autocorrect: widget.autocorrect, enableSuggestions: widget.enableSuggestions, maxLines: widget.maxLines, minLines: widget.minLines, expands: widget.expands, maxLength: widget.maxLength, maxLengthEnforcement: widget.maxLengthEnforcement, onChanged: widget.onChanged, onEditingComplete: widget.onEditingComplete, onSubmitted: widget.onSubmitted, onAppPrivateCommand: widget.onAppPrivateCommand, inputFormatters: widget.inputFormatters, enabled: widget.enabled, cursorWidth: widget.cursorWidth, cursorHeight: widget.cursorHeight, cursorRadius: widget.cursorRadius, cursorOpacityAnimates: widget.cursorOpacityAnimates, cursorColor: widget.cursorColor, selectionHeightStyle: widget.selectionHeightStyle, selectionWidthStyle: widget.selectionWidthStyle, keyboardAppearance: widget.keyboardAppearance, scrollPadding: widget.scrollPadding, dragStartBehavior: widget.dragStartBehavior, enableInteractiveSelection: widget.enableInteractiveSelection, selectionControls: widget.selectionControls, onTap: widget.onTap, onTapOutside: widget.onTapOutside, mouseCursor: widget.mouseCursor, buildCounter: widget.buildCounter, scrollController: widget.scrollController, scrollPhysics: widget.scrollPhysics, autofillHints: widget.autofillHints, contentInsertionConfiguration: widget.contentInsertionConfiguration, clipBehavior: widget.clipBehavior, restorationId: widget.restorationId, scribbleEnabled: widget.scribbleEnabled, enableIMEPersonalizedLearning: widget.enableIMEPersonalizedLearning, contextMenuBuilder: widget.contextMenuBuilder, canRequestFocus: widget.canRequestFocus, spellCheckConfiguration: widget.spellCheckConfiguration, magnifierConfiguration: widget.magnifierConfiguration, ); } }