Flutter call setState() to update UI in another class

957

Solution 1

I managed to make it work, by making both classes into one and calling a function to draw the messagecards, thank you all for your help and attention

Solution 2

You can only use setState in a StatefulWidget.

class MessageCard extends StatefulWidget {

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

class _MessageCardState extends State<MessageCard> {
  @override
  Widget build(BuildContext context) {
    // your build method here
  }
}

Solution 3

Stateless widget can not change the state once its rendered. To use setState and re-render the widget StatefulWidget is used.

Just change your MessageCard from Stateless Widget to StatefulWidget

class MessageCard extends StatefulWidget {
    final Mensagem message;
    final int messageLenght;
    final List<Mensagem> messageList;
    var i;

    MessageCard(
      {@required this.message,
        @required this.messageLenght,
        @required this.messageList});

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

    class _MessageCardState extends State<MessageCard> {
      @override
      Widget build(BuildContext context) {
        // your build method here
      }
    }

Also, now "to use your MessageCard properties" like message, messageLenght, messageList, in _MessageCardState you have to use a property like widget.message, widget.messageList and widget.messageLenght respectively.

Solution 4

can this work to refresh the ui?

_getMessages() async {
    _HomePageState()._messages = await mensagemRepo.getMessages();

    print('loaded messages: ${_messages?.length}');

    setState(() {
      _HomePageState()._messagesList();
    });
  }

The code for _messagesList() is:

SliverChildBuilderDelegate _messagesList() {
    int count() {
      if (_errorMsg != '')
        return 1;
      else
        return _messages == null ? 0 : _messages.length;
    }

    return SliverChildBuilderDelegate(
      (BuildContext context, int index) {
        print("i: $index");
        if (_errorMsg != '') {
          return Padding(
            padding: EdgeInsets.all(20),
            child: ErrorMessage(
              error: _errorMsg,
            ),
          );
        } else {
          return _MessageCard(
              message: this._messages[index],
              messageLength: this._messages.length,
              messageList: this._messages);
        }
      },
      childCount: count(),
    );
  }

Solution 5

Well, you can't set value for something that doesn't exist. Stateless by name itself makes it clear that it can't hold any state. Changing the widget to a stateful widget would work.

Share:
957
André Sousa
Author by

André Sousa

Updated on December 16, 2022

Comments

  • André Sousa
    André Sousa over 1 year

    I am trying to call a setState when a button is pressed so the ui can show the new list but even using functions i cant use setState or it will give me the error saying im calling setState inside a constructor.

    This is my code for the statlessWidget:

    class _MessageCard extends StatelessWidget {
    final Mensagem message;
    final int messageLenght;
    final List<Mensagem> messageList;
    var i;
    _MessageCard(
      {@required this.message,
      @required this.messageLenght,
      @required this.messageList});
    
    @override
    Widget build(BuildContext context) {
    return Center(
        child: Container(
      width: 600,
      child: InkWell(
        child: Container(
          width: 900,
          color: Colors.grey[200],
          child: Padding(
            padding: const EdgeInsets.fromLTRB(12, 0, 12, 0),
            child: Center(
              child: Container(
                width: 600,
                child: Column(
                  children: <Widget>[
                    ListTile(
                      leading: CircleAvatar(
                        child: Icon(
                          Icons.notifications,
                          color: Colors.red[400],
                        ),
                        backgroundColor: Colors.grey[200],
                      ),
                      title: Text(
                        (this.message.vDescricao ?? '').trim(),
                        style: TextStyle(
                          fontSize: 14,
                          color: Colors.black,
                        ),
                      ),
                      subtitle: Text(
                        (this.message.vData ?? '').trim() +
                            '   ' +
                            (this.message.vHora ?? '').trim(),
                        style: TextStyle(
                          color: Colors.red[400],
                          fontSize: 13,
                        ),
                      ),
                      trailing: FlatButton(
                          child: Text(
                            Translations.of(context)
                                .trans('finishmessageshort'),
                          ),
                          onPressed: () => _showDeleteAlertMessage(
                              this.message.vNumero, context)),
                    ),
                    Divider(
                      color: Colors.black54,
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),
      ),
     ));
    }
    
    Future _showDeleteAlertMessage(String id, BuildContext context) {
    return showDialog(
        context: context,
        builder: (BuildContext context) {
          return AlertDialog(
            title: new Text(
              Translations.of(context).trans('finishmessage') + '?',
            ),
            actions: <Widget>[
              FlatButton(
                  child: new Text(
                    Translations.of(context).trans('closealert'),
                  ),
                  onPressed: () {
                    Navigator.of(context).pop();
                  }),
              FlatButton(
                child: new Text(("Ok")),
                onPressed: () =>
                    {_deleteMessage(id), Navigator.of(context).pop()},
              )
            ],
          );
        });
    }
    
    _deleteMessage(String id) async {
    for (i = 0; i < this.messageLenght; i++) {
      if (this.messageList[0].vNumero == this.message.vNumero) {
        this.messageList.removeAt(i);
        _HomePageState().mensagemRepo.confirmMessage(this.message.vNumero);
        await _HomePageState()._getMessages();
        return this.messageList;
       }
      }
     }
    }
    

    And this is my _getMessages()

    _getMessages() async {
    setState(() {
      _loading = true;
      _errorMsg = '';
    });
    
    try {
      _messages = await mensagemRepo.getMessages();
    
      print('loaded messages: ${_messages?.length}');
    } catch (e) {
      _errorMsg = e.toString();
    }
    
    setState(() {
      _loading = false;
    });
    

    }

    How can i make it so i can use this setState?

    Thank you for your time and attention

    Edit: Now updates List but not UI, because im not able to set HomePage state from MessageCard

    • Golden Lion
      Golden Lion almost 3 years
      setState can only be used in a stateful widget. if you want to communicate between classes use a void call back function and pass the pointer function to the dependent widget. Otherwise use a notification and change notification broadcast stream.
  • André Sousa
    André Sousa over 4 years
    if i use it like that i cant call the messageCard build in my homepage or do i need to call the messagecardState?
  • Benjamin
    Benjamin over 4 years
    Call MessageCard() in your build method, it will be fine. The createState method will call _MessageCardState for you.
  • André Sousa
    André Sousa over 4 years
    and what if i wanted to access my other homepage widget? can i call setState inside my messagecard?
  • Jay Mungara
    Jay Mungara over 4 years
    MessageCard will be out of picture when you use StatefulWidget. You don't need to create and execute any functions, variables inside your MessageCard class. Whatever you have to perform will be done in your State class which is _MessageCardState . As name suggests, state class is used to manage and change your widget state and you can use it only inside _MessageCardState.
  • André Sousa
    André Sousa over 4 years
    so there is no way for me to update the state of another class inside the MessageCard class?
  • Jay Mungara
    Jay Mungara over 4 years
    _MessageCardState is the class which manages state of the MessageCard. So, ultimately whatever state you are changing is used to change the state of the MasterClass. Don't get confused with having two class MessageCard and _MessageCardState. They are ultimately interconnected.
  • André Sousa
    André Sousa over 4 years
    I can clear them from the list but i cant seem to update the ui in the HomePage class which is the main class
  • Jay Mungara
    Jay Mungara over 4 years
    update your list when data updates using setState() method. Update your question.