如何设置自定义高度的部件在 GridView 扑动?

即使在为 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,如右图所示?

output Required Output

195175 次浏览

关键是 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(),
),
),
);
}
}

几天前,我来到这里找到一种方法,以动态改变高度时,图像是从互联网加载和使用 childAspectRatio不能这样做,因为它适用于所有部件在 GridView (相同的高度为每个)。

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

我发现了一个叫做 颤动交错网景作者: 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
),
);

你可以找到完整的例子(复制,粘贴和运行) 给你

crossAxisCountcrossAxisSpacing和屏幕宽度决定 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,
),
),
);
}

enter image description here

对我有用。

示例代码:

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,
);
},
)

所有的功劳都归功于 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
...

好好利用

 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: [


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

enter image description here

在 GridView 中,使用以下代码:

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

将此行粘贴到 GridView.count 中

儿童人数比例: 2/4,

您可以使用 mainAxisExent 而不是 child Aspectratio

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

MainAxisExent: 150,//这里设置您想要的自定义高度

NOT: Dynamic Height like: (size. Height or other) is NOT Goting to Work 不要: 动态高度,比如: (size。高度或其他)是不起作用的

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'),
);
},
),
),

enter image description here

使用

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,
)

我在任何地方都能看到的第一个解决方案是使用 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],
),

使用选定的突出显示和其他未突出显示的 Gridview 生成

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]);
},
))

这个问题是我的问题之一,但是我发现如果我们把重复小部件放在 SingleChildScrollView 小部件中,重复小部件的高度将是最小的大小!

就是这样!

唯一内置的简单解决方案是,使用 childAspectRatio并根据小部件的大小将其设置为类似于 1 / .61 / .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'))),
},
)),

您需要在 GridViewgridDelegate中使用 childAspectRatio属性:

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