lcuis/search_choices

Text field does not unfocus after opening/close drop down dialog

trustmefelix opened this issue · 10 comments

I created TextField and type in something, and then press SearchChoices widget and open drop down dialog.
When I closed the dialog, TextField was still in focus. May I know is there any method to unfocus other text fields when open/close SearchChoices widget?

lcuis commented

Hello @trustmefelix ,

Sorry for the late reply.

What I understand is that when you close the SearchChoices dialog, the focus is moved to the next text field which is not part of SearchChoices. If this is correct, I am a bit surprised as this does not happen for me. Maybe a minimal example would help solving this issue. Otherwise, to handle focus questions in Flutter, I make heavy use of the FocusNode; maybe this could help you?

Can you please let me know whether I can close this issue?

lcuis commented

Closing for now, feel free to reopen if needed.

Hello, @lcuis

First, thanks a lot for this library

The problem as I understood, and encountered, is that, Assuming you have a page with 2 widgets. A TextField and a SearchChoices.single for example. First, tap on the TextField to focus it, then tap on the SearchChoices widget, then tap the close button of the SearchChoices widget.

Expected behavior: The TextField will no longer have the focus and the keyboard will be dismissed
Actual behaviour: The TextField is focused and the keyboard is showing

lcuis commented

Hello @KhatibFX ,

You're welcome for the plugin. I'm glad you find it useful and thank you for your message.

Thanks for the clarification of this issue. I was able to reproduce. I found a workaround. However, I am not sure it should be fixed within the plugin as I witness the same behavior with the CheckBox and DropdownButton Widgets as shown in the following example.

pubspec.yaml

name: flutter_searchchoices_issue_26
description: Text field does not unfocus after opening/close drop down dialog
version: 1.0.0+1
environment:
  sdk: ">=2.7.0 <3.0.0"
dependencies:
  flutter:
    sdk: flutter
  search_choices:
dev_dependencies:
  flutter_test:
    sdk: flutter
flutter:
  uses-material-design: true

main.dart

import 'package:flutter/material.dart';
import 'package:search_choices/search_choices.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SearchChoices issue 26',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(
          title:
              'Text field does not unfocus after opening/close drop down dialog'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String text = "";
  String item;
  List<String> items = ["First", "Second", "Third"];
  bool checked = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: SingleChildScrollView(
          scrollDirection: Axis.vertical,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'Text entered:',
              ),
              Text(
                '$text',
                style: Theme.of(context).textTheme.headline4,
              ),
              TextField(
                onChanged: (String value) {
                  text = value;
                },
              ),
              SizedBox(
                height: 20,
              ),
              Checkbox(
                value: checked,
                onChanged: (value) {
                  setState(() {
                    checked = value;
                  });
                },
              ),
              SizedBox(
                height: 20,
              ),
              DropdownButton(
                value: item,
                onChanged: (value) {
                  setState(() {
                    item = value;
                  });
                },
                items: items
                    .map<DropdownMenuItem<String>>(
                        (element) => DropdownMenuItem(
                              child: Text(element),
                              value: element,
                            ))
                    .toList(),
              ),
              SizedBox(
                height: 20,
              ),
              Text(
                'Selected item:',
              ),
              Text(
                '${item ?? ""}',
                style: Theme.of(context).textTheme.headline4,
              ),
              SearchChoices.single(
                value: item,
                hint: "Select one",
                items: items
                    .map<DropdownMenuItem<String>>(
                        (element) => DropdownMenuItem(
                              child: Text(element),
                              value: element,
                            ))
                    .toList(),
                onChanged: (value) {
                  setState(() {
                    item = value;
                  });
                  FocusScopeNode currentFocus = FocusScope.of(context);
                  if (!currentFocus.hasPrimaryFocus) {
                    Future.delayed(Duration(milliseconds: 500)).then((value) {
                      currentFocus.unfocus();
                    });
                  }
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

As you can see above, the workaround for the SearchChoices Widget to unfocus the TextField and dismiss the keyboard consists, within the onChanged function, in taking the focus from the context and requesting an unfocus after a delay. This is not very elegant but it works.

Can you please let me know your thoughts about this workaround?

Hey @lcuis,
I am aware that it also happens for the widgets you mentioned above. I have worked around it myself by overriding the buildDropDownDialog and calling unfocus at the beginning, like follows:

buildDropDownDialog: (
                                            Widget titleBar,
                                            Widget searchBar,
                                            Widget list,
                                            Widget closeButton,
                                            BuildContext dropDownContext,
                                            ) {
                                          unfocus(_targetQuantityFocusNode.context!);
                                          return AnimatedContainer(
                                            padding: MediaQuery.of(dropDownContext).viewInsets,
                                            duration: const Duration(milliseconds: 300),
                                            child: Card(
                                              margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 40),
                                              child: Container(
                                                padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
                                                child: Column(
                                                  crossAxisAlignment: CrossAxisAlignment.start,
                                                  mainAxisSize: MainAxisSize.min,
                                                  children: <Widget>[
                                                    titleBar,
                                                    searchBar,
                                                    list,
                                                    closeButton,
                                                  ],
                                                ),
                                              ),
                                            ),
                                          );
                                        },

I am not sure if onChanged is called even if no new item was selected.

As for DropdownButtonFormField for example, I work around it by overriding their onTap method:

onTap: () {
                                unfocus(layoutContext);
                              },

But your widget doesn't support that callback.

Again, thanks for the prompt response and your continuous effort!

lcuis commented

Hello @KhatibFX ,

Your workaround is a lot better than mine, thanks for sharing it!

What do you suggest I should do about this issue?
Should I add another parameter attempting the unfocus?
Should I add the onTap callback?

I think an onTap callback would be the most flexible, but any solution would be great honestly!

lcuis commented

Hi @KhatibFX ,

I just published version 2.0.6 to pub.dev with the onTap parameter. Feel free to reopen this issue if this doesn't help solving the unfocus issue.

Thanks a lot, it works perfectly!

lcuis commented

Excellent, thanks for this @KhatibFX !