diff --git a/packages/flutter/lib/src/widgets/focus_manager.dart b/packages/flutter/lib/src/widgets/focus_manager.dart index 24cae56d862..bf4cc7552d4 100644 --- a/packages/flutter/lib/src/widgets/focus_manager.dart +++ b/packages/flutter/lib/src/widgets/focus_manager.dart @@ -432,7 +432,7 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier { /// arguments must not be null. FocusNode({ String? debugLabel, - FocusOnKeyCallback? onKey, + this.onKey, bool skipTraversal = false, bool canRequestFocus = true, bool descendantsAreFocusable = true, @@ -441,8 +441,7 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier { assert(descendantsAreFocusable != null), _skipTraversal = skipTraversal, _canRequestFocus = canRequestFocus, - _descendantsAreFocusable = descendantsAreFocusable, - _onKey = onKey { + _descendantsAreFocusable = descendantsAreFocusable { // Set it via the setter so that it does nothing on release builds. this.debugLabel = debugLabel; } @@ -573,8 +572,7 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier { /// [hasFocus] returns true). /// /// {@macro flutter.widgets.FocusNode.keyEvents} - FocusOnKeyCallback? get onKey => _onKey; - FocusOnKeyCallback? _onKey; + FocusOnKeyCallback? onKey; FocusManager? _manager; List? _ancestors; @@ -1028,7 +1026,7 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier { @mustCallSuper FocusAttachment attach(BuildContext? context, {FocusOnKeyCallback? onKey}) { _context = context; - _onKey = onKey ?? _onKey; + this.onKey = onKey ?? this.onKey; _attachment = FocusAttachment._(this); return _attachment!; } diff --git a/packages/flutter/lib/src/widgets/focus_scope.dart b/packages/flutter/lib/src/widgets/focus_scope.dart index ce4520673a8..fdc50c25aaa 100644 --- a/packages/flutter/lib/src/widgets/focus_scope.dart +++ b/packages/flutter/lib/src/widgets/focus_scope.dart @@ -643,6 +643,9 @@ class _FocusState extends State { }()); if (oldWidget.focusNode == widget.focusNode) { + if (widget.onKey != focusNode.onKey) { + focusNode.onKey = widget.onKey; + } if (widget.skipTraversal != null) { focusNode.skipTraversal = widget.skipTraversal!; } diff --git a/packages/flutter/test/widgets/focus_scope_test.dart b/packages/flutter/test/widgets/focus_scope_test.dart index a02c692d8dd..4b378af2bc2 100644 --- a/packages/flutter/test/widgets/focus_scope_test.dart +++ b/packages/flutter/test/widgets/focus_scope_test.dart @@ -4,9 +4,10 @@ import 'dart:ui'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter/semantics.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'semantics_tester.dart'; @@ -1593,6 +1594,37 @@ void main() { final TestSemantics expectedSemantics = TestSemantics.root(); expect(semantics, hasSemantics(expectedSemantics)); }); + testWidgets('Focus updates the onKey handler when the widget updates', (WidgetTester tester) async { + final GlobalKey key1 = GlobalKey(debugLabel: '1'); + final FocusNode focusNode = FocusNode(); + bool? keyEventHandled; + await tester.pumpWidget( + Focus( + onKey: (_, __) => true, // This one does nothing. + focusNode: focusNode, + child: Container(key: key1), + ), + ); + + Focus.of(key1.currentContext!).requestFocus(); + await tester.pump(); + await tester.sendKeyEvent(LogicalKeyboardKey.enter); + expect(keyEventHandled, isNull); + + await tester.pumpWidget( + Focus( + onKey: (FocusNode node, RawKeyEvent event) { // The updated handler handles the key. + keyEventHandled = true; + return true; + }, + focusNode: focusNode, + child: Container(key: key1), + ), + ); + + await tester.sendKeyEvent(LogicalKeyboardKey.enter); + expect(keyEventHandled, isTrue); + }); }); group('ExcludeFocus', () { testWidgets("Descendants of ExcludeFocus aren't focusable.", (WidgetTester tester) async {