Flutter - 如何在 SimpleDialog 框中显示 GridView?

Flutter - How to display a GridView within an SimpleDialog box?

昨天问了一个关于在 AlertDialog 中显示 GridView 的非常相似的问题,并得到了将 Widget 包装在容器中并增加宽度的完美答案。现在我决定 SimpleDialog 会更好,以便提供更多 functionality/customization,即搜索文本字段和多个操作按钮。我在显示文本字段和 IconButton 时得到了想要的结果,但是当我尝试添加 GridView 时却出错了。我应用了以前的规则,但我遇到了类似的错误,其中 UI 消失但没有显示小部件。我试过为不同的小部件添加宽度,但没有成功。这是我的代码...

Future<Null> _neverSatisfied() async {
    return showDialog<Null>(
      context: context,
      barrierDismissible: false, // user must tap button!
      child: new SimpleDialog(
        contentPadding: const EdgeInsets.all(10.0),
        title: new Text(
          'CHOOSE YOUR GIF PROFILE IMAGE',
          style: new TextStyle(fontWeight: FontWeight.bold, color: Colors.blue),
          textAlign: TextAlign.center,
        ),
        children: <Widget>[
          new Column(
            mainAxisSize: MainAxisSize.max,
            children: <Widget>[
              new Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    new Padding(padding: new EdgeInsets.all(10.0)),
                    new Expanded(
                        child: new TextField(
                            decoration:
                                new InputDecoration(labelText: "SEARCH GIF'S"),
                            controller: _searchController,
                            onSubmitted: (str) {
                              setState(() {
                                str = _searchController.text;
                              });
                            })),
                    new Padding(padding: new EdgeInsets.all(2.0)),
                    new IconButton(
                      icon: new Icon(
                        Icons.search,
                        color: Colors.blue,
                      ),
                      onPressed: () {
                        print('${_searchController.text}');
                      },
                      highlightColor: Colors.amber,
                      splashColor: Colors.amber,
                      iconSize: 25.0,
                    ),
                  ]),
              new Padding(padding: new EdgeInsets.all(2.0)),

              new Container(
                // Specify some width
                width: MediaQuery.of(context).size.width * .5,
                child: new InkWell(
                  splashColor: Colors.blue,
                  onTap: null,
                  child: new GridView.count(
                      crossAxisCount: 4,
                      childAspectRatio: 1.0,
                      padding: const EdgeInsets.all(4.0),
                      mainAxisSpacing: 4.0,
                      crossAxisSpacing: 4.0,
                      children: <String>[
                        'http://www.for-example.org/img/main/forexamplelogo.png',
                        'http://www.for-example.org/img/main/forexamplelogo.png',
                        'http://www.for-example.org/img/main/forexamplelogo.png',
                        'http://www.for-example.org/img/main/forexamplelogo.png',
                        'http://www.for-example.org/img/main/forexamplelogo.png',
                        'http://www.for-example.org/img/main/forexamplelogo.png',
                        'http://www.for-example.org/img/main/forexamplelogo.png',
                        'http://www.for-example.org/img/main/forexamplelogo.png',
                        'http://www.for-example.org/img/main/forexamplelogo.png',
                        'http://www.for-example.org/img/main/forexamplelogo.png',
                        'http://www.for-example.org/img/main/forexamplelogo.png',
                      ].map((String url) {
                        return new GridTile(
                            child: new Image.network(url, fit: BoxFit.cover, width: 12.0, height: 12.0,));
                      }).toList()),
                )
              ),

              new Padding(padding: new EdgeInsets.all(2.0)),
              new IconButton(
              splashColor: Colors.green,
              icon: new Icon(
                Icons.cancel,
                color: Colors.blue,
              ),
              onPressed: () {
                Navigator.of(context).pop();
              })
            ],
          ),
        ],
      ),
    );
  }

这是我的错误日志...

══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════ The following assertion was thrown during performLayout(): RenderViewport does not support returning intrinsic dimensions. Calculating the intrinsic dimensions would require instantiating every child of the viewport, which defeats the point of viewports being lazy. If you are merely trying to shrink-wrap the viewport in the main axis direction, consider a RenderShrinkWrappingViewport render object (ShrinkWrappingViewport widget), which achieves that effect without implementing the intrinsic dimension API.

该错误本质上意味着有一个滚动区域或一个可以增长的小部件,而您试图在其中放置另一个滚动区域而没有指定内部滚动区域应该有多大。当 flutter 试图计算各种事物的大小时,它不能,因为有两种不同的事物可以 grow/shrink;特别是网格的垂直方向是你的问题。如果 gridview 计算了其所有 children 的大小,则它可以根据该大小(如错误消息所述)对所有其余大小进行排序。但是 gridview 的全部意义在于显示大量数据,这些数据很可能会滚出屏幕,因此如果要进行该计算,在使用 gridview 的正常情况下可能会减慢速度。

我敢打赌 AlertDialog 有高度限制 and/or 不会滚动,这就是为什么您没有发现它的问题。

要解决您的问题,您有两种选择。如果您要拥有大量物品(如果您描述了您实际尝试制作的东西,它会帮助我回答),请继续使用 gridview,但只需为它所在的容器分配一个高度。那样对话框可以很高兴地知道它所有 children 的高度,并且网格可以在你给定的高度内调整自己的大小(说实话,我不确定你是否还需要定义宽度)。

另一种选择,如果您没有大量项目(即它们或多或少会适合屏幕 and/or 您不希望它们滚动),请使用 wrap 而不是直接布局所有项目的网格。这样做的好处是,当您的方向也发生变化时,它会很好地布置所有内容。