forked from firka/flutter
Customizable obscuringCharacter (#55415)
This commit is contained in:
committed by
GitHub
parent
4552af155c
commit
de8cf8b530
@@ -241,6 +241,7 @@ class CupertinoTextField extends StatefulWidget {
|
||||
ToolbarOptions toolbarOptions,
|
||||
this.showCursor,
|
||||
this.autofocus = false,
|
||||
this.obscuringCharacter = '•',
|
||||
this.obscureText = false,
|
||||
this.autocorrect = true,
|
||||
SmartDashesType smartDashesType,
|
||||
@@ -272,6 +273,7 @@ class CupertinoTextField extends StatefulWidget {
|
||||
}) : assert(textAlign != null),
|
||||
assert(readOnly != null),
|
||||
assert(autofocus != null),
|
||||
assert(obscuringCharacter != null && obscuringCharacter.length == 1),
|
||||
assert(obscureText != null),
|
||||
assert(autocorrect != null),
|
||||
smartDashesType = smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled),
|
||||
@@ -428,6 +430,9 @@ class CupertinoTextField extends StatefulWidget {
|
||||
/// {@macro flutter.widgets.editableText.autofocus}
|
||||
final bool autofocus;
|
||||
|
||||
/// {@macro flutter.widgets.editableText.obscuringCharacter}
|
||||
final String obscuringCharacter;
|
||||
|
||||
/// {@macro flutter.widgets.editableText.obscureText}
|
||||
final bool obscureText;
|
||||
|
||||
@@ -601,6 +606,7 @@ class CupertinoTextField extends StatefulWidget {
|
||||
properties.add(DiagnosticsProperty<TextInputType>('keyboardType', keyboardType, defaultValue: TextInputType.text));
|
||||
properties.add(DiagnosticsProperty<TextStyle>('style', style, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<bool>('autofocus', autofocus, defaultValue: false));
|
||||
properties.add(DiagnosticsProperty<String>('obscuringCharacter', obscuringCharacter, defaultValue: '•'));
|
||||
properties.add(DiagnosticsProperty<bool>('obscureText', obscureText, defaultValue: false));
|
||||
properties.add(DiagnosticsProperty<bool>('autocorrect', autocorrect, defaultValue: true));
|
||||
properties.add(EnumProperty<SmartDashesType>('smartDashesType', smartDashesType, defaultValue: obscureText ? SmartDashesType.disabled : SmartDashesType.enabled));
|
||||
@@ -921,6 +927,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
|
||||
strutStyle: widget.strutStyle,
|
||||
textAlign: widget.textAlign,
|
||||
autofocus: widget.autofocus,
|
||||
obscuringCharacter: widget.obscuringCharacter,
|
||||
obscureText: widget.obscureText,
|
||||
autocorrect: widget.autocorrect,
|
||||
smartDashesType: widget.smartDashesType,
|
||||
|
||||
@@ -318,6 +318,7 @@ class TextField extends StatefulWidget {
|
||||
ToolbarOptions toolbarOptions,
|
||||
this.showCursor,
|
||||
this.autofocus = false,
|
||||
this.obscuringCharacter = '•',
|
||||
this.obscureText = false,
|
||||
this.autocorrect = true,
|
||||
SmartDashesType smartDashesType,
|
||||
@@ -350,6 +351,7 @@ class TextField extends StatefulWidget {
|
||||
}) : assert(textAlign != null),
|
||||
assert(readOnly != null),
|
||||
assert(autofocus != null),
|
||||
assert(obscuringCharacter != null && obscuringCharacter.length == 1),
|
||||
assert(obscureText != null),
|
||||
assert(autocorrect != null),
|
||||
smartDashesType = smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled),
|
||||
@@ -476,6 +478,9 @@ class TextField extends StatefulWidget {
|
||||
/// {@macro flutter.widgets.editableText.autofocus}
|
||||
final bool autofocus;
|
||||
|
||||
/// {@macro flutter.widgets.editableText.obscuringCharacter}
|
||||
final String obscuringCharacter;
|
||||
|
||||
/// {@macro flutter.widgets.editableText.obscureText}
|
||||
final bool obscureText;
|
||||
|
||||
@@ -727,6 +732,7 @@ class TextField extends StatefulWidget {
|
||||
properties.add(DiagnosticsProperty<TextInputType>('keyboardType', keyboardType, defaultValue: TextInputType.text));
|
||||
properties.add(DiagnosticsProperty<TextStyle>('style', style, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<bool>('autofocus', autofocus, defaultValue: false));
|
||||
properties.add(DiagnosticsProperty<String>('obscuringCharacter', obscuringCharacter, defaultValue: '•'));
|
||||
properties.add(DiagnosticsProperty<bool>('obscureText', obscureText, defaultValue: false));
|
||||
properties.add(DiagnosticsProperty<bool>('autocorrect', autocorrect, defaultValue: true));
|
||||
properties.add(EnumProperty<SmartDashesType>('smartDashesType', smartDashesType, defaultValue: obscureText ? SmartDashesType.disabled : SmartDashesType.enabled));
|
||||
@@ -1021,6 +1027,7 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
|
||||
textAlign: widget.textAlign,
|
||||
textDirection: widget.textDirection,
|
||||
autofocus: widget.autofocus,
|
||||
obscuringCharacter: widget.obscuringCharacter,
|
||||
obscureText: widget.obscureText,
|
||||
autocorrect: widget.autocorrect,
|
||||
smartDashesType: widget.smartDashesType,
|
||||
|
||||
@@ -146,6 +146,7 @@ class TextFormField extends FormField<String> {
|
||||
bool readOnly = false,
|
||||
ToolbarOptions toolbarOptions,
|
||||
bool showCursor,
|
||||
String obscuringCharacter = '•',
|
||||
bool obscureText = false,
|
||||
bool autocorrect = true,
|
||||
SmartDashesType smartDashesType,
|
||||
@@ -177,6 +178,7 @@ class TextFormField extends FormField<String> {
|
||||
assert(textAlign != null),
|
||||
assert(autofocus != null),
|
||||
assert(readOnly != null),
|
||||
assert(obscuringCharacter != null && obscuringCharacter.length == 1),
|
||||
assert(obscureText != null),
|
||||
assert(autocorrect != null),
|
||||
assert(enableSuggestions != null),
|
||||
@@ -230,6 +232,7 @@ class TextFormField extends FormField<String> {
|
||||
toolbarOptions: toolbarOptions,
|
||||
readOnly: readOnly,
|
||||
showCursor: showCursor,
|
||||
obscuringCharacter: obscuringCharacter,
|
||||
obscureText: obscureText,
|
||||
autocorrect: autocorrect,
|
||||
smartDashesType: smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled),
|
||||
|
||||
@@ -213,6 +213,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
bool readOnly = false,
|
||||
bool forceLine = true,
|
||||
TextWidthBasis textWidthBasis = TextWidthBasis.parent,
|
||||
String obscuringCharacter = '•',
|
||||
bool obscureText = false,
|
||||
Locale locale,
|
||||
double cursorWidth = 1.0,
|
||||
@@ -247,6 +248,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
assert(ignorePointer != null),
|
||||
assert(textWidthBasis != null),
|
||||
assert(paintCursorAboveText != null),
|
||||
assert(obscuringCharacter != null && obscuringCharacter.length == 1),
|
||||
assert(obscureText != null),
|
||||
assert(textSelectionDelegate != null),
|
||||
assert(cursorWidth != null && cursorWidth >= 0.0),
|
||||
@@ -284,6 +286,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
_selectionWidthStyle = selectionWidthStyle,
|
||||
_startHandleLayerLink = startHandleLayerLink,
|
||||
_endHandleLayerLink = endHandleLayerLink,
|
||||
_obscuringCharacter = obscuringCharacter,
|
||||
_obscureText = obscureText,
|
||||
_readOnly = readOnly,
|
||||
_forceLine = forceLine,
|
||||
@@ -295,9 +298,6 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
_promptRectPaint.color = promptRectColor;
|
||||
}
|
||||
|
||||
/// Character used to obscure text if [obscureText] is true.
|
||||
static const String obscuringCharacter = '•';
|
||||
|
||||
/// Called when the selection changes.
|
||||
///
|
||||
/// If this is null, then selection changes will be ignored.
|
||||
@@ -344,6 +344,20 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
markNeedsTextLayout();
|
||||
}
|
||||
|
||||
/// Character used for obscuring text if [obscureText] is true.
|
||||
///
|
||||
/// Cannot be null, and must have a length of exactly one.
|
||||
String get obscuringCharacter => _obscuringCharacter;
|
||||
String _obscuringCharacter;
|
||||
set obscuringCharacter(String value) {
|
||||
if (_obscuringCharacter == value) {
|
||||
return;
|
||||
}
|
||||
assert(value != null && value.length == 1);
|
||||
_obscuringCharacter = value;
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
/// Whether to hide the text being edited (e.g., for passwords).
|
||||
bool get obscureText => _obscureText;
|
||||
bool _obscureText;
|
||||
|
||||
@@ -357,6 +357,7 @@ class EditableText extends StatefulWidget {
|
||||
@required this.controller,
|
||||
@required this.focusNode,
|
||||
this.readOnly = false,
|
||||
this.obscuringCharacter = '•',
|
||||
this.obscureText = false,
|
||||
this.autocorrect = true,
|
||||
SmartDashesType smartDashesType,
|
||||
@@ -413,6 +414,7 @@ class EditableText extends StatefulWidget {
|
||||
this.autofillHints,
|
||||
}) : assert(controller != null),
|
||||
assert(focusNode != null),
|
||||
assert(obscuringCharacter != null && obscuringCharacter.length == 1),
|
||||
assert(obscureText != null),
|
||||
assert(autocorrect != null),
|
||||
smartDashesType = smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled),
|
||||
@@ -464,11 +466,20 @@ class EditableText extends StatefulWidget {
|
||||
/// Controls whether this widget has keyboard focus.
|
||||
final FocusNode focusNode;
|
||||
|
||||
/// {@template flutter.widgets.editableText.obscuringCharacter}
|
||||
/// Character used for obscuring text if [obscureText] is true.
|
||||
///
|
||||
/// Must be only a single character.
|
||||
///
|
||||
/// Defaults to the character U+2022 BULLET (•).
|
||||
/// {@endtemplate}
|
||||
final String obscuringCharacter;
|
||||
|
||||
/// {@template flutter.widgets.editableText.obscureText}
|
||||
/// Whether to hide the text being edited (e.g., for passwords).
|
||||
///
|
||||
/// When this is set to true, all the characters in the text field are
|
||||
/// replaced by U+2022 BULLET characters (•).
|
||||
/// replaced by [obscuringCharacter].
|
||||
///
|
||||
/// Defaults to false. Cannot be null.
|
||||
/// {@endtemplate}
|
||||
@@ -2029,6 +2040,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
||||
textDirection: _textDirection,
|
||||
locale: widget.locale,
|
||||
textWidthBasis: widget.textWidthBasis,
|
||||
obscuringCharacter: widget.obscuringCharacter,
|
||||
obscureText: widget.obscureText,
|
||||
autocorrect: widget.autocorrect,
|
||||
smartDashesType: widget.smartDashesType,
|
||||
@@ -2063,7 +2075,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
||||
TextSpan buildTextSpan() {
|
||||
if (widget.obscureText) {
|
||||
String text = _value.text;
|
||||
text = RenderEditable.obscuringCharacter * text.length;
|
||||
text = widget.obscuringCharacter * text.length;
|
||||
final int o =
|
||||
_obscureShowCharTicksPending > 0 ? _obscureLatestCharIndex : null;
|
||||
if (o != null && o >= 0 && o < text.length)
|
||||
@@ -2101,6 +2113,7 @@ class _Editable extends LeafRenderObjectWidget {
|
||||
this.textAlign,
|
||||
@required this.textDirection,
|
||||
this.locale,
|
||||
this.obscuringCharacter,
|
||||
this.obscureText,
|
||||
this.autocorrect,
|
||||
this.smartDashesType,
|
||||
@@ -2144,6 +2157,7 @@ class _Editable extends LeafRenderObjectWidget {
|
||||
final TextAlign textAlign;
|
||||
final TextDirection textDirection;
|
||||
final Locale locale;
|
||||
final String obscuringCharacter;
|
||||
final bool obscureText;
|
||||
final TextWidthBasis textWidthBasis;
|
||||
final bool autocorrect;
|
||||
@@ -2192,6 +2206,7 @@ class _Editable extends LeafRenderObjectWidget {
|
||||
onSelectionChanged: onSelectionChanged,
|
||||
onCaretChanged: onCaretChanged,
|
||||
ignorePointer: rendererIgnoresPointer,
|
||||
obscuringCharacter: obscuringCharacter,
|
||||
obscureText: obscureText,
|
||||
textWidthBasis: textWidthBasis,
|
||||
cursorWidth: cursorWidth,
|
||||
@@ -2234,6 +2249,7 @@ class _Editable extends LeafRenderObjectWidget {
|
||||
..onCaretChanged = onCaretChanged
|
||||
..ignorePointer = rendererIgnoresPointer
|
||||
..textWidthBasis = textWidthBasis
|
||||
..obscuringCharacter = obscuringCharacter
|
||||
..obscureText = obscureText
|
||||
..cursorWidth = cursorWidth
|
||||
..cursorRadius = cursorRadius
|
||||
|
||||
@@ -2281,7 +2281,7 @@ void main() {
|
||||
),
|
||||
));
|
||||
|
||||
const String expectedValue = '••••••••••••••••••••••••';
|
||||
final String expectedValue = '•' * originalText.length;
|
||||
|
||||
expect(
|
||||
semantics,
|
||||
@@ -2368,6 +2368,27 @@ void main() {
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
testWidgets('password fields can have their obscuring character customized', (WidgetTester tester) async {
|
||||
const String originalText = 'super-secret-password!!1';
|
||||
controller.text = originalText;
|
||||
|
||||
const String obscuringCharacter = '#';
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: EditableText(
|
||||
backgroundCursorColor: Colors.grey,
|
||||
controller: controller,
|
||||
obscuringCharacter: obscuringCharacter,
|
||||
obscureText: true,
|
||||
focusNode: focusNode,
|
||||
style: textStyle,
|
||||
cursorColor: cursorColor,
|
||||
),
|
||||
));
|
||||
|
||||
final String expectedValue = obscuringCharacter * originalText.length;
|
||||
expect(findRenderEditable(tester).text.text, expectedValue);
|
||||
});
|
||||
|
||||
group('a11y copy/cut/paste', () {
|
||||
Future<void> _buildApp(MockTextSelectionControls controls, WidgetTester tester) {
|
||||
return tester.pumpWidget(MaterialApp(
|
||||
|
||||
Reference in New Issue
Block a user