Flutter ListView.Builder() in scrollable Column with other widgets - flutter

I have a TabBarView() with an amount of different views. I want of them to be a Column with a TextField at top and a ListView.Builder() below, but both widgets should be in the same scrollable area (scrollview). The way I implemented it threw some errors:
#override
Widget build(BuildContext context) {
return new Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: new TextField(
decoration: new InputDecoration(
hintText: "Type in here!"
),
)
),
new ListView.builder(
itemCount: _posts.length, itemBuilder: _postBuilder)
],
);
}
Error:
I/flutter (23520): The following assertion was thrown during performResize():
I/flutter (23520): Vertical viewport was given unbounded height.
I/flutter (23520): Viewports expand in the scrolling direction to fill their container.In this case, a vertical
I/flutter (23520): viewport was given an unlimited amount of vertical space in which to expand. This situation
I/flutter (23520): typically happens when a scrollable widget is nested inside another scrollable widget.
I/flutter (23520): If this widget is always nested in a scrollable widget there is no need to use a viewport because
I/flutter (23520): there will always be enough vertical space for the children. In this case, consider using a Column
I/flutter (23520): instead. Otherwise, consider using the "shrinkWrap" property (or a ShrinkWrappingViewport) to size
I/flutter (23520): the height of the viewport to the sum of the heights of its children.
I read about stacking the ListView.builder() in an Expanded-Area but it made the textfield kind of "sticky" which is not what I want. :-)
I also came across CustomScrollView but didn't fully understand how to implement it.

Here is the solution:
SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(
children: <Widget>[
Text('Hey'),
ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount:18,
itemBuilder: (context,index){
return Text('Some text');
})
],
),
),

Placing the ListView inside an Expanded widget should solve your problem:
#override
Widget build(BuildContext context) {
return new Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: new TextField(
decoration: new InputDecoration(
hintText: "Type in here!"
),
)
),
new Expanded(child: ListView.builder(
itemCount: _posts.length, itemBuilder: _postBuilder))
],
);
}

Use SingleChildScrollView which allows the child widget to scroll
Solution
SingleChildScrollView(
child: Column(
children: <Widget>[
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
Two properties used here
shrinkWrap: true
only occupies the space it needs (it will still scroll when there more items).
physics: NeverScrollableScrollPhysics()
Scroll physics that does not allow the user to scroll. Means only Column+SingleChildScrollView Scrolling work.

Reason for the error:
Column expands to the maximum size in main axis direction (vertical axis), and so does the ListView
Solution
You need to constrain the height of the ListView, so that it does expand to match Column, there are several ways of solving this issue, I'm listing a few here:
If you want to allow ListView to take up all remaining space inside Column use Flexible.
Column(
children: <Widget>[
Flexible(
child: ListView(...),
)
],
)
If you want to limit your ListView to certain height, you can use SizedBox.
Column(
children: <Widget>[
SizedBox(
height: 200, // constrain height
child: ListView(),
)
],
)
If your ListView is small, you may try shrinkWrap property on it.
Column(
children: <Widget>[
ListView(
shrinkWrap: true, // use it
)
],
)

Use physics: NeverScrollableScrollPhysics() and shrinkWrap: true inside ListView.Builder() and enjoy

Here is an efficient solution:
class NestedListExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
const SliverToBoxAdapter(
child: Text('Header'),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(ctx, index) {
return ListTile(title:Text('Item $index'));
},
),
),
],
);
}
}
Here is a preview on dartpad.
You can use a SliverToBoxAdapter for the other children as only Slivers can be a direct child of a CustomScrollView.
If all the list items are the same height, then you could use SliverFixedExtentList, which is more efficient because the height of each child isn't calculated on the fly, but you will have to know the exact pixel height. You could also use a SliverPrototypeExtentList, where you provide the first item in the list(the prototype), and all the other children will use the height of the prototype so you don't need to know the exact height in pixels.

Use Expanded widget to constrain without overflowing those pixels, :)
Column(
children: <Widget>[
Expanded(
child: ListView(),
),
Expanded(
child: ListView(),
),
],
)

just add
Column(
mainAxisSize: MainAxisSize.max, //Add this line onyour column
children:[
SomeWidget(),
Expanded(child:ListView.builder())
]
)

In my case with a future i did it like this:
SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text("Hey ho let's go!"),
Flexible(
child: FutureBuilder(
future: getData(),
builder: (BuildContext context,
AsyncSnapshot<List<Sale>> snapshot) {
if (snapshot.connectionState != ConnectionState.done ||
snapshot.hasData == null) {
return CircularProgressIndicator();
} else {
data = snapshot.data;
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return dataItemWidget(size, data[index], context);
},
itemCount: data.length,
);
}
},
),
),
],
),
),

//If you want Listview.builder inside ListView and want to scroll the parent ListView// //whenever the Items in ListView.builder ends or start you can do it like this
body: ListView(
physics: ScrollPhysics(),
children: [
SizedBox(height: 20),
Container( height: 110.0 *5, // *5 to give size to the container //according to items in the ListView.builder. Otherwise will give hasSize Error
child:ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
itemCount: 5,
itemBuilder: (BuildContext context, int indexChild) {
return InkWell(child:Container(height:100));}))
),]),

The best way will be to make the column scrollable by making the column child of SingleChildScrollView and then assigning the same ScrollController to both the SingleChildScrollView and the ListView.builder. This will make the text field and the below ListView as scrollable.

Just add physics: NeverScrollableScrollPhysics() in ListView.builder() so you can scroll

Add physics: NeverScrollableScrollPhysics() inside Listview.builder() method and the nested Listview will scroll

Column is not scrollable, which is why the TextField on top wouldn't scroll but the ListView on the bottom would.
The best way to solve this in my opinion is to make your TextField the first item in your ListView.
So you won't need a column, your parent widget is the ListView, and its children are the TextField followed by the remaining items you build with _postBuilder.

return Column(
children: [
Text("Popular Category"),
Expanded(
child: ListView.builder(`enter code here`
shrinkWrap: false,
scrollDirection: Axis.horizontal,
itemCount: 3,
itemBuilder: (context, index) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("hello"),
],
);
}),
),
],
);

body: SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(
children: [
getFiltersOnHomePage(),
SizedBox(
child: StreamBuilder(
stream: FirebaseFirestore.instance.collection('posts').snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (ctx, index) => Container(
margin: EdgeInsets.symmetric(
horizontal: width > webScreenSize ? width * 0.3 : 0,
vertical: width > webScreenSize ? 15 : 0,
),
child: PostCard(
snap: snapshot.data!.docs[index].data(),
),
));
},
),
),
],
),
),[enter image description here][1]***You will be able to scroll through the page by using Expanded Widget
Blockquote
Using this you can scroll over the entire page. This page includes a row and a listview builder inside a scrollable column.

In my case, I added a Container with transparent color and height up to 270 to solve this one.
Column(
children: <Widget>[
ListView(
shrinkWrap: true, // use it
),
Container(
color: Colors.transparent,
height: 270.0,
),
],
)

Related

How to get second ListView take all space in Column?

I have got parent Widget:
return Column(children: [
Windget1(), // I tried to make both of them Expanded
Windget2(),
]
Windget1 and Windget2 are ListView and look like:
ListView.builder(
shrinkWrap: true,
controller: ScrollController(),
itemBuilder: (ctx, idx) =>
SingleJobItem(controller.jobList[idx], key: UniqueKey()),
itemCount: controller.jobList.length)
);
At start Widgets1 display containers, that should be moved to Widget2 and Widget2 should take all available space, but it do not happens. It's take only half of size.
I tried different combinations with wrapping Column's widgets in Expand, Tried to remove/add flexible in Widget1/2, but nothing do not work.
What I am seeing on start:
What I am getting at result:
I need to make second take all available space.
I tried to make one of Widget Expanded, but it five me overflow:
return Column(
children: [
Widget1(),
Expanded(child: Widget2())
]
)
Bind your column with SingleChildScrollView
SingleChildScrollView(
child: Stack(
children:[
Column(
children: [
// your widgets
]
),
Align(
alignment: Alignment.bottomCenter,
child: //your button
)
]
)
)
Try below code hope its helpful to you. just try add padding: EdgeInsets.zero,
ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: 10,
physics: ScrollPhysics(),
itemBuilder: (context, index) {
return Container(
child:Text('Add Text! '),
);
},
),
If I got your question correctly then, wrap your Widget2() inside Expanded and do not use any Expanded in Widget1():
return Column(children: [
Windget1(),
Expanded(child: Windget2()),
]

How to stop listview from overflowing inside a column?

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

Flutter: unexpected space at the top of ListView

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

Vertical ListView inside horizontal SingleChildScrollView

I need to build a page with lists of objects that are grouped in a single line. This needs to be scrolled both vertically (to show more groups) and horizontally (to show groups children).
The vertical and horizontal scroll needs to move the items altogether.
Something like this: https://gph.is/g/46gjW0q
I'm doing this with this code:
Widget _buildGroups() {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Container(
width: 2000,
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: boardData.length,
itemBuilder: (context, index) {
return Row(children: _buildSingleGroup(index));
},
),
),
);
}
List<Widget> _buildSingleGroup(int groupIndex) {
return List.generate(...);
}
I need this to be horizontally dynamic but here, as you can see, I need to set a width cause otherwise an error occurs:
══════════════════ Exception caught by rendering library ═════════════════════
The following assertion was thrown during performResize()
Vertical viewport was given unbounded width.
Viewports expand in the cross axis to fill their container and constrain their
children to match their extent in the cross axis. In this case, a vertical
viewport was given an unlimited amount of horizontal space in which to expand.
I tried to set "shrinkWrap: true" in the ListView but the result gives another error:
Failed assertion: line 1629 pos 16: 'constraints.hasBoundedWidth': is not true.
So how do you do this with a dynamical ListView width?
The error happens because when ListView tries to get height from its parent to fill the entire height, the parent returns infinity height because SingleChildScrollView doesn't have fixed container height value. To fix that issue you need to limit the height value. For example, I used SizedBox to specify height for ListView.
The following code works for me.
SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
height: 120, // <-- you should put some value here
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: items.length,
itemBuilder: (context, index) {
return Text('it works');
},
),
),
],
),
);
Please can you explain your guess about the objects size?
I have a solution, but in my solution , flutter will build all objects (In both axes) for first frame. And build again any setState in the three. So its not good solition.
Last scroll offset horizontally
and
But I'll explain anyway.
///
class BothAxisScroll extends StatefulWidget {
#override
_BothAxisScrollState createState() => _BothAxisScrollState();
}
class _BothAxisScrollState extends State<BothAxisScroll> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.all(30),
child: Container(
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: _build(),
),
),
),
),
),
);
}
}
List<Widget> _build() {
return List.generate(
20,
(index) => Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: _buildSingleGroup(index),
));
}
List<Widget> _buildSingleGroup(int _index) {
return List.generate(
Random().nextInt(20),
(index) {
print('BUILD: $_index');
return Container(
padding: const EdgeInsets.all(15), child: Text("$_index-$index"));
},
);
}
and result in first frame :

Flutter Gridview in Column. What's solution..?

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