Unverified Commit 63f48d1f authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

[EditableText] honor the "brieflyShowPassword" system setting (#97769)

parent 9eb94c59
...@@ -1923,13 +1923,13 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -1923,13 +1923,13 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
hideToolbar(); hideToolbar();
_currentPromptRectRange = null; _currentPromptRectRange = null;
if (_hasInputConnection) { final bool revealObscuredInput = _hasInputConnection
if (widget.obscureText && value.text.length == _value.text.length + 1) { && widget.obscureText
_obscureShowCharTicksPending = _kObscureShowLatestCharCursorTicks; && WidgetsBinding.instance!.window.brieflyShowPassword
_obscureLatestCharIndex = _value.selection.baseOffset; && value.text.length == _value.text.length + 1;
}
}
_obscureShowCharTicksPending = revealObscuredInput ? _kObscureShowLatestCharCursorTicks : 0;
_obscureLatestCharIndex = revealObscuredInput ? _value.selection.baseOffset : null;
_formatAndSetValue(value, SelectionChangedCause.keyboard); _formatAndSetValue(value, SelectionChangedCause.keyboard);
} }
...@@ -2621,7 +2621,9 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -2621,7 +2621,9 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
if (_obscureShowCharTicksPending > 0) { if (_obscureShowCharTicksPending > 0) {
setState(() { setState(() {
_obscureShowCharTicksPending--; _obscureShowCharTicksPending = WidgetsBinding.instance!.window.brieflyShowPassword
? _obscureShowCharTicksPending - 1
: 0;
}); });
} }
} }
...@@ -3245,11 +3247,13 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -3245,11 +3247,13 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
String text = _value.text; String text = _value.text;
text = widget.obscuringCharacter * text.length; text = widget.obscuringCharacter * text.length;
// Reveal the latest character in an obscured field only on mobile. // Reveal the latest character in an obscured field only on mobile.
if (defaultTargetPlatform == TargetPlatform.android || const Set<TargetPlatform> mobilePlatforms = <TargetPlatform> {
defaultTargetPlatform == TargetPlatform.iOS || TargetPlatform.android, TargetPlatform.iOS, TargetPlatform.fuchsia,
defaultTargetPlatform == TargetPlatform.fuchsia) { };
final int? o = final bool breiflyShowPassword = WidgetsBinding.instance!.window.brieflyShowPassword
_obscureShowCharTicksPending > 0 ? _obscureLatestCharIndex : null; && mobilePlatforms.contains(defaultTargetPlatform);
if (breiflyShowPassword) {
final int? o = _obscureShowCharTicksPending > 0 ? _obscureLatestCharIndex : null;
if (o != null && o >= 0 && o < text.length) if (o != null && o >= 0 && o < text.length)
text = text.replaceRange(o, o + 1, _value.text.substring(o, o + 1)); text = text.replaceRange(o, o + 1, _value.text.substring(o, o + 1));
} }
......
...@@ -3611,6 +3611,72 @@ void main() { ...@@ -3611,6 +3611,72 @@ void main() {
expect((findRenderEditable(tester).text! as TextSpan).text, expectedValue); expect((findRenderEditable(tester).text! as TextSpan).text, expectedValue);
}); });
testWidgets('password briefly shows last character when entered on mobile', (WidgetTester tester) async {
final bool debugDeterministicCursor = EditableText.debugDeterministicCursor;
EditableText.debugDeterministicCursor = false;
addTearDown(() {
EditableText.debugDeterministicCursor = debugDeterministicCursor;
});
await tester.pumpWidget(MaterialApp(
home: EditableText(
backgroundCursorColor: Colors.grey,
controller: controller,
obscureText: true,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
),
));
await tester.enterText(find.byType(EditableText), 'AA');
await tester.pump();
await tester.enterText(find.byType(EditableText), 'AAA');
await tester.pump();
expect((findRenderEditable(tester).text! as TextSpan).text, '••A');
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 500));
expect((findRenderEditable(tester).text! as TextSpan).text, '•••');
});
testWidgets('password briefly does not show last character on Android if turned off', (WidgetTester tester) async {
final bool debugDeterministicCursor = EditableText.debugDeterministicCursor;
EditableText.debugDeterministicCursor = false;
addTearDown(() {
EditableText.debugDeterministicCursor = debugDeterministicCursor;
});
await tester.pumpWidget(MaterialApp(
home: EditableText(
backgroundCursorColor: Colors.grey,
controller: controller,
obscureText: true,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
),
));
await tester.enterText(find.byType(EditableText), 'AA');
await tester.pump();
await tester.enterText(find.byType(EditableText), 'AAA');
await tester.pump();
tester.binding.window.brieflyShowPasswordTestValue = false;
addTearDown(() {
tester.binding.window.brieflyShowPasswordTestValue = true;
});
expect((findRenderEditable(tester).text! as TextSpan).text, '••A');
await tester.pump(const Duration(milliseconds: 500));
expect((findRenderEditable(tester).text! as TextSpan).text, '•••');
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 500));
expect((findRenderEditable(tester).text! as TextSpan).text, '•••');
});
group('a11y copy/cut/paste', () { group('a11y copy/cut/paste', () {
Future<void> _buildApp(MockTextSelectionControls controls, WidgetTester tester) { Future<void> _buildApp(MockTextSelectionControls controls, WidgetTester tester) {
return tester.pumpWidget(MaterialApp( return tester.pumpWidget(MaterialApp(
......
...@@ -271,6 +271,15 @@ class TestWindow implements ui.SingletonFlutterWindow { ...@@ -271,6 +271,15 @@ class TestWindow implements ui.SingletonFlutterWindow {
platformDispatcher.onTextScaleFactorChanged = callback; platformDispatcher.onTextScaleFactorChanged = callback;
} }
@override
bool get brieflyShowPassword => _brieflyShowPasswordTestValue ?? platformDispatcher.brieflyShowPassword;
bool? _brieflyShowPasswordTestValue;
/// Hides the real [brieflyShowPassword] and reports the given
/// `brieflyShowPasswordTestValue` instead.
set brieflyShowPasswordTestValue(bool brieflyShowPasswordTestValue) { // ignore: avoid_setters_without_getters
_brieflyShowPasswordTestValue = brieflyShowPasswordTestValue;
}
@override @override
ui.FrameCallback? get onBeginFrame => platformDispatcher.onBeginFrame; ui.FrameCallback? get onBeginFrame => platformDispatcher.onBeginFrame;
@override @override
......
...@@ -127,6 +127,18 @@ void main() { ...@@ -127,6 +127,18 @@ void main() {
); );
}); });
testWidgets('TestWindow can fake brieflyShowPassword', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<bool>(
tester: tester,
realValue: ui.window.brieflyShowPassword,
fakeValue: !ui.window.brieflyShowPassword,
propertyRetriever: () => WidgetsBinding.instance!.window.brieflyShowPassword,
propertyFaker: (TestWidgetsFlutterBinding binding, bool fakeValue) {
binding.window.brieflyShowPasswordTestValue = fakeValue;
},
);
});
testWidgets('TestWindow can fake default route name', (WidgetTester tester) async { testWidgets('TestWindow can fake default route name', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<String>( verifyThatTestWindowCanFakeProperty<String>(
tester: tester, tester: tester,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment