How to unfocus TextField that has custom FocusNode?
FlutterFlutter Problem Overview
I know that general answer to unfocusing is to use this piece of code: FocusScope.of(context).requestFocus(new FocusNode());
But when TextField has custom focusNode, this code doesn't seem to work.
SystemChannels.textInput.invokeMethod('TextInput.hide');
still works, but it only removes the keyboard - field itself is still selected.
The code (irrelevant parts removed):
class RegisterScreen extends StatelessWidget {
final phoneNumberTEC = TextEditingController();
final passwordTEC = TextEditingController();
final passwordFocusNode = FocusNode();
@override
Widget build(BuildContext context) {
return this.keyboardDismisser(
context: context,
child: Scaffold(
appBar: new AppBar(
title: new Text("Register"),
),
body: this.page(context),
resizeToAvoidBottomPadding: false,
),
);
}
Widget keyboardDismisser({BuildContext context, Widget child}) {
final gesture = GestureDetector(
onTap: () {
this.passwordFocusNode.unfocus();
FocusScope.of(context).requestFocus(new FocusNode());
SystemChannels.textInput.invokeMethod('TextInput.hide');
},
child: child,
);
return gesture;
}
Widget page(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
padding: new EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
this.phoneNumberTextField(context),
this.passwordTextField(context),
]
),
),
// cutting irrelevant widgets out
)
]
);
}
Widget phoneNumberTextField(BuildContext context) {
return TextField(
controller: this.phoneNumberTEC,
decoration: InputDecoration(hintText: "Phone number"),
onSubmitted: (string) {
FocusScope.of(context).requestFocus(this.passwordFocusNode);
},
);
}
Widget passwordTextField(BuildContext context) {
return TextField(
controller: this.passwordTEC,
decoration: InputDecoration(hintText: "Password"),
obscureText: true,
focusNode: this.passwordFocusNode,
onSubmitted: (string) {
this.performRegister(context);
},
);
}
}
Flutter Solutions
Solution 1 - Flutter
Here is a similar answer to @kasiara's answer but another way.
FocusScope.of(context).unfocus();
_textEditingController.clear();
Solution 2 - Flutter
In my case I had a TextFormField
which I wanted to remove focus from and hide the keyboard if possible. The following code does both and clears TextField content.
FocusScope.of(context).requestFocus(new FocusNode()); //remove focus
WidgetsBinding.instance.addPostFrameCallback((_) => _textEditingController.clear()); // clear content
where _textEditingController is just a TextEditingController(text: "")
held in widget's State.
Solution 3 - Flutter
similar to @Blasanka , but more shorter. Put this inside a GestureDetector that wraps the whole page.
onTap: () {
FocusScope.of(context).unfocus();
new TextEditingController().clear();
},
Solution 4 - Flutter
This works for the new Flutter version:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return Listener(
onPointerDown: (_) {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.focusedChild?.unfocus();
}
},
child: MaterialApp(
title: 'Flutter Demo',
home: Scaffold(body: SafeArea(child: TextField())),
),
);
}
}
Solution 5 - Flutter
you may have seen some people use this approach:
FocusScope.of(context).requestFocus(new FocusNode());
in this approach you are creating a FocusNode on the fly and not disposing it. while this also works but it's not recommended because FocusNode's are persistence objects and you should dispose them to avoid memory leaks:
@override
void dispose() {
focusNode.dispose();
super.dispose();
}
check out this link. the author has great explanation of how to do it in the best and right way.have a nice day:)
Solution 6 - Flutter
Comes out, I didn't manage the lifecycle of FocusNode properly: https://flutter.io/docs/cookbook/forms/focus
Thus, following code did work for me:
class RegisterScreen extends StatefulWidget {
@override
_RegisterScreenState createState() => _RegisterScreenState();
}
class _RegisterScreenState extends State<RegisterScreen> {
final phoneNumberTEC = TextEditingController();
final passwordTEC = TextEditingController();
FocusNode passwordFocusNode;
@override
void initState() {
super.initState();
this.passwordFocusNode = FocusNode();
}
@override
void dispose() {
this.passwordFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return this.keyboardDismisser(
context: context,
child: Scaffold(
appBar: new AppBar(
title: new Text("Register"),
),
body: this.page(context),
resizeToAvoidBottomPadding: false,
),
);
}
Widget keyboardDismisser({BuildContext context, Widget child}) {
final gesture = GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
debugPrint("!!!");
},
child: child,
);
return gesture;
}
// ...
}
Thanks for @SnakeyHips for help - inability to reproduce the issue when it was clearly reproducible on my side gave me some thoughts :)
Solution 7 - Flutter
Wrap the page Root Widget with GestureDetector Widget. That's work for me
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
new TextEditingController().clear();
},
child: _yourPageRootWidget(..)
)
}
Solution 8 - Flutter
just wrap your scaffold with GestureDetector,then put
FocusScope.of(context).unfocus();
inside the onTap Function
Solution 9 - Flutter
@override
Widget build(BuildContext context) {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
return Material(
child: Scaffold(
Only add new function(page) header , ( back main screen auto close keyboard )