Reputation: 462
Is there a way to build a grid that's not scrollable in itself and which size is according to its children, the same way we can specify in a row or column mainAxisSize: MainAxisSize.min?
To give you the big picture -
I'm trying to create a responsive layout that depends on the device's width. It should be split into 2 parts, connected seamlessly via a column.
1) 2 big containers which sizes depend on the screen width, taking into account a small space in between them. Each container's width and height will be the same (square containers).
2) Same idea, but instead have 3 rows, each consisting of 3 smaller containers. This creates a grid. It's very important though that the grid won't be scrollable in itself and that its size will be according to its children. It should only be scrolled together with the rest of the page that's contained in a SingleChildScrollView.
Especially since each container's height needs to be the same as its width, I was thinking of going with a combination of rows, columns, and LayoutBuilder - they gives me all the capabilities I need.
However, before doing things manually, I was wondering if there's something that could work out of the box.
Upvotes: 26
Views: 25104
Reputation: 162
GridView(){
...
shrinkWrap: true, //This line prevents GridView from occupying as much space as there is.
physics: NeverScrollableScrollPhysics(), // This attr. disables user scroll.
...
}
Set these properties and you are good to go.
Upvotes: 1
Reputation: 2124
For my case, I use **
NeverScrollableScrollPhysics()
**
GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: listPaymentMethods.length,
primary: false,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final paymentMethod = listPaymentMethods[index];
bool isSelected = paymentMethod.code == selectedPaymentMethod?.code;
return PaymentMethodItemWidget(
paymentMethod: paymentMethod,
onPaymentMethodChanged: onPaymentMethodChanged,
isSelected: isSelected);
});
Upvotes: 7
Reputation: 739
You can create a custom widget for it
import 'package:flutter/material.dart';
class StaticGrid extends StatelessWidget {
const StaticGrid({
Key? key,
this.columnCount = 2,
this.gap,
this.padding,
this.columnMainAxisAlignment = MainAxisAlignment.start,
this.columnCrossAxisAlignment = CrossAxisAlignment.center,
this.rowMainAxisAlignment = MainAxisAlignment.start,
this.rowCrossAxisAlignment = CrossAxisAlignment.center,
required this.children,
}) : super(key: key);
final int columnCount;
final double? gap;
final EdgeInsets? padding;
final MainAxisAlignment columnMainAxisAlignment;
final CrossAxisAlignment columnCrossAxisAlignment;
final MainAxisAlignment rowMainAxisAlignment;
final CrossAxisAlignment rowCrossAxisAlignment;
final List<Widget> children;
@override
Widget build(BuildContext context) {
return Container(
padding: padding,
child: Column(
crossAxisAlignment: columnCrossAxisAlignment,
mainAxisAlignment: columnMainAxisAlignment,
children: _createRows(),
),
);
}
List<Widget> _createRows() {
final List<Widget> rows = [];
final childrenLength = children.length;
final rowCount = (childrenLength / columnCount).ceil();
for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
final List<Widget> columns = _createCells(rowIndex);
rows.add(
Row(
crossAxisAlignment: rowCrossAxisAlignment,
mainAxisAlignment: rowMainAxisAlignment,
children: columns,
),
);
if (rowIndex != rowCount - 1) {
rows.add(SizedBox(height: gap));
}
}
return rows;
}
List<Widget> _createCells(int rowIndex) {
final List<Widget> columns = [];
final childrenLength = children.length;
for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) {
final cellIndex = rowIndex * columnCount + columnIndex;
if (cellIndex <= childrenLength - 1) {
columns.add(Expanded(child: children[cellIndex]));
} else {
columns.add(Expanded(child: Container()));
}
if (columnIndex != columnCount - 1) {
columns.add(SizedBox(width: gap));
}
}
return columns;
}
}
how to use it is the same as using the column or row widget, you can define column count, gap, padding, and margin.
StaticGrid(
gap: 16,
padding: const EdgeInsets.all(16),
children: [
Text("1"),
Text("2"),
Text("3"),
Text("4"),
Text("5"),
Text("6"),
],
);
Upvotes: 6
Reputation: 305
The table widget might be an easier way to do this than GridView (janstol's answer) as it's purpose built for what you're looking for. Though this doesn't apply to your case, Table is especially useful if your widgets can be different sizes-it'll align widgets vertically and horizontally relative to the widest/tallest widget in each column/row (respectively).
Here's janstol's solution code rewritten to use Table. Note that Table requires all rows to be of the same length, so your first two element row needs to be outside of the table.
Widget bigBox = Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: AspectRatio(
aspectRatio: 1.0,
child: Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(width: 3.0, color: Colors.green),
),
),
),
),
);
Widget smallBox = Padding(
padding: const EdgeInsets.all(10.0),
child: AspectRatio(
aspectRatio: 1.0,
child: Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(width: 3.0),
),
),
),
);
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
Row(
children: List.filled(2, bigBox),
),
Container(
child: Table(
children: List.filled(
// 7 rows instead of 3 just to demonstrate the scrolling functionality
7,
TableRow(
children: List.filled(3, smallBox),
)
),
),
),
],
),
),
Upvotes: 5
Reputation: 3157
Something like this?
SingleChildScrollView(
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: AspectRatio(
aspectRatio: 1.0,
child: Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(width: 3.0, color: Colors.green),
),
),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: AspectRatio(
aspectRatio: 1.0,
child: Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(width: 3.0, color: Colors.green),
),
),
),
),
),
],
),
Container(
padding: const EdgeInsets.all(10.0),
child: GridView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 1.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
),
itemCount: 21,
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
border: Border.all(width: 3.0),
),
);
},
),
),
],
),
)
Upvotes: 48