Repository navigation
Not possible to de-focus a text field without clicking another focusable widget #82515
Description
Consider a page with a focusable text field, such as the gallery. After focusing the text field, I would expect that one the web or desktop I could click outside the text field area to remove focus. However this is not possible today, I need to click another focusable widget such as a text field or leave the page.
Aside from platform adaoption, this makes it difficult or impossible to use the Shortcuts widget to on a page with text fields. The Shortcuts may not be able to gain focus again after the text field has been focused.
Since the text fields cannot receive tap events that don't hit them (in order to dispose of focus), the solution requires a new widget be introduced that can intercept focus from the text fields.
Solution: The parent focus trap. A widget introduced at either the app level or route level. On tap events, it always requests focus. This will only be granted if the tap does not hit a more specific focus node. Draft:
class FocusTrap extends StatefulWidget {
/// Create a new [FocusTrap] widget.
const FocusTrap({
required this.child,
this.focusNode,
Key? key
}) : super(key: key);
final Widget child;
final FocusNode? focusNode;
@override
State<FocusTrap> createState() => _FocusTrapState();
}
class _FocusTrapState extends State<FocusTrap> {
final Map<Type, GestureRecognizerFactory> _gestures = <Type, GestureRecognizerFactory>{};
late FocusNode _focusNode;
FocusNode get focusNode => widget.focusNode ?? _focusNode;
void _stealFocus(TapDownDetails details) {
if (details.kind != PointerDeviceKind.mouse)
return;
focusNode.requestFocus();
}
@override
void initState() {
super.initState();
_focusNode = FocusNode();
_gestures[TapGestureRecognizer] = GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
() => TapGestureRecognizer(debugOwner: this),
(TapGestureRecognizer instance) {
instance
.onTapDown = _stealFocus;
},
);
}
@override
Widget build(BuildContext context) {
return Focus(
autofocus: true,
child: RawGestureDetector(
child: widget.child,
gestures: _gestures,
),
focusNode: focusNode,
);
}
}
And currently introduced in the same place I was putting SelectionArea (since this is needed to make Ctrl+C and other shortcuts work reliably)
@@ -10,6 +10,7 @@ import 'package:flutter/scheduler.dart';
import 'package:flutter/semantics.dart';
import 'actions.dart';
+import 'base_focus.dart';
import 'basic.dart';
import 'focus_manager.dart';
import 'focus_scope.dart';
@@ -841,11 +842,11 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
key: widget.route._subtreeKey, // immutable
child: Builder(
builder: (BuildContext context) {
- return widget.route.buildPage(
+ return FocusTrap(child: widget.route.buildPage(
context,
widget.route.animation!,
widget.route.secondaryAnimation!,
- );
+ ));
},
),
),new behavior:
Flutter.Gallery.-.Google.Chrome.2021-05-13.20-56-00.mp4
blocks: #81839
Activity
jonahwilliams commentedon May 14, 2021
cc @chunhtai @gspencergoog
jonahwilliams commentedon May 14, 2021
and here is the current behavior: I am clicking a ton:
Flutter.Gallery.-.Google.Chrome.2021-05-13.21-01-47.mp4
SpajicM commentedon May 14, 2021
#32433
HansMuller commentedon May 14, 2021
CC @LongCatIsLooong
pedromassango commentedon Jun 5, 2021
Nice to see this issue getting attention from the team again :)
I believe this case should be coved as well:
#72029 - Selection toolbar/handler should disappear when it is opened and user clicks outside of it
@jonahwilliams will this be the default behavior? I hope it does.
jonahwilliams commentedon Jun 7, 2021
I don't know what the right behavior is for mixed pointer and mouse selection. I think that would likely be a different issue
pedromassango commentedon Jun 7, 2021
The expected behavior is to remove the selection toolbar. I've shared more about that in #72029 (see
Side Note - issue /2section).github-actions commentedon Jul 31, 2021
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.