I'm having a bit of annoyance. I want a scroll bar present in my flutter web 'page' at all times, to indicate to the user that there's still some elements to view them. And I achieved this using scrollbar isAlwaysShown to true in my Theme class.
Now, when I use GridView.builder to generate elements on the screen, I must provide height constraints beforehand, or else I'll get an error that says 'height is infinite'.
The problem with this is, there are 2 scroll bars visible at all times. One from the SingleChildScrollView, and one from the GridView.builder. I need the SingleChildScrollView with the column so I can have a footer at the bottom of the page.
My question is, how can I get rid of the GridView.builder scroll bar?
Thanks in advance...
My code:
Scaffold(
key: _scaffoldKey,
appBar: AppBar(),
drawer: const DrawerWidget(),
body: SingleChildScrollView(
child: Column(
children: [
Container(
constraints: BoxConstraints(maxHeight: _size.height),
margin: Responsive.isDesktop(context)
? const EdgeInsets.symmetric(vertical: 10)
: null,
padding: Responsive.isDesktop(context)
? desktopPadding
: smallAndMediumPadding,
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4),
itemCount: 12,
itemBuilder: (BuildContext context, int index) {
return GridCard(name: '', image: '');
}
)
),
/////////////// Footer ///////////////////
Footer()
]
)
)
)
Actually you can remove the BoxConstraints from your Container and to solve the height issue you have a property in GridView.builder named shrinkWrap. You just need to set it true and it will solve your height issue.
shrinkWrap: true,
and after that you may have issue with Scrolling the GridView.builder so to solve that use another property primary and set it to false
primary : false,
You shouldn't use multiple Scrollables inside each other to get footer functionality. By using shrinkWrap on the inner scrollable (GridView) you'd effectively turn it into a regular unscrollable widget and use scrolling of the outer view (SingleChildScrollView). It is terrible for performance because Flutter would have to build, layout and (probably) render entire GridView including all the items every time.
You should instead use CustomScrollView to display your grid and footer like this:
Scaffold(
body: CustomScrollView(
slivers: [
SliverPadding(
// not sure what you were trying to do with your padding
padding: (Responsive.isDesktop(context)
? const EdgeInsets.symmetric(vertical: 10)
: EdgeInsets.zero) +
(Responsive.isDesktop(context)
? desktopPadding
: smallAndMediumPadding),
sliver: SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
),
delegate: SliverChildBuilderDelegate(
(context, index) => GridCard(name: '', image: ''),
childCount: 12,
),
),
),
SliverToBoxAdapter(
child: Footer(),
),
],
),
)
Related
I have a relatively complex Listview setup, where one Listview acts as a scrolling parent of a horizontal Listview, which acts as a parent to a third vertical Listview.
Here is an image of the general idea of the layout: https://cdn.discordapp.com/attachments/752981111738466467/895739370227773480/IMG_20211007_142732.jpg.
I'm having trouble getting the middle Listview, the horizontal Listview (marked 2 in the image), to scroll.
ConstrainedBox(
constraints: BoxConstraints(...),
child: ListView.builder( // This is Listview 1
controller: ScrollController(),
itemCount: itemCount,
itemBuilder: (context, worldIndex) {
return ConstrainedBox(
constraints: BoxConstraints(...),
child: ListView.builder( // This is Listview 2
controller: ScrollController(),
itemCount: ...,
scrollDirection: Axis
.horizontal, // grows horizontally to show the hierarchy of one card (the card selected in the hierarchy above it, or for the first level, the world) to its children
itemBuilder: (context, index) {
...
return ConstrainedBox(
constraints: BoxConstraints(...),
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Container(
decoration: ...,
),
child: ListView.builder( // This is Listview 3
controller: ScrollController(),
itemCount: getNumChildren(index),
itemBuilder: (context, index2) {
return ...;
}),
)));
}));
}));
For brevity, I have removed several parts of my code and replaced them with elipses. I don't think it is likely that any of these areas could cause any issues with the Listview, but please let me know if they could.
Edit: The code I have already works properly, aside from the horizontal Listview not scrolling. My solution needs to have a dynamically expandable Listview at each level of the tree (1, 2, and 3, in the image, for example). The primary target platform for this is Windows.
I'm assuming the issue involves a problem with which Listview wins the GestureArena, but I am currently at a loss in how to work around that issue, providing a way to scroll all of available Listviews.
Thank you in advance, and I hope you have a great day!
As I could understand it. Maybe you are looking for something like this? I have commented the 3 types of scrolls you need. If anything is not as expected, please mention.
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
for (var i = 0; i < 5; i++)
// One big section in the largest col (1).
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 200.0,
child: ListView.builder(
// Horizontal item builder. (2)
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: 10,
itemBuilder: (context, index) {
return SingleChildScrollView(
child: Column(
// Inside one vertical section. (3)
children: [
for (var i = 0; i < 5; i++)
ElevatedButton(
onPressed: () {}, child: Text("OK")),
],
),
);
},
),
),
],
),
],
),
),
),
),
);
}
The solution to this turned out to be that Flutter has intentionally turned off the ability to scroll horizontally using both scroll wheel and dragging for Windows (or at least, that's what seems to be the case according to what I was able to find in this issue).
To solve that, they have made a migration guide here.
Following the guide, it is extremely simple to override the drag devices used in order to re-enable the intended drag scrolling. This still does not enable scrolling horizontally with a mouse wheel, but for my code that is not an issue.
class CustomScrollBehavior extends MaterialScrollBehavior { // A custom scroll behavior allows setting whichever input device types that we want, and in this case, we want mouse and touch support.
#override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.mouse,
PointerDeviceKind.touch,
};
}
Then, in the actual Listview's code, we set a ScrollConfiguration with this new class as its behavior.
ScrollConfiguration(
behavior: CustomScrollBehavior(),
child: Listview.Builder(
controller: ScrollController(),
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
...
})
So basically I ListView.builder widget inside of a fixed container.
Container(
height: 500,
child: TabBarView(
children: [
Container(
height: 500,
color: Colors.red,
child: ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: 100,
itemBuilder: (context, index) {
return Center(child: Text("This is index $index"));
},
),
),
Center(child: Text("Second Tab")),
Center(child: Text("Third Tab")),
],
),
),
Issue:
The issue with this widget is that since it is inside a container of a fixed height, the content of the list is cut off. I want the list to scroll along with the rest of the page so I don't want to remove the shrinkWrap nor NeverScrollableScrollPhysics(). I want it so that the list takes up as much room as it needs.
Things I've tried:
Wrap the ListView with a Column: this causes the widget to overflow and when I add the SingleChildScrollView around that Column, it removes the functionality of the shrinkWrap and NeverScrollableScrollPhysics().
Checked this answer. The parent widget is actually a CustomScrollView and this widget is inside of a SliverToBoxAdapter. That's why the container has a fixed size in the first place.
What can I do to get the ListView to take up as much room as it want? Thank you!
Wrap your container with SingleChildScrollView and make scrollPhysics
SingleChildScrollView(
Container(
height: 500,
color: Colors.red,
child: ListView.builder(
shrinkWrap: true,
physics: ScrollPhysics(),
itemCount: 100,
itemBuilder: (context, index) {
return Center(child: Text("This is index $index"));
},
),
),
)
I have the following source code:
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
controller: scrollController,
slivers: <Widget>[
SliverList(
delegate: SliverChildBuilderDelegate(
(context, cardIndex) {
return Container(
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Main Course',
style: kRestaurantMenuTypeStyle,
),
ListView.builder(
itemCount: menuCards[cardIndex].menuItems.length,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
itemBuilder: (context, itemIndex) {
return RestaurantMenuItem(
menuItem: menuCards[cardIndex].menuItems[itemIndex],
);
},
),
],
),
);
},
childCount: menuCards.length,
),
),
],
),
);
}
Unfortunately, the ListView.builder() creates this extra space on top automatically. This is shown in the image below. That is the big white space between the 'Main Course' and 'Pancit Malabon' texts.
I don't understand why ListView does that. How do I remove the space?
To avoid this behaviour of listview, override padding property with a zero [padding]
return ListView.builder(
padding: EdgeInsets.zero,
itemCount: data.items.length,
itemBuilder: (context, index) {}
);
Looking at your screenshot, the ListView scrolls close to the top of the screen and by default, ListView adds a padding to avoid obstructing the system UI. So a zero padding would remove the extra space.
By default, ListView will automatically pad the list's scrollable
extremities to avoid partial obstructions indicated by MediaQuery's
padding. To avoid this behavior, override with a zero padding
property.
Source : ListView
I solved the issue by adding a padding to my list view like so:
ListView.builder(
padding: EdgeInsets.only(top: 0.0),
...
),
I don't understand why the solution works. If someone can explain the bug, I can accept theirs as the correct answer.
You can wrap your listview with MediaQuery.removePadding
MediaQuery.removePadding(
context: context,
removeTop: true,
child: ListView(...),
)
Hi I was wondering it is possible to have a grid view within a tabbarview which has properties of a sliver so it moves the app bar.
I have a grid view within a tabbarview however it does not cause the tab bar to collapse
This is my code so far
NestedScrollView(
headerSliverBuilder: (context, value) {
return SliverAppBar(
backgroundColor: Colors.white,
floating: true,
pinned: false,
stretch: false,
snap: false,
(...)///Collapsible space etc
);
},
body: TabBarView(
children: [
///Other widgets
(...)
///My gridview
GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2),
children: List.generate(10, (index) {
return Container(
height: 100.0,
width: 100.0,
color: Colors.pink,
child: Text(index.toString()),
);
}),
)
]
)
)
Edit Below
[EDIT]
Set shrinkWrap to true
Sets physics to NeverScrollableScrollPhysics()
set shrinkWrap to true and physics to NeverScrollableScrollPhysics() to your GridView.
This is useful when you add a scrollable widget inside another scrollable widget.
I have a problem with gridview and column. In this case, i want put an image in upper of gridview. Please give me a solution..
return new Container(
child: new Column(
children: <Widget>[
new Container(
child: new Image.asset(
"assets/promo.png",
fit: BoxFit.cover,
),
),
new Container(
child: new GridView.count(
crossAxisCount: 2,
childAspectRatio: (itemWidth / itemHeight),
controller: new ScrollController(keepScrollOffset: false),
shrinkWrap: true,
scrollDirection: Axis.vertical,
children: new List<Widget>.generate(16, (index) {
return new GridTile(
header: new Container(
padding: const EdgeInsets.all(10.0),
alignment: Alignment.centerRight,
child: new Icon(
Icons.shopping_cart,
size: 20.0,
color: Colors.red,
),
),
child: new MyList(
nomor: '$index',
));
}),
),
),
],
),
);
and this is the result:
Flutter Gridview in Column
You just need to put your grid view into Expanded widget, for example:
body: new Column(
children: <Widget>[
new Expanded(
child: GridView.count(
// Create a grid with 2 columns. If you change the scrollDirection to
// horizontal, this would produce 2 rows.
crossAxisCount: 2,
// Generate 100 Widgets that display their index in the List
children: List.generate(10, (index) {
return _buildCard(index);
}),
),
),
new Text("text")
],
),
Reason for the error:
Column expands to the maximum size in main axis direction (vertical axis), and so does the GridView (scroll direction is vertical by default)
Solution
You need to constrain the height of the GridView, so that it expands to fill the remaining space inside Column, there are several ways of solving this issue, use whichever suits you better.
If you want to allow GridView to take up all remaining space inside Column use Flexible.
Column(
children: <Widget>[
Flexible(
child: GridView(...),
)
],
)
If you want to limit your GridView to certain height, you can use SizedBox.
Column(
children: <Widget>[
SizedBox(
height: 200, // constrain height
child: GridView(...),
)
],
)
If your GridView is small, you may try shrinkWrap property on it.
Column(
children: <Widget>[
GridView(
shrinkWrap: true, // use it
)
],
)
If Column is the parent of GridView then it will give rendering issue as
It happens because Column and GridView both take the entire space of the screen individually which is there default behavior(Default axis of scrolling is Vertical).
Solution:
To solve the above problem we have to disable scrolling of GridView, This can be possible by shrinkWrap and physics property
shrinkWrap:true - With this GridView only occupies the space it needs
physics: NeverScrollableScrollPhysics() - It disables scrolling functionality of GridView, which means now we have only SingleChildScrollView who provide the scrolling functionality.
Code:
SingleChildScrollView
Column
GridView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
//...
)
The answer mentioned by #RuslanLeshchenko is correct but if it is still not working for you, trying the following properties on GridView
Try changing shrinkwrap to true
Apply physics: BouncingScrollPhysics()
Inside your Gridview make sure to add this 2 properties.
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
Just wrap with scaffold and scrollview
Scaffold(
resizeToAvoidBottomInset: true,
body: SingleChildScrollView(
child: Column(
children: [
...
Tested working on dialog model aswell.
I had the same problem and couldn't fix.
Then again I used a GridView.builder().
I changed it to GridView.count and added shrinkWrap: true and physics: NeverScrollableScrollPhysics() as mentioned in the other comments.
It works fine now. Thanks!
You can solve it by these two properties and change them as follows :
shrinkWrap: true,
primary: false,
shrinkWrap will wrap your and will handle overflow.
Through primary we can set either you want to make this list scrollable or it's parent list. By setting it to false it allows to take scroll of parent list or scrollable view.
NOTE : You can also wrap your column with SingleChildScrollView to have scroll in your view.
Happy Fluttering !
Try changing childAspectRatio property of GridView to 0.8 or lower.
body:Container(
body: Container(
child: Column(
children: [
Text('Exemple'),
Expanded(
child: GridView.count(
children: <Widget>[
//Widgets
]
)
)
]
)
)
Just wrap GridView inside ListView
Column(
children: <Widget>[
ListView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
children: [
GridView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
),
],
),
)
Also it works with SingleChildScrollView
SingleChildScrollView(
child: Column(
children: <Widget>[
ListView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
children: [
GridView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
),
],
),
)
)
Works like a charm (: