7

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()
        ],
      ),
    );
  }
}
  • 1
    https://stackoverflow.com/a/55314860/122313 – Aaron Saunders Jun 07 '19 at 19:27
  • 2
    thank @AaronSaunders but **detach** did not work for me. VSC did mark as a error code saying: "The method 'detach' isn't defined for the class 'FocusScopeNode'". I had to use `FocusScope.of(context).requestFocus(new FocusNode());` – Joan Farfán Armas Jun 09 '19 at 18:18

4 Answers4

6

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

nick.tdr
  • 4,627
  • 4
  • 28
  • 41
  • 1
    for that you will need to remove the DefaultTabController and user a controller object – nick.tdr Jun 07 '19 at 19:39
  • @diegoveloper I tried it on my end. If the gesture detector is the root widget of the body in scaffold, its on tap won't be called when tapped on the tabs. That's why i added a listener to the tab controller. If you have a better way of doing it please explain. Thanks – nick.tdr Jun 08 '19 at 08:17
  • Thanks @nick.tdr it worked perfectly but it was necessary to make some adjustment to my code. I will edit my question above with my solving code. – Joan Farfán Armas Jun 09 '19 at 17:05
  • 1
    `indexIsChanging` only returns `true` when clicking on the tab buttons. It is returning `false` when swipes between tabs. Therefore, this will not always work. – prahack Mar 16 '21 at 15:13
  • `detach()` is also not valid now. – prahack Mar 16 '21 at 15:18
3

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.

Dharman
  • 30,962
  • 25
  • 85
  • 135
prahack
  • 1,267
  • 1
  • 15
  • 19
0

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());
  });
}
Muhammed Refaat
  • 8,914
  • 14
  • 83
  • 118
-1

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.

  • thanks Manmeet! I tried it but it was necessary to press anywhere in the Tab body in order to hide the keyboard. It did not hide it right after pressing the Tab. – Joan Farfán Armas Jun 09 '19 at 16:57