FocusNode should lose focus when clicking outside the widget - flutter

does anyone know how to make a focus node loose focus when clicking outside the focus node ? Basically, I got something like this:
InkWell(
focusNode: focusNode,
child: ...,
onTap: () {
...
},
),
But the focusNode doesn't lose his focus when I'm clicking outside the InkWell widget.
I have tried to use the FocusTrap widget, which seems to do exactly that. But I can't get a focusScopeNode from my focusNode, all I get is null (I don't really understand how focusScopeNode works)
Note that I can't do something hacky like
GestureDetector(
onTap: () {
Focus.of(context).unfocus();
},
child: Scaffold(
// ...
),
);
Because I'm in a package context
Does anyone know how to do this ?

Related

Flutter keyboard pops up on an unwanted screen, because a different screen is initialized at the same time in a list

I know how to manually make the keyboard go away but that's now what I want, let me explain:
I have a BottomNavigationBar using which I can switch between 5 screens.
This is the code for the body:
body: GetBuilder<MainTabBarController>(builder: (_) {
return IndexedStack(
index: controller.pageIndex,
children: controller.tabPages,
);
}),
This is tabPages in the GetxController:
List<Widget> tabPages = [
HomeScreen(),
SearchScreen(),
const Placeholder(color: Colors.orange),
const Placeholder(color: Colors.green),
const Placeholder(color: Colors.indigo),
];
Now the thing is, my SearchScreen has a textfield with autofocus set to true, so that whenever I open the SearchScreen, the keyboard should popup automatically. But the problem that I am having here is that, as soon as the "tabPages" is initializes (which happens after the login as I come to the MainTabBarScreen() ), the SearchScreen also gets initialized and it brings up the keyboard even though I am only on the HomeScreen.
I hope I properly explained the issue here, let me know if anymore information is needed. Thank you!
The following code helped me to hide the keyboard.
#override
void initState() {
FocusManager.instance.primaryFocus?.unfocus();
super.initState();
}
Unfocused from current focus so that keyboard will dismiss.
You enter the above code in other screens except for SearchScreen().
Extra
GestureDetector(
onTap: () {
FocusScopeNode currentfocus = FocusScope.of(context);
if (!currentfocus.hasPrimaryFocus &&
currentfocus.focusedChild != null) {
FocusManager.instance.primaryFocus?.unfocus();
}
},
child: MaterialApp(),
)
Warp the MaterialApp with GestureDetector and enter the above code
I hope your problem will be solved. Thank you!
I removed the autofocus: true from the search-textfield and I added a focusNode to it which only gives the focus to the textfield once I select the search-screen from the bottom-navigation-bar

inkwell ontab does not work when clicking on the form

I use the searchField, judging by the documentation, there should be an onTab field in the searcField widget, but apparently the library has been updated and there is no such function anymore, since I need to trigger an event in the block when clicking on the field, I decided to wrap the widget in inkwell, but when clicked, nothing works and the event does not called
BlocBuilder<FillprofileBloc, FillprofileState>(
builder: (context, state) {
return InkWell(
focusColor: Colors.transparent,
onTap: () {
_bloc.add(OnCitySearchTabEvent());
},
child: SearchField(
searchInputDecoration: InputDecoration(
There is a workaround with GestureDetector.
GestureDetector(
behavior: HitTestBehavior.translucent,
onPanDown: (_) {
debugPrint("pan down");
},
child: SearchField(),
),
You can use onTap param of TextField itself here. Or wrap your TextField with IgnorePointer to use InkWell

How to make InkWell `onLongPress()` override tap on a TextField inside of it?

I have a TextField inside of an InkWell with an onLongPress() callback. The problem is, despite the fact that even when long pressing on the TextField, I see the ripple effect on InkWell, but the onLongPress() does not run after the long press time passes. It only gets me into editing Text. When pressing on the bottom side of the Card, everything runs fine.
In short: On tap I want to get into TextField editing. On long press I want to trigger the onLongPress(), not the TextField, even if I am pressing on it.
How do I do this? Thank you.
InkWell(
onLongPress: () {
// do stuff
}
child: ListTile(
title: TextField(),
),
),
You can use the AbsorbPointer widget to ignore the TextField gesture recognizer:
InkWell(
onLongPress: () {
print('onLongPress');
},
child: AbsorbPointer(
child: ListTile(
title: TextField(),
),
),
)
To still enabling the editing of TextField when single tapping on it, you can use FocusNode like this:
InkWell(
onLongPress: () {
print('onLongPress');
},
onTap: () => node.requestFocus(),
child: AbsorbPointer(
child: ListTile(
title: TextField(
focusNode: node,
controller: textController,
),
),
),
)
#Bach 's answer helped me to find a solution. Thank you!
InkWell(
onLongPress: () {
// do stuff
},
child: ListTile(
title: GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(_focusNode),
child: AbsorbPointer(
child: TextField(
focusNode: _focusNode,
),
),
),
),
The only problem is now that I started messing with focusNode, multiple input fiels are focusing at the same time. But that is a whole other story ;)
UPD: Just realised, that I can't move text cursor this way. So not useful.
It seems that IntrinsicWidth widget can find the right balance between long press and text editing.
The rationale behind is that IntrinsicWidth will let the TextField shrink to its minimum width, therefore avoiding a gesture collision with the InkWell
So your solution can be like this:
InkWell(
onLongPress: () {
// do stuff
}
child: ListTile(
child: IntrinsicWidth(
title: TextField(
//remember to make some hints here
//because with intrinsicwidth if your textfield is empty it might disappear
),
),
),
),

How to skip Flutter Widget in tap detection?

I'm trying to make a modal bottom sheet with ListTiles that contain a Checkbox as the leading widget. However, the Checkbox will receive tap event instead of the ListTile, now showing the ink ripple effect for the tile and making me implement two onTap/onChanged callbacks.
I have tried not implementing the Checkbox's onChanged callback and wrapping it around an AbsorbPointer widget, however in these two cases the ListTile would also not get the tap event.
return ListTile(
onTap: onTap,
title: Text("Show expired schedules"),
leading: Checkbox(value: snapshot.data, onChanged: (_) => onTap()),
);
I would like to somehow make the Checkbox not tappable, but have the ListTile still receive the tap event. Kind of like AbsorbPointer, except skipping one Widget, not absorbing the tap event entirely.
Wrap your Checkbox widget inside IgnorePointer widget and it should work:
return ListTile(
onTap: onTap,
title: Text("Show expired schedules"),
leading: IgnorePointer(child: Checkbox(value: snapshot.data, onChanged: (_) => onTap())),
);
You can do that using AbsorbPointer too
return ListTile(
onTap: onTap,
title: Text("Show expired schedules"),
leading: AbsorbPointer(
absorbing: _condition, // bool value, true makes it absorb touch event on CheckBox still making ListTile tappable.
child: Checkbox(value: snapshot.data, onChanged: (_) => onTap()),
),
);

Flutter disable touch on the entire screen

Is there any way to prevent my screen from receiving touch events, I don't want to disable touch for every Widget in my app. I just want to lock the app so that it doesn't receive touch events. How can I do that?
You can wrap your widget in AbsorbPointer and it won't receive touches. To enable the touch again, you can set absorbing: false
AbsorbPointer(
child: YourWidget(...),
);
Two way to do :
AbsorbPointer
IgnorePointer
Check difference with example here :
https://programmer.help/blogs/the-difference-between-flutter-absorbpointer-and-ignorepointer.html
Flutter AbsorbPointer vs IgnorePointer difference
Lets see a practical example of using IgnorePointer widget.
This case is pretty common when we started trying to implement something like toggling a selection on a widget to delete or something like this.
RESULT:
Example Senario :
Holding on a WhatsApp message and delete option coming on top. if tap anywhere else while this option active, it will go.
I implemented it like this.
appBar: AppBar(
title: Text('MyApp'),
actions: [
if (_selected != null) // <-- Delete button only appear something selected.
IconButton(
icon: Icon(Icons.delete),
onPressed: () {
// Delete Code here
}
]
),
body: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
print('Tapped');
setState(() { _selected = null });
},
child: IgnorePointer(
ignoring: _selected != null ? true : false, // <-- Ignore only when selecting something.
child: Column(
children: [
...
// This is a sample message
GestureDetector(
onLongPress: () {
setState(() { _selected = messageId });
}
child: Container(
child: Text('This is a message'),
),
...
Hope it will help somebody! Have a nice day.