Keyboard is still showing when changing the to other Tab in a TabBarView

Viewed 2174

In one tab I have a TextFormField and in the other only a list of texts. When I select the Text field the keyboard is open, then I jump to the second Tab and the keyboard is still displayed. I can even write and when I go back to the Tab 1 I see why I typed.

Do you know how can I give an action to the second Tab in order to take the focus out from the text field?

DefaultTabController(      
      length: 2,
      child: Scaffold(
          appBar: AppBar(
            title: Text('Manage Products'),
            bottom: TabBar(tabs: <Widget>[
              Tab(icon: Icon(Icons.create), text: 'Create Product'),
              Tab(icon: Icon(Icons.list), text: 'My Products'),
            ]),
          ),
          body: TabBarView(            
            children: <Widget>[
              ProductEditPage(addProduct: addProduct),
              ProductListPage(products, updateProduct),
            ],
          )),
    );

Tab1

Tab2

SOLVING CODE

After applying @nick.tdr suggestion an example code can be as follow:

class _Test extends State<Test> with TickerProviderStateMixin {
  TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(vsync: this, length: 2);
    _tabController.addListener(() {
      if (_tabController.indexIsChanging) {
        FocusScope.of(context).requestFocus(new FocusNode());
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('2 Tabs'),
        bottom: TabBar(controller: _tabController, tabs: <Widget>[
          Tab(text: 'Tab with Text Field'),
          Tab(text: 'Empty Tab'),
        ]),
      ),
      body: TabBarView(
        controller: _tabController,
        children: <Widget>[
          Container(
            child: TextFormField(
              decoration: InputDecoration(labelText: 'Title'),
            ),
          ),
          Container()
        ],
      ),
    );
  }
}
4 Answers

You can add gesture detector to you scaffold and remove the focus. But that won't work for the tabs. For the tabs you need to do the following:

controller.addListener((){

  if(controller.indexIsChanging)
    {
      FocusScope.of(context).detach();
    }
});

Where controller is your tab controller. Hope that helps

I improved @nick.tdr 's answer.

For the tabs you need to do the following;

controller.addListener((){

  if(controller.indexIsChanging)
    {
      FocusScope.of(context).unfocus();
    }
});

If you want to work this when swiping between tabs instead of clicking tab buttons, try the following;

controller.addListener((){
    FocusScope.of(context).unfocus();
});

Where the controller is your tab controller.

I think wrapping up your whole Scaffold body into a GestureDetector should solve your problem.

new Scaffold(

  body: new GestureDetector(
      onTap: () {
        // call this method here to hide keyboard
        FocusScope.of(context).requestFocus(new FocusNode());
      },
    child: new Container(
       /*Remaining code goes here*/
        )
   )

This simply gains focus on the widget you tapped on removing focus from previous one.

For me I found the best way is to request a new FocusNode on tabChange listener that has been set in didChangeDependencies() callback:

in build() method:

TabBar(
   controller: tabController,
      .
      .
      .
  ),

didChangeDependencies callback:

@override
void didChangeDependencies() {
  setState(() {
    tabController.addListener(handleTabChange);
  });
  super.didChangeDependencies();
}

The listener implementation:

handleTabChange() {
 // do whatever handling required first
  setState(() {
    FocusScope.of(context).requestFocus(new FocusNode());
  });
}
Related