From 9cda30923fd6ba4819cf990dbe4bba7b6544c505 Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Wed, 2 Aug 2023 14:16:46 -0700 Subject: [PATCH] Replace TextField.canRequestFocus with TextField.focusNode.canRequestFocus (#130164) Simplifying the TextField API. --- .../lib/src/material/dropdown_menu.dart | 23 +++++++++++++++++-- .../flutter/lib/src/material/text_field.dart | 20 ++++++++++++---- .../test/material/dropdown_menu_test.dart | 7 +++--- .../test/material/text_field_test.dart | 5 +++- 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/packages/flutter/lib/src/material/dropdown_menu.dart b/packages/flutter/lib/src/material/dropdown_menu.dart index 51a972a3398a..53003486f6b1 100644 --- a/packages/flutter/lib/src/material/dropdown_menu.dart +++ b/packages/flutter/lib/src/material/dropdown_menu.dart @@ -4,6 +4,7 @@ import 'dart:math' as math; +import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; @@ -309,6 +310,7 @@ class _DropdownMenuState extends State> { int? currentHighlight; double? leadingPadding; bool _menuHasEnabledItem = false; + late final FocusNode _focusNode; @override void initState() { @@ -326,6 +328,18 @@ class _DropdownMenuState extends State> { TextSelection.collapsed(offset: _textEditingController.text.length); } refreshLeadingPadding(); + _focusNode = FocusNode( + canRequestFocus: canRequestFocus(), + ); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + final bool widgetCanRequestFocus = canRequestFocus(); + if (widgetCanRequestFocus != _focusNode.canRequestFocus) { + _focusNode.canRequestFocus = widgetCanRequestFocus; + } } @override @@ -353,6 +367,10 @@ class _DropdownMenuState extends State> { TextSelection.collapsed(offset: _textEditingController.text.length); } } + final bool widgetCanRequestFocus = canRequestFocus(); + if (widgetCanRequestFocus != _focusNode.canRequestFocus) { + _focusNode.canRequestFocus = widgetCanRequestFocus; + } } bool canRequestFocus() { @@ -360,7 +378,7 @@ class _DropdownMenuState extends State> { return widget.requestFocusOnTap!; } - switch (Theme.of(context).platform) { + switch (defaultTargetPlatform) { case TargetPlatform.iOS: case TargetPlatform.android: case TargetPlatform.fuchsia: @@ -592,7 +610,8 @@ class _DropdownMenuState extends State> { final Widget textField = TextField( key: _anchorKey, mouseCursor: effectiveMouseCursor, - canRequestFocus: canRequestFocus(), + focusNode: _focusNode, + readOnly: !canRequestFocus(), enableInteractiveSelection: canRequestFocus(), textAlignVertical: TextAlignVertical.center, style: effectiveTextStyle, diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart index aa8f715bf905..7d6ea5b865ac 100644 --- a/packages/flutter/lib/src/material/text_field.dart +++ b/packages/flutter/lib/src/material/text_field.dart @@ -314,6 +314,10 @@ class TextField extends StatefulWidget { this.scribbleEnabled = true, this.enableIMEPersonalizedLearning = true, this.contextMenuBuilder = _defaultContextMenuBuilder, + @Deprecated( + 'Use `focusNode` instead. ' + 'This feature was deprecated after v3.12.0-14.0.pre.', + ) this.canRequestFocus = true, this.spellCheckConfiguration, this.magnifierConfiguration, @@ -776,6 +780,10 @@ class TextField extends StatefulWidget { /// 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. + @Deprecated( + 'Use `focusNode` instead. ' + 'This feature was deprecated after v3.12.0-14.0.pre.', + ) final bool canRequestFocus; /// {@macro flutter.widgets.undoHistory.controller} @@ -1026,7 +1034,9 @@ class _TextFieldState extends State with RestorationMixin implements if (widget.controller == null) { _createLocalController(); } - _effectiveFocusNode.canRequestFocus = widget.canRequestFocus && _isEnabled; + _effectiveFocusNode.canRequestFocus = widget.focusNode == null + ? widget.canRequestFocus && _isEnabled + : widget.focusNode!.canRequestFocus && _isEnabled; _effectiveFocusNode.addListener(_handleFocusChanged); } @@ -1034,7 +1044,9 @@ class _TextFieldState extends State with RestorationMixin implements final NavigationMode mode = MediaQuery.maybeNavigationModeOf(context) ?? NavigationMode.traditional; switch (mode) { case NavigationMode.traditional: - return widget.canRequestFocus && _isEnabled; + return widget.focusNode == null + ? widget.canRequestFocus && _isEnabled + : widget.focusNode!.canRequestFocus && _isEnabled; case NavigationMode.directional: return true; } @@ -1086,8 +1098,8 @@ class _TextFieldState extends State with RestorationMixin implements void _createLocalController([TextEditingValue? value]) { assert(_controller == null); _controller = value == null - ? RestorableTextEditingController() - : RestorableTextEditingController.fromValue(value); + ? RestorableTextEditingController() + : RestorableTextEditingController.fromValue(value); if (!restorePending) { _registerController(); } diff --git a/packages/flutter/test/material/dropdown_menu_test.dart b/packages/flutter/test/material/dropdown_menu_test.dart index a721cfdb87a2..898c835f9272 100644 --- a/packages/flutter/test/material/dropdown_menu_test.dart +++ b/packages/flutter/test/material/dropdown_menu_test.dart @@ -1232,7 +1232,7 @@ void main() { final Finder textFieldFinder = find.byType(TextField); final TextField result = tester.widget(textFieldFinder); - expect(result.canRequestFocus, false); + expect(result.focusNode!.canRequestFocus, false); }, variant: TargetPlatformVariant.mobile()); testWidgets('The text input field should be focused on desktop platforms ' @@ -1300,7 +1300,7 @@ void main() { final Finder textFieldFinder1 = find.byType(TextField); final TextField textField1 = tester.widget(textFieldFinder1); - expect(textField1.canRequestFocus, false); + expect(textField1.focusNode!.canRequestFocus, false); // Open the dropdown menu. await tester.tap(textFieldFinder1); await tester.pump(); @@ -1329,7 +1329,7 @@ void main() { final Finder textFieldFinder = find.byType(TextField); final TextField textField = tester.widget(textFieldFinder); - expect(textField.canRequestFocus, false); + expect(textField.focusNode!.canRequestFocus, false); final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 1); await gesture.moveTo(tester.getCenter(textFieldFinder)); @@ -1526,7 +1526,6 @@ void main() { // Item 5 should show up. expect(find.text('Item 5').hitTestable(), findsOneWidget); }); - } enum TestMenu { diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index 3998f9750032..8339d81cd902 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -15568,6 +15568,7 @@ void main() { expect(focusNode.hasFocus, isTrue); // Set canRequestFocus to false: the text field cannot be focused when it is tapped/long pressed. + focusNode.canRequestFocus = false; await tester.pumpWidget( boilerplate( child: TextField( @@ -15749,7 +15750,9 @@ void main() { }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); testWidgets('Right clicking cannot request focus if canRequestFocus is false', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); + final FocusNode focusNode = FocusNode( + canRequestFocus: false, + ); final UniqueKey key = UniqueKey(); await tester.pumpWidget( MaterialApp(