如何在Flutter中为GridView中的Widget设置自定义高度?

问题描述 投票:0回答:17

即使指定了 Container GridView 的高度,我的代码也会生成方形小部件。

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  List<String> widgetList = ['A', 'B', 'C'];

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Container(
        child: new GridView.count(
          crossAxisCount: 2,
          controller: new ScrollController(keepScrollOffset: false),
          shrinkWrap: true,
          scrollDirection: Axis.vertical,
          children: widgetList.map((String value) {
            return new Container(
              height: 250.0,
              color: Colors.green,
              margin: new EdgeInsets.all(1.0),
              child: new Center(
                child: new Text(
                  value,
                  style: new TextStyle(fontSize: 50.0,color: Colors.white),
                ),
              ),
            );
          }).toList(),
        ),
      ),
    );
  }
}

上面代码的输出如左图所示。如何获得具有自定义高度小部件的 GridView,如右图所示?

gridview dart height flutter
17个回答
345
投票

关键是

childAspectRatio
。该值用于确定
GridView
中的布局。为了获得所需的外观,您必须将其设置为 (
itemWidth
/
itemHeight
)。解决方案是这样的:

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  List<String> widgetList = ['A', 'B', 'C'];

  @override
  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size;

    /*24 is for notification bar on Android*/
    final double itemHeight = (size.height - kToolbarHeight - 24) / 2;
    final double itemWidth = size.width / 2;

    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Container(
        child: new GridView.count(
          crossAxisCount: 2,
          childAspectRatio: (itemWidth / itemHeight),
          controller: new ScrollController(keepScrollOffset: false),
          shrinkWrap: true,
          scrollDirection: Axis.vertical,
          children: widgetList.map((String value) {
            return new Container(
              color: Colors.green,
              margin: new EdgeInsets.all(1.0),
              child: new Center(
                child: new Text(
                  value,
                  style: new TextStyle(
                    fontSize: 50.0,
                    color: Colors.white,
                  ),
                ),
              ),
            );
          }).toList(),
        ),
      ),
    );
  }
}

45
投票

您会找到的最佳答案

只需使用

 childAspectRatio: (1 / .4)

其中1为宽度,0.4为高度,可根据自己的需求定制 完整代码在这里

 return GridView.count(
  crossAxisCount: 2,
  childAspectRatio: (1 / .4),
  shrinkWrap: true,
  children: List.generate(6, (index) {
    return Padding(
      padding: const EdgeInsets.all(10.0),
      child: Container(
        color: Colors.grey[600],
        child: Row(
          children: [

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


30
投票

几天前,我来这里寻找一种在从互联网加载图像时动态更改高度的方法,但使用

childAspectRatio
无法做到这一点,因为它适用于 GridView 中的所有小部件(每个小部件的高度相同)。

这个答案可能会帮助那些根据每个小部件内容想要不同高度的人:

我发现了一个名为 Flutter Staggered GridView by Romain Rastel 的包。使用这个包我们可以做很多事情,查看这里的例子。

要获得我们想要的东西,我们可以使用

StaggeredGridView.count()
及其属性
staggeredTiles:
,对于其值,您可以映射所有小部件并应用
StaggeredTile.fit(2)

示例代码:

StaggeredGridView.count(
    crossAxisCount: 4, // I only need two card horizontally
    padding: const EdgeInsets.all(2.0),
    children: yourList.map<Widget>((item) {
      //Do you need to go somewhere when you tap on this card, wrap using InkWell and add your route
      return new Card(
        child: Column(
          children: <Widget>[
             Image.network(item.yourImage),
             Text(yourList.yourText),//may be the structure of your data is different
          ],
        ),
      );
    }).toList(),

    //Here is the place that we are getting flexible/ dynamic card for various images
    staggeredTiles: yourList.map<StaggeredTile>((_) => StaggeredTile.fit(2))
        .toList(),
    mainAxisSpacing: 3.0,
    crossAxisSpacing: 4.0, // add some space
  ),
);

您可以在这里找到完整的示例(复制、粘贴并运行)。


24
投票
所有功劳都归于 Github 上的@ayRatul 的

这个答案,这是迄今为止我见过的在 GridView

 中实现固定项目高度的最好和最简单的方法:

创建您自己的自定义委托:

class SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight extends SliverGridDelegate { /// Creates a delegate that makes grid layouts with a fixed number of tiles in /// the cross axis. /// /// All of the arguments must not be null. The `mainAxisSpacing` and /// `crossAxisSpacing` arguments must not be negative. The `crossAxisCount` /// and `childAspectRatio` arguments must be greater than zero. const SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight({ @required this.crossAxisCount, this.mainAxisSpacing = 0.0, this.crossAxisSpacing = 0.0, this.height = 56.0, }) : assert(crossAxisCount != null && crossAxisCount > 0), assert(mainAxisSpacing != null && mainAxisSpacing >= 0), assert(crossAxisSpacing != null && crossAxisSpacing >= 0), assert(height != null && height > 0); /// The number of children in the cross axis. final int crossAxisCount; /// The number of logical pixels between each child along the main axis. final double mainAxisSpacing; /// The number of logical pixels between each child along the cross axis. final double crossAxisSpacing; /// The height of the crossAxis. final double height; bool _debugAssertIsValid() { assert(crossAxisCount > 0); assert(mainAxisSpacing >= 0.0); assert(crossAxisSpacing >= 0.0); assert(height > 0.0); return true; } @override SliverGridLayout getLayout(SliverConstraints constraints) { assert(_debugAssertIsValid()); final double usableCrossAxisExtent = constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1); final double childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount; final double childMainAxisExtent = height; return SliverGridRegularTileLayout( crossAxisCount: crossAxisCount, mainAxisStride: childMainAxisExtent + mainAxisSpacing, crossAxisStride: childCrossAxisExtent + crossAxisSpacing, childMainAxisExtent: childMainAxisExtent, childCrossAxisExtent: childCrossAxisExtent, reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection), ); } @override bool shouldRelayout( SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight oldDelegate) { return oldDelegate.crossAxisCount != crossAxisCount || oldDelegate.mainAxisSpacing != mainAxisSpacing || oldDelegate.crossAxisSpacing != crossAxisSpacing || oldDelegate.height != height; } }
然后您可以像这样使用自定义委托:

... GridView.builder( shrinkWrap: true, itemCount: list.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight( crossAxisCount: 5, crossAxisSpacing: 5, mainAxisSpacing: 5, height: 48.0, //48 dp of height ...
    

20
投票

crossAxisCount

crossAxisSpacing
和屏幕宽度决定
width
childAspectRatio
决定
height

我做了一些计算来找出它们之间的关系。

var width = (screenWidth - ((_crossAxisCount - 1) * _crossAxisSpacing)) / _crossAxisCount; var height = width / _aspectRatio;


完整示例:

double _crossAxisSpacing = 8, _mainAxisSpacing = 12, _aspectRatio = 2; int _crossAxisCount = 2; @override Widget build(BuildContext context) { double screenWidth = MediaQuery.of(context).size.width; var width = (screenWidth - ((_crossAxisCount - 1) * _crossAxisSpacing)) / _crossAxisCount; var height = width / _aspectRatio; return Scaffold( body: GridView.builder( itemCount: 10, itemBuilder: (context, index) => Container(color: Colors.blue[((index) % 9) * 100]), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: _crossAxisCount, crossAxisSpacing: _crossAxisSpacing, mainAxisSpacing: _mainAxisSpacing, childAspectRatio: _aspectRatio, ), ), ); }
    

18
投票
mainAxisExtent: 150, // 这里设置你想要的自定义高度

NOT:动态高度如:(尺寸.高度或其他)不起作用

Container( padding: EdgeInsets.all(10), color: Colors.grey.shade300, child: GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 10, mainAxisSpacing: 10, mainAxisExtent: 150, // here set custom Height You Want ), itemCount: 10, itemBuilder: (BuildContext context, int index) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(5), color: Colors.white, ), child: Text('150px'), ); }, ), ),


11
投票

这对我有用。

示例代码:

var _crossAxisSpacing = 8; var _screenWidth = MediaQuery.of(context).size.width; var _crossAxisCount = 2; var _width = ( _screenWidth - ((_crossAxisCount - 1) * _crossAxisSpacing)) / _crossAxisCount; var cellHeight = 60; var _aspectRatio = _width /cellHeight;

网格视图:

GridView.builder( padding: EdgeInsets.only( left: 5.0, right: 5.0, top: 10, bottom: 10), shrinkWrap: false, itemCount: searchList.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: _crossAxisCount,childAspectRatio: _aspectRatio), itemBuilder: (context, index) { final item = searchList[index]; return Card( child: ListTile( title: Text(item.name,maxLines: 1,overflow: TextOverflow.ellipsis), trailing: Container( width: 15, height: 15, decoration: BoxDecoration( color: item.isActive == true ? Theme.of(context).primaryColor : Colors.red, borderRadius: BorderRadius.all( Radius.circular(50))), ), onTap: () { }, ), elevation: 0.5, ); }, )
    

8
投票
您可以使用 mainAxisExtent 代替 childAspectRatio

GridView.builder( physics: BouncingScrollPhysics(), itemCount: resumes.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisExtent: 256, ), itemBuilder: (_, index) => Container(), ),
    

8
投票

使用选定的突出显示和其他未突出显示的网格视图构建

mainAxisExtent

:200(所需高度)

GridView.builder( shrinkWrap: true, itemCount: _images.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 10, mainAxisSpacing: 10, mainAxisExtent: 200), itemBuilder: (BuildContext context, int index) { return InkWell( onTap: () { setState(() { _selectedTasteIndex = index; }); }, child: Container( decoration: BoxDecoration( color: _selectedTasteIndex == index ? Colors.green : Colors.white, borderRadius: BorderRadius.circular(WodlDimens.SPACE10), border: Border.all( color: Colors.grey, width: 1, ), ), alignment: Alignment.center, child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( height: 30, width: 30, child: Image.asset(_images[index]), ), SizedBox( width: 10, ), Text( _tastes[index], style: TextStyle( fontSize: 20, color: _selectedTasteIndex == index ? Colors.white : Colors.black, ), ), ], ), ), ); //return Image.network(images[index]); }, ))
    

6
投票
嗯,我到处都能看到的第一个解决方案是使用

childAspectRatio


但是纵横比为不同屏幕尺寸的设备提供了 GridView 内容器的不同高度!

但是,我终于得到了完美的解决方案:

不要使用

childAspectRatio

,而是使用 
mainAxisExtent
 来定义容器的固定高度

GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( mainAxisSpacing: CommonDimens.MARGIN_20, crossAxisSpacing: CommonDimens.MARGIN_20, crossAxisCount: 2, mainAxisExtent: 152, // childAspectRatio: 1.1, DONT USE THIS when using mainAxisExtent ), shrinkWrap: true, padding: EdgeInsets.zero, itemCount: itemList.length, physics: const NeverScrollableScrollPhysics(), itemBuilder: (_, position) => itemList[position], ),
    

4
投票
使用

childAspectRatio: .7 (or something you can change it and update your view)
在 GridView.builder 中

这样使用

gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 10, childAspectRatio: .7, ),
在GridView.count中像这样使用

GridView.count( crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 10, childAspectRatio: .7, )
    

4
投票
您需要在

childAspectRatio

gridDelegate
 中使用 
GridView
 属性:

gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( childAspectRatio: 2/1.6 // try to fiddle with this ratio crossAxisCount: 2, crossAxisSpacing: 15, mainAxisSpacing: 15, ),
    

1
投票
在 GridView 内部,使用以下代码:

gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, //Number of columns you want childAspectRatio: (1 / .4) //height & width for the GridTile ),
    

1
投票
将此行粘贴到 GridView.count 中

子纵横比:2/4,


1
投票
唯一内置的简单解决方案是,使用

childAspectRatio

 并将其设置为 
1 / .6
1 / .7
 之类的值,具体取决于您的小部件大小。默认情况下,Flutter GridView.count 将高度和宽度设置为相同大小,如果设计需要不同的形状输出,则会导致过多的填充。通过
childAspectRatio
的方式,我们可以将宽度的一半值分配给高度,并得到我们想要实现的效果。

GridView.count( childAspectRatio: (1 / .7), // this line is the one determines the width-height ratio crossAxisCount: 2, crossAxisSpacing: gScreenWidth / 40, mainAxisSpacing: gScreenWidth / 40, children: List.generate( yourList.length, (index) { return Center( child: Container( child: Text('Hello World. Finally I fixed the height issue'))), }, )),
    

0
投票
这个问题是我的问题之一,但我发现如果我们将重复的小部件放在 SingleChildScrollView 小部件中,重复的小部件的高度将处于最小尺寸!

就是这样!


0
投票
您可以使用自动高度网格视图来轻松管理网格视图。

https://pub.dev/packages/auto_height_grid_view

© www.soinside.com 2019 - 2024. All rights reserved.