Flutter remove space between GridView row - flutter

I am trying to create a gridview of raised buttons but there is a large amount of space between the rows of of the grid like this picture:
I am implementing the GridView with the code at the bottom of the page:
As you can see I set:
SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 150, mainAxisSpacing: 4, crossAxisSpacing: 4),
I would have expected that setting the main axis spacing and cross axis spacing should remove those spaces.
I also tried returning the gridview in a sized box and changing it to SliverGridWithFixedCount, but nothing seems to be changing it.
I am not sure what I have done incorrectly in the layout?
Thanks for your help
body: Column(
children: <Widget>[
Flexible(
child: filtersGridView(),
),
],
),
);
}
}
class filtersGridView extends StatefulWidget {
#override
_filtersGridViewState createState() => _filtersGridViewState();
}
class _filtersGridViewState extends State<filtersGridView> {
final List <DocumentSnapshot> documents;
_filtersGridViewState({this.documents});
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: Firestore.instance.collection('categories').snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: const Text('Loading events...'));
}
return GridView.builder(
gridDelegate:
SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 150, mainAxisSpacing: 4, crossAxisSpacing: 4),
itemBuilder: (BuildContext context, int index) {
return Column(children: <Widget>[
InkWell(
onTap: () {
},
child: SizedBox(
height: 30,
child: RaisedButton(
color: Colors.white,
child: Row(
children: <Widget>[
Text(snapshot.data.documents[index]['name'].toString(), textAlign: TextAlign.center, style: TextStyle(fontSize: 15, color: Colors.black,),),
],
),

If you are concerned about the padding inside of the buttons - it happens due to the minimum width setting of the material buttons being set to 88.
Also, in my experience I noticed that material buttons have some weird margin around them. I solved that with materialTapTargetSize: MaterialTapTargetSize.shrinkWrap.
ButtonTheme(
minWidth: 0,
height: 30,
child: RaisedButton(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
// ...
)
)
If you want the buttons to fill the entire maxCrossAxisExtent in height - use RawMaterialButton with custom constraints assigned.
Updated
I assumed the problem is within the buttons, but it just occurred to me that it is in fact about the GridView Delegate.
How SliverGridDelegateWithMaxCrossAxisExtent works as per Flutter docs:
This delegate creates grids with equally sized and spaced tiles.
The default value for childAspectRatio property of the delegate is 1.0, i.e. - a square. You are getting a perfectly logical layout displayed - grid of 1:1 blocks.
To solve this you need to come up with the right childAspectRatio value.
Some pseudocode: cellHeight = cellWidth / childAspectRatio.
e.g.
childAspectRatio: 2 will display cells sized as following:
2
-----------------
| |
| | 1
| |
-----------------
Please let me know if this helped.

Try this hope so it will work,
GridView.builder(
itemCount: 6,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3,),
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
child: Flex(
direction: Axis.vertical,
children: <Widget>[
Expanded(flex: 5, child: InkWell(
onTap: (){},
)),
Expanded(
flex: 5,
child: RaisedButton(
onPressed: () {},
child: Text('Bt'),
))
],
),
),
);
},
),

increase the childAspectRatio value to 2,
primary: false,
scrollDirection: Axis.vertical,
// padding: const EdgeInsets.all(10),
crossAxisSpacing: 10,
mainAxisSpacing: 10,
maxCrossAxisExtent: 180.0,
shrinkWrap: true,
childAspectRatio: 2,
children: <Widget>[
container(),
container(),
container(),
])

Related

Flutter: How to expand/collapse (show/hide) a Row in a GridView or Wrap widget

I'm trying to build a GridView where selected the item can expand/collapse (show/hide) a row in the immediate consecutive row of the selected item, like an information box.
On the screenshot below I'm showing an example where the Node 001 is selected.
Don't know if I can achieve this with GridView widget or Wrap Widget, but any help will be very appreciated.
Thanks in advance for your support.
Sample mockup screenshot:
My actual GridView code:
GridView.builder(
shrinkWrap: true,
padding: EdgeInsets.zero,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 5,
mainAxisSpacing: 5,
mainAxisExtent: 40),
itemCount: nodes.length,
itemBuilder: (BuildContext context, int index) {
return TextButton(
onPressed: (selectedNode == nodes[index].slaveId)
? () {}
: () =>
Provider.of<SelectedNode>(context, listen: false)
.value = nodes[index].slaveId!,
style: TextButton.styleFrom(
backgroundColor:
(selectedNode == nodes[index].slaveId)
? selectedColor
: unselectedColor),
child: Text(
'Node ${nodes[index].slaveId.toString().padLeft(3, '0')}'),
);
},
)
Modified code:
SizedBox(
width: double.infinity,
child: SizedBox(
height: MediaQuery.of(context).size.height / 3.2,
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 5,
mainAxisSpacing: 5,
mainAxisExtent: 40),
itemCount: [1, 2, 3, 4, 5, 6, 7, 8, 9].length + 1,
clipBehavior: Clip.none,
itemBuilder: (context, index) {
if (index != 1) {
return Stack(
clipBehavior: Clip.none,
children: [
TextButton(
onPressed: (selectedNode == nodes[index].slaveId)
? () {}
: () => Provider.of<SelectedNode>(context,
listen: false)
.value = nodes[index].slaveId!,
style: TextButton.styleFrom(
backgroundColor:
(selectedNode == nodes[index].slaveId)
? selectedColor
: unselectedColor),
child: Text(
'Node ${nodes[index].slaveId.toString().padLeft(3, '0')}'),
),
index == 0
? Positioned(
child: Text('1hjkkhj'),
bottom: -20,
left: 0,
right: 0,
)
: SizedBox(),
],
);
} else {
return TextButton(
onPressed: (selectedNode == nodes[index].slaveId)
? () {}
: () => Provider.of<SelectedNode>(context,
listen: false)
.value = nodes[index].slaveId!,
style: TextButton.styleFrom(
backgroundColor:
(selectedNode == nodes[index].slaveId)
? selectedColor
: unselectedColor),
child: Text(
'Node ${nodes[index].slaveId.toString().padLeft(3, '0')}'),
);
}
}),
),
),
you can get the selected index and dived it to 4 and round up the result should give you the row number which that item click, so call this selectedRow row. in your grid view you can use selectedRow and add empty space for the next row. After that change item widget that you show in grid view and add it in a stack and add another container that contain your expanded widget. also remember that you should set stack and grid view clipBehavior to Clip.none.
something like this:
ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: [1, 2, 3].length + 1,
clipBehavior: Clip.none,
itemBuilder: (context, index) {
if (index != 2) {
return Stack(
clipBehavior: Clip.none,
children: [
Text('dasdasdasd'),
index == 1
? Positioned(
child: Text('1hjkkhj'),
bottom: -20,
left: 0,
right: 0,
)
: SizedBox(),
],
);
} else {
return SizedBox(
height: 20,
);
}
})
I know it is listview but the concept work in gridview too.

flutter GridView.builder won't let rest of screen scroll

I have GridView.builder middle of my screen but it does not let my screen to scroll! I must find some white space in screen in order to be able to scroll rest of my screen.
Here is how my screen structure looks like:
Widget build(BuildContext context) {
return Scaffold(
appBar: const TopBarFile(),
drawer: const DrawerFile(),
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: FutureBuilder(
future: myCards,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// some widgets...
const SizedBox(height: 15),
// here middle of screen is GridView
SizedBox(
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
shrinkWrap: true,
itemCount: snapshot.data['cards'].length,
itemBuilder: (BuildContext context, int index) {
return InkWell(...);
}
)
),
const SizedBox(height: 15),
// some more widgets...
]
);
} else {...}
return container();
}
),
),
),
),
),
}
Note: this grid view only has 6 items so I don't need it to be scrollable actually I just use it for design purpose but its causing scrolling issue, so I just need to be able to scroll my page even if I'm touching on top of this grid view items.
Any suggestions?
you can define a physics property with NeverScrollableScrollPhysics() value inside GridView, this will solve the issue. Always use physics property When you have nested scrollable widget.
You can give physics: NeverScrollableScrollPhysics() to GridView() or use Wrap() widget instead of GridView().
Use physics and shrinkWrap property as below:
SizedBox(
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: snapshot.data['cards'].length,
itemBuilder: (BuildContext context, int index) {
return InkWell(...);
}
)
),

Container with drop shadow containing a dynamic GridView

So a design requires Containing a GridView of profiles that is dynamic in size.
The one I came up with is something like this:
class Applicants extends StatelessWidget {
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
padding: EdgeInsets.all(20),
child: GridView.builder(
clipBehavior: Clip.none,
itemCount: 10,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemBuilder: (context, index) => _buildBox(context, index),
),
),
),
],
);
}
}
But how do I put them in a Container with a drop shadow that extends with GridView? This list is populated from DB and my attempts of placing the GridView in a Container with DropShadow always fail.
An easy way is using Card and use elevation.
Widget _buildBox(BuildContext context, int index) {
return Card(
elevation: 12,
child: Center(
child: Text("Applicant"),
),
);
}

Container Height for Listview and Gridview

Both Listview and Gridview needs height to be shown into our app.
But each model of phone has different size as height so it is impossible to define static height of the parent container of the list.
What I need to put as height for the container once use listview and gridview to cover all the phone model ?
Code:
Container(
height: 500,
child: GridView.builder(
itemCount: 20,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
itemBuilder: (BuildContext context, int index){
return GestureDetector(
onTap: () => showDialog(
context: context, builder: (context) => AlertDialog(
actions: [
Image.asset(widget.user.imageUrl),
Text(widget.user.name),
],
)
),
child: Card(
elevation: 5,
child: Image.asset(widget.user.imageUrl),
),
);
},
),
),
in this specific case height is 500 and it is good for some mobile phone but for other no, please let me know.
You can use screen height width for make it responsive according to devices:
height: MediaQuery.of(context).size.height * 0.1 //It takes 1 of 10 proportion of screen height
width: MediaQuery.of(context).size.width* 0.2 //It takes 2 of 10 proportion of screen width
You can place your GridView or ListView inside Expanded widget. The Expanded widget will take all the available space & you can easily show your GridView or ListView widget.
This solution will work on multiple screen sizes also without any problem because it will occupy the available space on its own.
Column(
children: [
// Any other widget
Expanded(
child: GridView.builder(
itemCount: 20,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
itemBuilder: (BuildContext context, int index){
return GestureDetector(
onTap: () => showDialog(
context: context, builder: (context) => AlertDialog(
actions: [
Image.asset(widget.user.imageUrl),
Text(widget.user.name),
],
),
),
child: Card(
elevation: 5,
child: Image.asset(widget.user.imageUrl),
),
);
},
),
),
// Any other widget
],
),

Issues with GridView for the following UI

I am trying to construct the following UI, but having issues with that. More specifically I tried different ways of doing this with GridView, but had no luck since I was not able to switch from 2 to 1 for crossAxisCount, because after showing the favorite cakes I need to "come back" to "normal" and show one block on one line for "Similar Products". Here is what I tried:
body: new LayoutBuilder(
builder: (context, constraint) {
return Padding(
padding: const EdgeInsets.fromLTRB(8.0, 0, 8.0, 0),
child: new GridView(
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: constraint.maxWidth / constraint.maxHeight,
),
children: <Widget>[
// those widgets here will be created dynamically at the end
// manually created a few just for illustration
new MyFavoriteWidget(),
new MyFavoriteWidget(),
new MyFavoriteWidget(),
new MyFavoriteWidget(),
new MyFavoriteWidget(),
new MyFavoriteWidget(),
new MyFavoriteWidget(),
new MyFavoriteWidget(),
new Container(
child: Text("Similar Products"),
),
// same for those widgets
new SimilarProductWidget(),
new SimilarProductWidget(),
new SimilarProductWidget(),
new SimilarProductWidget(),
],
),
);
},
),
But this renders just 2 columns, which is what I want for the top part, but after the list of MyFavoriteWidget() I need to switch to normal layout and have one block on one line. And if I take this part out of the GridView it won't be visible. I feel like GridView is still the best way of implementing this but was not able to find a way around it. Even a skeleton code to get started or a hint how to overcome this issue will be much helpful.
I offer you try this code. For top list use GridView.count and for bottom list use ListView.builder. I give you a simple example to understand it. Run code below and see the result.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Your heading'),
),
body: SafeArea(
child: ListView(
shrinkWrap: true,
physics: ScrollPhysics(),
children: [
GridView.count(
crossAxisCount: 2,
scrollDirection: Axis.vertical,
physics: ScrollPhysics(),
shrinkWrap: true,
children: List.generate(
6,
(index) => Container(
margin: EdgeInsets.all(10),
width: 50,
height: 50,
color: Colors.yellow,
),
),
),
ListView.builder(
physics: ScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return Card(
color: Colors.red,
child: Container(
height: 70,
margin: EdgeInsets.all(30),
width: double.infinity,
),
);
},
),
],
)));
}