首页 > 解决方案 > Flutter Callback with return error setState() or markNeedsBuild called during build

问题描述

I use PopupMenuButton to edit the listView. For every action PopupMenuButton (remove item, add item, edit item in listView) trying to do Callback. For example, the simplest case is to delete

//_editorChannels - data with which I fill in the list
delete(dynamic val) {
 setState(() => _editorChannels.removeWhere((data) => data == val));
}

But since I pass the callback function delete to the widget, when I create it I get error setState() or markNeedsBuild called during build. If change and remove the setState(), then no error occurs, but of course the list will not be updated when the item is deleted via callback.

delete(dynamic val) {
  _editorChannels.removeWhere((data) => data == val);
}

My PopupMenuButton Widget

  class PopMenuWidget2 extends StatelessWidget {
  final VoidCallback onDelete;

  const PopMenuWidget2({Key key, this.onDelete}) : super(key: key);

  @override
  Widget build(BuildContext context) => PopupMenuButton<int>(
    onSelected: (result) {
      //On View
      if (result == 0) {}
      //On Edit
      if (result == 1) { }

      //OnDelete Callback run
      if (result == 2) {
        onDelete();
      }
    },
    itemBuilder: (context) => [
      PopupMenuItem(
        value: 0,
        child: Row(
          children: <Widget>[
            Icon(
              Icons.remove_red_eye_rounded,
              color: Colors.black38,
            ),
            Text('  View Option', style: TextStyle(color: Colors.black38)),
          ],
        ),
      ),
      PopupMenuItem(
        value: 1,
        child: Row(
          children: <Widget>[
            Icon(
              Icons.create,
              color: Colors.black38,
            ),
            Text('  Edit Option', style: TextStyle(color: Colors.black38)),
          ],
        ),
      ),
      PopupMenuItem(
        value: 2,
        child: Row(
          children: <Widget>[
            Icon(
              Icons.delete,
              color: Colors.black38,
            ),
            Text('  Delete Option',
                style: TextStyle(color: Colors.black38)),
          ],
        ),
      ),
    ],
  );
 }

Main Widget

  class EditorPage extends StatefulWidget {

  EditorPage({Key key, this.Channels}) : super(key: key);

  final List<Channel> Channels;

  static const String routeName = "/EditorPage";

  @override
  _EditorPageState createState() => new _EditorPageState();
  }

  class _EditorPageState extends State<EditorPage> {
  Stream<Mock> _result;
  final _coreObj = new Core();

 List<Channel> _editorChannels;


  //callback onDelete
  delete(dynamic val) {
  setState(() => _editorChannels.removeWhere((data) => data == val));
 }

 @override
 void initState() {
 _editorChannels = widget.Channels;
 super.initState();
 }


@override
Widget build(BuildContext context) {
return DefaultTabController(
    length: 2,
    child: new Scaffold(
        appBar: AppBar(
          title: Text(''),
          bottom: TabBar(tabs: [
            Tab(icon: FaIcon(FontAwesomeIcons.calendarCheck), text: "One"),
            Tab(icon: FaIcon(FontAwesomeIcons.tasks), text: "Two"),
          ]),
        ),
        body: SafeArea(
            child: TabBarView(children: [
          Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Expanded(
                child: Scaffold(
                  body: ListView.builder(
                      itemCount: _editorChannels == null
                          ? 0
                          : _editorChannels.length,
                      itemBuilder: (context, index) {
                        final item = _editorChannels[index];
                        return Card(
                          shadowColor: Colors.black26,
                          margin: EdgeInsets.all(3.0),
                          clipBehavior: Clip.antiAlias,
                          shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(2),
                          ),
                          child: ListTile(
                              title: Container(
                                  child: Text(
                                item.Name != null ? item.Name : '',
                                style: new TextStyle(
                                    fontWeight: FontWeight.bold,
                                    fontSize: 16.0),
                              )),
                              subtitle: Text(item.Url),
                              onTap: () => {},
                              isThreeLine: false,
                              leading: getIconbyId(item.Status),
                              
                              

                               //Pass callback   
                              trailing: PopMenuWidget2(
                                onDelete: delete(item),
                              )),


                        );
                      }),
                ),
              )
            ],
          ),
        ]))));
    }
  }

标签: fluttercallback

解决方案


为了setState()工作,您需要拥有PopMenuWidget2as StatefulWidget。改变

class PopMenuWidget2 extends StatelessWidget {

class PopMenuWidget2 extends StatefulWidget {

还传递你的回调函数,比如它上面的链接,而不是函数执行的结果 onDelete: () => onDelete(item)

  trailing: PopMenuWidget(
  onDelete: () => onDelete(item),
  )),

完整代码:

class PopMenuWidget2 extends StatefulWidget {
  final VoidCallback onDelete;

  const PopMenuWidget2({Key key, this.onDelete}) : super(key: key);
  @override
  _PopMenuWidget2State createState() => _PopMenuWidget2State();
}

class _PopMenuWidget2State extends State<PopMenuWidget2> {
  @override
  Widget build(BuildContext context) => PopupMenuButton<int>(
    onSelected: (result) {
      //On View
      if (result == 0) {}
      //On Edit
      if (result == 1) { }

      //OnDelete Callback run
      if (result == 2) {
        onDelete();
      }
    },
    itemBuilder: (context) => [
      PopupMenuItem(
        value: 0,
        child: Row(
          children: <Widget>[
            Icon(
              Icons.remove_red_eye_rounded,
              color: Colors.black38,
            ),
            Text('  View Option', style: TextStyle(color: Colors.black38)),
          ],
        ),
      ),
      PopupMenuItem(
        value: 1,
        child: Row(
          children: <Widget>[
            Icon(
              Icons.create,
              color: Colors.black38,
            ),
            Text('  Edit Option', style: TextStyle(color: Colors.black38)),
          ],
        ),
      ),
      PopupMenuItem(
        value: 2,
        child: Row(
          children: <Widget>[
            Icon(
              Icons.delete,
              color: Colors.black38,
            ),
            Text('  Delete Option',
                style: TextStyle(color: Colors.black38)),
          ],
        ),
      ),
    ],
  );
}

推荐阅读