2

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

André Sousa
  • 187
  • 4
  • 17
  • 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. – Golden Lion May 26 '21 at 21:44

5 Answers5

4

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
  }
}
Jay Mungara
  • 6,663
  • 2
  • 27
  • 49
Benjamin
  • 5,783
  • 4
  • 25
  • 49
  • if i use it like that i cant call the messageCard build in my homepage or do i need to call the messagecardState? – André Sousa Dec 27 '19 at 12:23
  • Call `MessageCard()` in your build method, it will be fine. The `createState` method will call `_MessageCardState` for you. – Benjamin Dec 27 '19 at 12:24
1

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.

Yudhishthir Singh
  • 2,941
  • 2
  • 23
  • 42
1

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.

Jay Mungara
  • 6,663
  • 2
  • 27
  • 49
  • and what if i wanted to access my other homepage widget? can i call setState inside my messagecard? – André Sousa Dec 27 '19 at 13:45
  • 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. – Jay Mungara Dec 27 '19 at 13:51
  • so there is no way for me to update the state of another class inside the MessageCard class? – André Sousa Dec 27 '19 at 13:56
  • _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. – Jay Mungara Dec 27 '19 at 14:00
  • I can clear them from the list but i cant seem to update the ui in the HomePage class which is the main class – André Sousa Dec 27 '19 at 14:15
  • update your list when data updates using setState() method. Update your question. – Jay Mungara Dec 27 '19 at 14:16
1

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(),
    );
  }
André Sousa
  • 187
  • 4
  • 17
0

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

André Sousa
  • 187
  • 4
  • 17