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

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

Related

FocusNode should lose focus when clicking outside the widget

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 ?

Howto hide the keyboard in flutter when tapping outside of the TextField without triggering any other actions?

Hi I have the following (simplified) code:
class Example extends StatelessWidget {
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => FocusScope.of(context).focusedChild?.unfocus(),
child: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
print("Button pressed!");
},
child: Text("Click me"),
),
TextField(),
],
),
),
);
}
}
This code results in the following UI:
When I click on the blue button, "Button pressed!" gets printed and the keyboard does not disappear.
The problem here, is that this behavior is not what I want. I want that the keyboard disappears when I click somewhere outside of the TextField, and that the button does not get triggered, even when I directly click on it. So for example if I click on the button just the keyboard should disappear without any other action/side effect (nothing gets printed in this case). But it should still be possible to interact with the TextField normally.
Note: Disabling the button is not a good option since in my real case scenario the page is build out of a lot complex widgets and disabling them is really complicated.
Already stuck there for a while now. Hope you can help me :)
Wrap the Scaffold with Gesture detector and on tap unfocus the focus scope
example
class Test extends StatelessWidget {
const Test({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: const Scaffold(),
);
}
}
Wrap your Scaffold Widget in a Button it can be Gesture Detector or Inkwell. Then add unfocus method in it.
GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: const Scaffold(
body: TextFormField(
decoration: OutlineInputBorder()),
);
I also needed to hide the keyboard when I clicked on a date picker (another widget)
During my experience, with GestureDetector keyboard hid itself when I clicked somewhere that is not a widget. It would not hide itself otherwise.
Simply changing GestureDetector
to
Listener and adding
onPointerDown: (PointerDownEvent event) => FocusManager.instance.primaryFocus?.unfocus(), gave me the solution.
So, in your instance, what you should do is, wrap the Button "Click me" with a Listener and give it onPointerDown ,that should hide your keyboard whenever you press the button.

Flutter - How to take an action when the bottom modal sheet is dismissed?

I have a filters button. It's a container with a border, and inside are two icons in a row. All of this is wrapped in a gesture detector.
When the user taps the gesture detector, I am showing a bottom modal sheet. This all works. However, I want one of the icons to change depending on whether the user has the bottom modal sheet activated or not. How can I achieve this?
I think I can call setState once the button is tapped, BEFORE showing the modal bottom sheet. How can call setState again when the user taps out of it?
Thanks!
Is there an option to configure this in the showModalBottomSheet constructor?
As you did not post any code I'm guessing that you are using the method showModalBottomSheet.
This method has the following prototype:
Future<T?> showModalBottomSheet<T>({
required BuildContext context,
required WidgetBuilder builder,
Color? backgroundColor,
double? elevation,
ShapeBorder? shape,
Clip? clipBehavior,
BoxConstraints? constraints,
Color? barrierColor,
bool isScrollControlled = false,
bool useRootNavigator = false,
bool isDismissible = true,
bool enableDrag = true,
RouteSettings? routeSettings,
AnimationController? transitionAnimationController,
})
source
As you can see it returns a Future<T?> type which means that you can apply await, then or whenComplete to the returned future operation. In your case using whenComplete might be the better option if you don't need any value from the bottom sheet.
Example
showModalBottomSheet<void>(
context: context,
builder: (context) {
return Container(
height: 200,
color: Colors.amber,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Modal BottomSheet'),
ElevatedButton(
child: const Text('Close BottomSheet'),
onPressed: () => Navigator.pop(context),
)
],
),
),
);
},
).whenComplete(_onBottomSheetClosed);
void _onBottomSheetClosed() {
print("Closed bottomsheet");
}
Try the full example on DartPad
I was searching how to dismiss bottom sheet after navigation through it to a second screen , I found out that bottom sheet gets also added to navigation stack so ,
just use pushReplacement:
Navigator.pushReplacement(
context,PageTransition(type: PageTransitionType.leftToRight, child: Page()));
hope this help someone.

Floating action button to be ontop of drawer after opening

I have a floating action button which opens the drawer. I want it to stay in its place when it opens the drawer and just change the icon to close icon with animation. I couldn't find a good solution to do this.
I tried wrapping inside my drawer with scaffold and adding a floating action button there and using hero widgets so make it look like it stays in its place but didn't help at all.
Is there anyway to achieve this?
U can use foldable Sidebar package to achieve this functionalities that's package is available on :
https://pub.dev/packages/foldable_sidebar
And check this piece of code how to use this :
child: Scaffold(
body: FoldableSidebarBuilder(
drawerBackgroundColor: Colors.deepOrange,
drawer: CustomDrawer(closeDrawer: (){
setState(() {
drawerStatus = FDBStatus.FDB_CLOSE; // For Closing the Sidebar
});
},),
screenContents: FirstScreen(), // Your Screen Widget
status: drawerStatus,
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.deepOrange,
child: Icon(Icons.menu,color: Colors.white,),
onPressed: () {
// To Open/Close Sidebar
setState(() {
drawerStatus = drawerStatus == FDBStatus.FDB_OPEN ? FDBStatus.FDB_CLOSE : FDBStatus.FDB_OPEN;
});
}),
),
),

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.