diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart index 6bba4a9326..51e67ecf9a 100644 --- a/packages/flutter/lib/src/rendering/editable.dart +++ b/packages/flutter/lib/src/rendering/editable.dart @@ -589,6 +589,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin, return; } _obscureText = value; + _cachedAttributedValue = null; markNeedsSemanticsUpdate(); } diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index b00b52ec76..d7d16b0704 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -6119,6 +6119,60 @@ void main() { semantics.dispose(); }); + // Regressing test for https://github.com/flutter/flutter/issues/99763 + testWidgets('Update textField semantics when obscureText changes', (WidgetTester tester) async { + final SemanticsTester semantics = SemanticsTester(tester); + final TextEditingController controller = TextEditingController(); + await tester.pumpWidget(_ObscureTextTestWidget(controller: controller)); + + controller.text = 'Hello'; + await tester.pump(); + + expect( + semantics, + includesNodeWith( + actions: [SemanticsAction.tap], + textDirection: TextDirection.ltr, + flags: [ + SemanticsFlag.isTextField, + ], + value: 'Hello', + ) + ); + + await tester.tap(find.byType(ElevatedButton)); + await tester.pump(); + + expect( + semantics, + includesNodeWith( + actions: [SemanticsAction.tap], + textDirection: TextDirection.ltr, + flags: [ + SemanticsFlag.isTextField, + SemanticsFlag.isObscured, + ], + ) + ); + + await tester.tap(find.byType(ElevatedButton)); + await tester.pump(); + + expect( + semantics, + includesNodeWith( + actions: [SemanticsAction.tap], + textDirection: TextDirection.ltr, + flags: [ + SemanticsFlag.isTextField, + ], + value: 'Hello', + ) + ); + + semantics.dispose(); + }); + testWidgets('TextField semantics, enableInteractiveSelection = false', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); final TextEditingController controller = TextEditingController(); @@ -12204,3 +12258,40 @@ void main() { }); }); } + +/// A Simple widget for testing the obscure text. +class _ObscureTextTestWidget extends StatefulWidget { + const _ObscureTextTestWidget({ required this.controller }); + + final TextEditingController controller; + @override + _ObscureTextTestWidgetState createState() => _ObscureTextTestWidgetState(); +} + +class _ObscureTextTestWidgetState extends State<_ObscureTextTestWidget> { + + bool _obscureText = false; + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + body: Builder( + builder: (_) { + return Column( + children: [ + TextField( + obscureText: _obscureText, + controller: widget.controller, + ), + ElevatedButton( + onPressed: () => setState(() {_obscureText = !_obscureText;}), + child: const SizedBox.shrink(), + ), + ], + ); + }, + ), + ), + ); + } +}