我对Flutter中GridView的使用感到困惑。我想显示功能列表及其费率。功能组件/小部件的高度并没有那么大,但是当我将它们放置在 GridView 中时,每个单元格的高度比所需的要大得多。
如何控制每个箱子的高度以适合孩子的身高?
这是我的代码:
import 'package:flutter/material.dart';
import 'package:project/Components/ReviewComps/AttributeRatings.dart';
class ReviewPage extends StatelessWidget {
const ReviewPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: const <Widget>[
AttributeRatings(),
],
),
),
));
}
}
这是我的 GridView,它产生了问题。
import 'package:flutter/material.dart';
class AttributeRatings extends StatelessWidget {
const AttributeRatings({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
var featureList = [
_featureItems(4),
_featureItems(3.2),
_featureItems(1),
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text("Features average rating"),
GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 20,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: featureList,
),
],
);
}
}
Container _featureItems(double rate) {
return Container(
color: Colors.red,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Feature'),
Text(rate.toString()),
],
),
Stack(
children: [
Container(
height: 2,
width: double.infinity,
color: const Color(0xffDBDFED),
),
FractionallySizedBox(
widthFactor: rate / 5,
child: Container(
height: 2,
color: const Color(0xff39B0EA),
),
)
],
)
],
),
);
}
您可以给
childAspectRatio
自定义高度如下
import 'package:flutter/material.dart';
class AttributeRatings extends StatelessWidget {
const AttributeRatings({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
var featureList = [
_featureItems(4),
_featureItems(3.2),
_featureItems(1),
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text("Features average rating"),
GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 20,
childAspectRatio: 4,//You may have to calculate based on screen size
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: featureList,
),
],
);
}
}
Container _featureItems(double rate) {
return Container(
color: Colors.red,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Feature'),
Text(rate.toString()),
],
),
Stack(
children: [
Container(
height: 2,
width: double.infinity,
color: const Color(0xffDBDFED),
),
FractionallySizedBox(
widthFactor: rate / 5,
child: Container(
height: 2,
color: const Color(0xff39B0EA),
),
)
],
)
],
),
);
}
我发现的解决方案非常复杂,但我将网格视图更改为列表视图并将行放入其中。最困难的是使用奇数长度列表,这是我的解决方案:
import 'package:flutter/material.dart';
class AttributeRatings extends StatelessWidget {
const AttributeRatings({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
var featureList = [4, 3.2, 4, 2, 3.2];
double nbRows = featureList.length % 2 == 0
? (featureList.length / 2)
: (featureList.length / 2) + 1;
var reworkedFeatureList = [];
if (featureList.length % 2 == 0) {
for (var i = 0; i < (featureList.length - 1) / 2; i++) {
reworkedFeatureList.add([featureList[i * 2], featureList[(i * 2) + 1]]);
}
} else {
final retVal = featureList.removeLast();
for (var i = 0; i < (featureList.length - 1) / 2; i++) {
reworkedFeatureList.add([featureList[i * 2], featureList[(i * 2) + 1]]);
}
reworkedFeatureList.add([retVal, -1]);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text("Features average rating"),
ListView.separated(
separatorBuilder: (context, index) {
return const SizedBox(height: 12);
},
itemCount: nbRows.toInt(),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) =>
_featureItemsRow(reworkedFeatureList[index]),
),
],
);
}
}
Row _featureItemsRow(var rates) {
return Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 10.0),
child: _featureItem(rates[0].toDouble()),
)),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 10.0),
child: _featureItem(rates[1].toDouble()),
)),
],
);
}
Column _featureItem(double rate) {
if (rate == -1) {
return Column();
} else {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Feature'),
Text(rate.toString()),
],
),
Stack(
children: [
Container(
height: 2,
width: double.infinity,
color: const Color(0xffDBDFED),
),
FractionallySizedBox(
widthFactor: rate / 5,
child: Container(
height: 2,
color: const Color(0xff39B0EA),
),
)
],
)
],
);
}
}
我有一个类似的问题,我在
GridView.builder
内有一个SingleChildScrollView
。现在的问题是你不能在 GridView
内创建 SingleChildScrollView
因为两者都会尝试占用尽可能多的可用空间,这使得高度无界/无限。
所以解决方案是用
GridView
包裹 SizedBox
,我总是更喜欢使用 GridView.Builder
。
现在的实际问题是,“当列表是动态时,我如何知道
SizedBox
的大小?”
所以我编写了这段代码,其逻辑是根据
SizedBox
的
childAspectRatio
属性动态计算
GridView
的高度
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return const MaterialApp(title: 'Dynamic GridView', home: Home());
}
}
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
final double width = MediaQuery.of(context).size.width;
final double height = MediaQuery.of(context).size.height;
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
itemGrid(width),
],
),
),
);
}
Widget itemGrid(double width) {
const int count = 16;
const int itemsPerRow = 2;
const double ratio = 1 / 1;
const double horizontalPadding = 0;
final double calcHeight = ((width / itemsPerRow) - (horizontalPadding)) *
(count / itemsPerRow).ceil() *
(1 / ratio);
return SizedBox(
width: width,
height: calcHeight,
child: GridView.builder(
padding: const EdgeInsets.symmetric(horizontal: horizontalPadding),
itemCount: count,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
mainAxisSpacing: 0,
crossAxisSpacing: 0,
crossAxisCount: itemsPerRow,
childAspectRatio: ratio),
itemBuilder: (context, index) {
return SizedBox(
child: Card(
clipBehavior: Clip.hardEdge,
child: Column(
children: [
Expanded(
child: Image.network(
"https://picsum.photos/200?${DateTime.now().millisecondsSinceEpoch.toString()}")),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 4.0),
child: Text(
"Lorem Ipsum is a dummy text, lorem ipsum",
maxLines: 3,
overflow: TextOverflow.ellipsis,
style:
TextStyle(fontSize: 10, fontWeight: FontWeight.bold),
textAlign: TextAlign.start,
),
),
],
),
),
);
},
),
);
}
}
指向工作 Dart 要点的链接 - https://dartpad.dev/?id=05ba8804b19a2978a087c68622000a01
说明
count
是项目总数或 itemCount
。itemsPerRow
是一行中的项目数或 crossAxisCount
。ratio
是 childAspectRatio
的 GridView
属性,通常用于设置网格内项目的大小。 ratio
计算为宽度/高度。horizontalPadding
是给GridView
的水平填充(在垂直列表的情况下)calcHeight
是 SizedBox
的计算高度,因此在动态列表的情况下不需要给出固定高度。