26

I am building a page, dynamicly generating some views into it. In my case the list showned will updated given the user input (used as a filter).

All was working perfectly when using a Text Widget to be dynamicly rendered, but when I tried to switch to a Column or Gridview, all went wrong And a got the folowwing error

The following assertion was thrown building ServerGrid(dirty; state: _ServerGridState#59211289()):
I/flutter (14351): setState() or markNeedsBuild() called during build.
I/flutter (14351): This Overlay widget cannot be marked as needing to build because the framework is already in the
I/flutter (14351): process of building widgets. A widget can be marked as needing to be built during the build phase
I/flutter (14351): only if one of its ancestors is currently building. This exception is allowed because the framework
I/flutter (14351): builds parent widgets before children, which means a dirty descendant will always be built.
I/flutter (14351): Otherwise, the framework might not visit this widget during this build phase.
I/flutter (14351): The widget on which setState() or markNeedsBuild() was called was:
I/flutter (14351):   Overlay([LabeledGlobalKey<OverlayState>#774312152]; state: OverlayState#252339409(entries:
I/flutter (14351):   [OverlayEntry#727022347(opaque: false; maintainState: false), OverlayEntry#1051156104(opaque:
I/flutter (14351):   false; maintainState: true), OverlayEntry#280497357(opaque: false; maintainState: false),
I/flutter (14351):   OverlayEntry#41791024(opaque: false; maintainState: true), OverlayEntry#444321361(opaque: false;
I/flutter (14351):   maintainState: false), OverlayEntry#717668788(opaque: false; maintainState: true)]))

Here is the code I got so far,

ServerGrid which is where the error is thrown import 'package:flutter/material.dart';

import'package:firebase_sandbox/models/server.dart';

class ServerGrid extends StatefulWidget {
  ServerGrid({Key key, this.servers}) : super(key: key);

  final servers;

  @override
  State createState() => new _ServerGridState();
}

class _ServerGridState extends State<ServerGrid> {

  showInfos(serv){
    showDialog(context: context, child: new AlertDialog(content: new Text(serv.toString())));
  }

  Widget buildServerTile(Server serv) {
    return new InkWell(
        onTap: showInfos(serv),
        child :
          new Column(
            children: [
                new CircleAvatar(child : new Image.network(serv.image)),
                new Text(
                  serv.name,
                  style : new TextStyle(fontWeight: FontWeight.bold),
                ),
                new Text(
                  serv.description,
                  style : new TextStyle(color : Colors.grey[500]),
                ),
            ],
          ),
    );
  }

   List<Widget> buildGrid() {
    var theGrid = <Widget>[];
    for(Server s in widget.servers) {
      theGrid.add(buildServerTile(s));
    }
    return theGrid;
  }

  @override
  Widget build(BuildContext context) {
    // return new Text("${widget.servers.toString()}"); //Working perfectly
    return new GridView(
              gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 3,
              ),
               children: buildGrid(),
          );
  }
}

Controller calling the view to update :

  filter(value) {
    filteredList = _servers.where(
            (serv) =>
        serv.name.contains(value) || serv.tags
            .where((tag) => tag.contains(value))
            .length > 0
    ).toList();

    print(filteredList);
    setState(() {
      serverList = new ServerGrid(servers : filteredList);
    });
  }

Been searching, but I can't figure out what is wrong when building my Gridview

Alexi Coard
  • 7,106
  • 12
  • 38
  • 58

3 Answers3

61

You have an issue with your onTap handler. It is trying to show the dialog immediately during your build function, which is not allowed.

onTap: showInfos(serv),

Change your handler to be a function:

onTap: () => showInfos(serv),
Collin Jackson
  • 110,240
  • 31
  • 221
  • 152
6

For those who are facing the same error but are not in the same situation as @Alexi, you can do this:

WidgetsBinding.instance.addPostFrameCallback((_) {
  // Your code HERE
  // Flutter will wait until the current build is completed before executing this code.
});

sitatech
  • 1,311
  • 11
  • 17
3

I have used another interesting solution. So, what is happening here is that it notifies to rebuild the UI, while UI is already building which causes the error. So, what I did to fix it was to put it inside a Future.delayed() function, and give it a small duration of say 1 milisecond. What it does is it runs notifylistener later and you won't get any problem. Obviously this is not a proper fix but can help