Flutter Autocompletetextfield with dynamic hintText

Asked

Viewed 207 times

0

Description

My App consists of two widgets:

  • The first (Autocompleteinput) is composed of an autocomplete field (Autocompletetextfield) and a button beside it as shown in the following image: Widget AutocompleteInput
  • When an option is selected and the button pressed, the option is listed in the second component (Substanceslist), according to the image below: Exibindo os dois Widgets da aplicação

The Autocompletetextfield present in the first widget needs to have the hintText (text with the same functionality as the html "placeholder") dynamic. The hint should be: "Inform the Xº product", in which "X" should be the amount of items listed in the other widget + 1.

The way I get the number of items listed is through a final int variable that is assigned in the constructor of the Autocompleteinput class, called "nextSub".

Problem

The value of "nextSub" is being updated correctly (as items are inserted and removed from the list in the other widget), but its change is not being reflected in the interface, that is, the hintText is always: "Inform the 1st product", regardless of the amount of items listed in the second component.

The correct hint in this print should be "Inform the 3rd product": Exibindo o erro na interface.

I believe it is a problem related to setState() (was not used to assign the string "hintText"), since the widget Autocompleteinput is statefull, but I’m not sure where to put this method in order for the widget to work properly.

Question

What to do to make the "nextSub" value appear correctly in the Autocompletetextfield hintText?

Main.Dart code (where the Autocompleteinput component is imported):

class _MyHomePageState extends State<MyHomePage> {
  
  final List<SubstanciaAutocompleteOption> _substanciasAutocompleteOptions = new List<SubstanciaAutocompleteOption>();
  final List<SubstanciaAutocompleteOption> _substanciasSelecionadas = new List<SubstanciaAutocompleteOption>();
  int _nextSub = 1;

  void _addSubstanceToList(SubstanciaAutocompleteOption sub) {
    setState(() {
      _substanciasSelecionadas.add(sub);
      _nextSub += 1;
    });
  }
  
  @override
  Widget build(BuildContext context) {
    final mediaQuery = MediaQuery.of(context);
    final pageBody = SafeArea(
      child: SingleChildScrollView(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[

            Container(
                height: mediaQuery.size.height * 0.1,
                  padding: EdgeInsets.all(10),
                  child: AutocompleteInput(
                    options: _substanciasAutocompleteOptions,
                    submitHandler: _addSubstanceToList,
                    nextSub: _nextSub,
                  ),
                ),
                ...

Code for the Autocompleteinput widget

class AutocompleteInput extends StatefulWidget {
  final List<SubstanciaAutocompleteOption> options; //lista de opções para autocomplete
  final Function submitHandler; // o que fazer quando clicar no botão
  final int nextSub; // quantidade de itens presentes no segundo componente

  AutocompleteInput({
    this.options,
    this.submitHandler,
    this.nextSub
  });

  @override
  _AutocompleteInputState createState() => _AutocompleteInputState();
}

class _AutocompleteInputState extends State<AutocompleteInput> {
  AutoCompleteTextField searchTextField;
  GlobalKey<AutoCompleteTextFieldState<SubstanciaAutocompleteOption>> key = new GlobalKey();
  SubstanciaAutocompleteOption selectedOption;

  Widget custonListTile(SubstanciaAutocompleteOption option) {
    return Card(
      elevation: 5,
      margin: EdgeInsets.only(
        top: 6,
      ),
      child: Container(
        padding: EdgeInsets.only(left: 33, top: 5, bottom: 5, right: 5),
        height: 45,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              option.descricao.capitalize(),
              style: TextStyle(
                fontSize: 16,
              ),
            ),
            if(option.descricao != option.principioAtivo)
              Text(
                option.descricao.capitalize(),
                style: TextStyle(
                  fontSize: 14,
                  color: Colors.black45
                ),
              )
          ],
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    print('nextSub: ${widget.nextSub}');
    return Row(
      mainAxisSize: MainAxisSize.max,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        Expanded(
          child: searchTextField  = AutoCompleteTextField<SubstanciaAutocompleteOption> (
            itemSubmitted: (item) {
              setState(() {
                searchTextField.textField.controller.text = item.descricao;
                selectedOption = new SubstanciaAutocompleteOption(
                  subsId: item.subsId,
                  principioAtivo: item.principioAtivo,
                  comercialId: item.comercialId,
                  descricao: item.descricao
                );
              });
            },
            key: key, 
            clearOnSubmit: false,
            suggestions: widget.options, 
            itemBuilder: (context, item) {
              SubstanciaAutocompleteOption opAux = new SubstanciaAutocompleteOption(
                subsId: item.subsId, 
                principioAtivo: item.principioAtivo, 
                descricao: item.descricao,
                comercialId: item.comercialId
                );
              return custonListTile(opAux);
            }, 
            itemSorter: (a, b) {
              return a.descricao.compareTo(b.descricao);
            }, 
            itemFilter: (item, query) {
              return item.descricao.toLowerCase().startsWith(query.toLowerCase());
            },
            decoration: InputDecoration(
              contentPadding: EdgeInsets.only(top: 10.0),
              border: OutlineInputBorder(
                borderSide: BorderSide(
                  color: Theme.of(context).primaryColor
                )
              ),
              hintText: 'Informe o ${widget.nextSub}º produto',
              labelText: 'Adicionar Produto à Mistura',
              prefixIcon: Icon(Custom.beaker),
            ),
            style: TextStyle(
              fontSize: 18,
            ),
          ),
        ),
        Container(
          height: double.infinity,
          margin: EdgeInsets.only(left: 5),
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(6),
            boxShadow: [
              BoxShadow(color: Colors.green, spreadRadius: 1),
            ],
          ),
          child: IconButton(
            color: Colors.white,
            icon: Icon(Icons.add), 
            onPressed: () {
              if(selectedOption != null) {
                searchTextField.textField.controller.clear();
                widget.submitHandler(selectedOption);
                selectedOption = null;
              }
              else {
                // TO DO
              }
            }  
          ),
        ),
      ],
    );
  }
} 
  • Oops beauty? Edita your question and places the images directly in it.

  • The value of your property subsIndex comes from outside the widget AutocompleteInput exemplified in the question, so you probably need to increment it in the function passed in the submitHandler, by putting the setState(). If possible show us where you are using this widget.

  • 1

    @Matheusribeiro , I made the edits, I believe the error is time to assign hintText, must have some correct place to put setState() but I’m not able to identify where

  • I did some tests here, and I could not reproduce the problem (but I did not use the autocomplete), apparently this right your control...

  • @Matheusribeiro ai that has the problem, the autocomplete being stored in the variable "searchTextField", in which it is not being updated when the button is clicked, has some way to force the widget Autocompletetextfield, within this various "searchTextfield", run the build method again? Since this variable has an assigned value, the hintText is set and as a consequence of this variable does not be updated, the hintText also does not change. I’ve tried putting setState on the onPressed button but n worked: https://prnt.sc/t98r71

1 answer

1

Dude it doesn’t make much sense for you to store the widget in a variable just to get its text if you can create one TextEditingController which serves this purpose. As you said in your comment, I had not attempted to attack you stored in a variable....

Try the following change:

class _AutocompleteInputState extends State<AutocompleteInput> {
  AutoCompleteTextField searchTextField;
  GlobalKey<AutoCompleteTextFieldState<SubstanciaAutocompleteOption>> key = new GlobalKey();
  SubstanciaAutocompleteOption selectedOption;

  TextEditingController controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    
    print('nextSub: ${widget.nextSub}');
    return Row(
      mainAxisSize: MainAxisSize.max,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        Expanded(
          child: AutoCompleteTextField<SubstanciaAutocompleteOption> (
            controller: controller,
            itemSubmitted: (item) {
              setState(() {
                controller.text = item.descricao;
                selectedOption = new SubstanciaAutocompleteOption(
                  subsId: item.subsId,
                  principioAtivo: item.principioAtivo,
                  comercialId: item.comercialId,
                  descricao: item.descricao
                );
              });
            },
            key: key, 
            clearOnSubmit: false,
            suggestions: widget.options, 
            itemBuilder: (context, item) {
              SubstanciaAutocompleteOption opAux = new SubstanciaAutocompleteOption(
                subsId: item.subsId, 
                principioAtivo: item.principioAtivo, 
                descricao: item.descricao,
                comercialId: item.comercialId
                );
              return custonListTile(opAux);
            }, 
            itemSorter: (a, b) {
              return a.descricao.compareTo(b.descricao);
            }, 
            itemFilter: (item, query) {
              return item.descricao.toLowerCase().startsWith(query.toLowerCase());
            },
            decoration: InputDecoration(
              contentPadding: EdgeInsets.only(top: 10.0),
              border: OutlineInputBorder(
                borderSide: BorderSide(
                  color: Theme.of(context).primaryColor
                )
              ),
              hintText: 'Informe o ${widget.nextSub}º produto',
              labelText: 'Adicionar Produto à Mistura',
              prefixIcon: Icon(Custom.beaker),
            ),
            style: TextStyle(
              fontSize: 18,
            ),
          ),
        ),
        Container(
          height: double.infinity,
          margin: EdgeInsets.only(left: 5),
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(6),
            boxShadow: [
              BoxShadow(color: Colors.green, spreadRadius: 1),
            ],
          ),
          child: IconButton(
            color: Colors.white,
            icon: Icon(Icons.add), 
            onPressed: () {
              if(selectedOption != null) {
                controller.clear();
                widget.submitHandler(selectedOption);
                selectedOption = null;
              }
              else {
                // TO DO
              }
            }  
          ),
        ),
      ],
    );
  }
}
  • Still not working, here the print of a test: https://prnt.sc/t9akt9, the text should be "Inform the 4th product". I used this code: https://pastebin.com/J3tunRd5 (as suggested above)

  • Would transform this widget to statefull resolve ?

  • He already is Statefull , but I believe it’s still.

  • Excuse me, stateless

  • I believe I found the source of the bug: The way the widget key is used. I did a search here and saw that Keys preserve widgets states, so to solve this problem should probably be made some correction in Globalkey Autocompletetextfield. I tried to declare Globalkey in main.Dart and pass the constructor of the Autocompleteinput class , but it didn’t work, no ended error appeared: https://prnt.sc/t9c44y

  • Ah you’re using the package autocomplete_textfield... That’s why you need the GlobalKey... I never used this widget so you will have to analyze to see the need for it and can handle the update of it...

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.