This is regarding the package inview_notifier_list. t
I tried to modify the official example with the video auto-playing in a list when it comes into view (example 3). I however came across a small challenge.
In the official example, InViewNotifierList returns a Container with a fixed height.
I realized that when I don't give the container a height, nothing is shown on the screen and when I give the Container a very big height, say 800.0, the children begin to look ugly.
How can I approach a scenario where I don't want the container to have a fixed height, for example in a scenario where I want to return a column widget with a text, a video and a button as children and would like the height of the container to adjust itself automatically in relation to the length of the text, something like this:
class VideoList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return InViewNotifierList(
scrollDirection: Axis.vertical,
initialInViewIds: ['0'],
isInViewPortCondition:
(double deltaTop, double deltaBottom, double viewPortDimension) {
return deltaTop < (0.5 * viewPortDimension) &&
deltaBottom > (0.5 * viewPortDimension);
},
itemCount: 10,
builder: (BuildContext context, int index) {
return Container(
//I would like to have a flexible height here
height: 400,
child: InViewNotifierWidget(
id: '$index',
builder: (BuildContext context, bool isInView, Widget? child) {
return Column(
children: [
Expanded(
child: Text("Some long text from a database"),
),
Expanded(
child: VideoWidget(
play: isInView,
url:
'https://cdn.videvo.net/videvo_files/video/premium/video0392/large_watermarked/906_906-1044_preview.mp4'),
),
Container(
height: 40,
child: Text("Button"),
)
],
);
},
),
);
},
);
}
}
Thank you very much.
It's because you are using a Column as items of a vertically scrolling list. What's worse, you added Expanded inside the Column to tell it to "occupy all available space (be as tall as possible)", but a vertically scrolling list (viewport) has infinite vertical space - that's a conflict and will throw an error for you.
Remove the Expanded should fix the problem. If you want to insert white spaces in a Column you can use something like SizedBox(height: 200). If you have more complex needs, you should look into SliverFillViewport and SliverFillRemaining widgets. If you want to get total available space, try LayoutBuilder or MediaQuery widgets.
I am developing a Flutter app and would like to keep my button always in the bottom of the screen UNLESS what is in top of it is high enough to make the screen scroll. Examples:
Example A (what is on top of the button does not have height enough to scroll the screen)
Example B (what is on top of the button has height enough to scroll the screen, so the button will just go offscreen and allow the screen to scroll)
So summarizing: the button should be forced to stay at the bottom of the screen when the rest of the list is shorter than the device height, but if the list height is greater than the device height the button should behave normally and stay below the list.
What I tried:
Using a ListView, which will normally scroll if necessary, but I couldn't find a way to send the button to the botton of the screen;
Using a Column. With the help of a Spacer I could make the button go to the bottom of the screen, but a Column will not scroll, and if I add a SingleChildScrollViewto wrap it, the Spacer will no longer work because SingleChildScrollView has the potential to have infinite height;
Thanks.
I got it. The right way of doing this is using LayoutBuilder, ConstrainedBox, IntrinsicHeight and Expanded, like this:
(In this example, _widgets is a list of widgets that I want it to be on top. _buttons is a list of buttons that I want to be in the bottom)
return Scaffold(
appBar: buildAppBar(),
body: LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: IntrinsicHeight(
child: Column(
children: <Widget>[
Expanded(
child: Column(
children: _widgets,
),
),
Column(
children: _buttons,
)
],
),
),
),
);
},
),
);
The documentation says to avoid this, though, as it can be expensive. You can read more about this solution and other details in the documentation.
Stack(
children:[
//Use list widget,
yourList.length<7? Postioned(
bottom :10, margin from bottom
child: //button widget):Container()
])
In listbuilder
ListView.builder(
physics: ScrollPhysics(),
itemCount: yourList+1,
itemBuilder: (BuildContext ctxt, int index) {
if (index== yourList.length) {
return yourList.length >7? //your button widget:Container();
} else {
return //list item}
});
This is my code:
#override
Widget build(BuildContext context) {
return new Material(
color: Colors.deepPurpleAccent,
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children:<Widget>[new GridView.count(crossAxisCount: _column,children: new List.generate(_row*_column, (index) {
return new Center(
child: new CellWidget()
);
}),)]
)
);
}
Exception as follows:
I/flutter ( 9925): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter ( 9925): The following assertion was thrown during performResize():
I/flutter ( 9925): Vertical viewport was given unbounded height.
I/flutter ( 9925): Viewports expand in the scrolling direction to fill their container.In this case, a vertical
I/flutter ( 9925): viewport was given an unlimited amount of vertical space in which to expand. This situation
I/flutter ( 9925): typically happens when a scrollable widget is nested inside another scrollable widget.
I/flutter ( 9925): If this widget is always nested in a scrollable widget there is no need to use a viewport because
I/flutter ( 9925): there will always be enough vertical space for the children. In this case, consider using a Column
I/flutter ( 9925): instead. Otherwise, consider using the "shrinkWrap" property (or a ShrinkWrappingViewport) to size
I/flutter ( 9925): the height of the viewport to the sum of the heights of its children.
I/flutter ( 9925):
I/flutter ( 9925): When the exception was thrown, this was the stack:
I/flutter ( 9925): #0 RenderViewport.performResize.<anonymous closure> (package:flutter/src/rendering/viewport.dart:827:15)
I/flutter ( 9925): #1 RenderViewport.performResize (package:flutter/src/rendering/viewport.dart:880:6)
I/flutter ( 9925): #2 RenderObject.layout (package:flutter/src/rendering/object.dart:1555:9)
Adding this two lines
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
...
This generally happens when you try to use a ListView/GridView inside a Column, there are many ways of solving it, I am listing few here.
Wrap ListView in Expanded
Column(
children: <Widget>[
Expanded( // wrap in Expanded
child: ListView(...),
),
],
)
Wrap ListView in SizedBox and give a bounded height
Column(
children: <Widget>[
SizedBox(
height: 400, // fixed height
child: ListView(...),
),
],
)
Use shrinkWrap: true in ListView.
Column(
children: <Widget>[
ListView(
shrinkWrap: true, // use this
),
],
)
So, everyone posted answers but no one cared to explain why:
I'll copy the documentation about shrinkWrap:
Whether the extent of the scroll view in the [scrollDirection] should be
determined by the contents being viewed.
If the scroll view does not shrink wrap, then the scroll view will expand
to the maximum allowed size in the [scrollDirection]. If the scroll view
has unbounded constraints in the [scrollDirection], then [shrinkWrap] must
be true.
Shrink wrapping the content of the scroll view is significantly more
expensive than expanding to the maximum allowed size because the content
can expand and contract during scrolling, which means the size of the
scroll view needs to be recomputed whenever the scroll position changes.
Defaults to false.
So, taking the vocabulary of the docs, what's happening here is that our ListView is in a situation of unbounded constraints (in the direction that we are scrolling), thus the ListView will complain that:
... a vertical viewport was given an unlimited amount of vertical space in
which to expand.
By simply setting shrinkWrap to true will make sure that it wraps its size defined by the contents. A sample code to illustrate:
// ...
ListView(
// Says to the `ListView` that it must wrap its
// height (if it's vertical) and width (if it's horizontal).
shrinkWrap: true,
),
// ...
That's what is going with your code, nonetheless, #Rémi suggestion is the best for this case, using Align instead of a Column.
put grid view inside Flexible or Expanded widget
return new Material(
color: Colors.deepPurpleAccent,
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children:<Widget>[
Flexible(
child: GridView.count(crossAxisCount: _column,children: new List.generate(_row*_column, (index) {
return new Center(
child: new CellWidget(),
);
}),))]
)
);
Although shrinkWrap do the thing, but you can't scroll in ListView.
If you want scrolling functionality, you can add physics property:
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
...
Cause of Viewport Exception
GridView / ListView grow until constraints (limits) stop this expansion & then scroll to view items beyond that size.
But Column lays out its children without any constraints, completely ignoring its own size and screen size. There is infinite space inside of Column.
Viewport was given unbounded height exception occurs because Grid/ListView expands to infinity inside a Column.
Flexible/Expanded exist to give Column children constraints based Column's size. (These two widgets cannot be used anywhere else, except inside Row and Column.)
Inside a Flexible/Expanded widget in Column, Grid/ListView only uses remaining space not taken by other, non-flexible widgets, which get laid out first. (See bottom for layout phases info.)
shrinkWrap is not a good solution
Using shrinkWrap: true on ListView inside a Column isn't really helpful as:
ListView no longer scrolls
ListView can still overflow
A ListView tall enough to show all of its items, will not scroll. Arguably defeats the purpose of using a ScrollView widget (parent class of ListView).
In Column layout phase 1 (see bottom for explanation of layout phases), ListView can be any height it wants (there are no constraints).
A ListView with shrinkWrap:true will grow in height to show all its items.
With enough items, a shrinkWrapped ListView will grow & grow (it never scrolls) to overflow whatever Column is inside, be it screen or other tighter constraint.
Shouldn't shrinkWrap just make ListView only as big as its items OR remaining space (up to screen height), and then scroll?
That would make intuitive sense, but inside a Column in phase 1 layout is done in unbounded space.
So, remaining space is unlimited/infinite. A max height is never reached. shrinkWrap:true just keeps growing Column height as items are added until overflowing the screen (or other smaller constraint).
Example shrinkWrap:true Overflow
Here's an example of adding items to a shrinkwrapped ListView in a Column until its height is taller than the screen, creating an overflow warning area:
(just keep pressing the + sign Floating Action Button)
import 'package:flutter/material.dart';
class ColumnListViewPage extends StatefulWidget {
#override
_ColumnListViewPageState createState() => _ColumnListViewPageState();
}
class _ColumnListViewPageState extends State<ColumnListViewPage> {
int _count = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Column & ListView'),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
setState(() {
_count++;
});
},
),
body: SafeArea(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red)
),
/// // without center, Column will be as narrow as possible
alignment: Alignment.center,
/// Column is forced (constrained) to match screen dimension,
child: Column(
children: [
Text('Inner (Nested) Column'),
ListView.separated( // ← should be wrapped in Expanded
itemCount: _count,
separatorBuilder: (context, index) => const Divider(),
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.symmetric(vertical: 38.0),
child: Text('Row $index'),
),
shrinkWrap: true, // should be removed
)
],
),
),
),
);
}
}
For GridView/ListView in a Column, wrapping within Expanded or Flexible is likely the safest solution.
Expanded / Flexible
Expanded and Flexible can only be used inside Column (and its siblings like Row, etc.).
They must be used as immediate children of Column. We can't "nest" Expanded/Flexible in SizedBox or other widgets. Only directly under Column as immediate children.
Inside Column, placing ListView/GridView in Expanded or Flexible ensures it will use only available space, rather than infinite space.
Column
→ Expanded
→ ListView
ListView/GridView inside Expanded/Flexible in a Column doesn't need shrinkWrap because it is constrained (given a max. height) by the Expanded/Flexible widget. So when that constrained/defined height is used up, ListView/GridView will stop growing & begin scrolling.
Column Layout Phases
Column lays out children in 2 phases:
1st without constraints (unbounded space)
2nd with remaining space based on Column's parent
Phase 1
Any Column child not inside Flexible or Expanded will be laid out in phase 1, in infinite, limitless space completely ignoring screen size.
Widgets that need a vertical boundary/constraint to limit their growth (e.g. ListView) will cause a Vertical viewport was given unbounded height exception in phase 1 as there are no vertical bounds in unbounded space.
Most widgets have intrinsic size. Text for example, is only as high as its content & font size/style. It doesn't try to grow to fill a space. Defined-height widgets play well in Phase 1 of Column layout.
Phase 2
Any widget that grows to fill bounds, should be put in a Flexible or Expanded for Phase 2 layout.
Phase 2 calculates Column's remaining space from its parent constraints minus space used in Phase 1. This remaining space is provided to Phase 2 children as constraints.
ListView for example, will only grow to use remaining space & stop, avoiding viewport exceptions.
More info on Column layout algorithm and how Expanded works within it.
This situation typically happens when a scrollable widget is nested inside another scrollable widget.
In my case i use GridView inside of Column and that error throws.
GridView widget has shrinkWrap property/named parameter, to set it true my problem is fixed.
GridView.count(
shrinkWrap: true,
// rest of code
)
Don't use Column for single child alignment. Use Align instead.
new Align(
alignment: Alignment.topCenter,
child: new GridView(),
)
There are a lot of answer but each one have its own limitation. everyone is saying use shrinkWrap: true. Yes you should use it but the effect of these code is when you will scroll the list, it will automatically move to top item, means scroll back to top.
So perfect solution is use shrinkwrap with physics then only the list will be able to scroll smoothly.
shrinkWrap: true, //
physics: ScrollPhysics(), // both line is mandatory
If you want to see other example of Listview.builder with column as a child and list can be scroll vertically then you can see the example here - arrange multiple text in a vertical list.
Adding this two lines
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
...
I wanted to post another answer because many answers (including the accepted one!) suggest using shrinkWrap: true in ListView.builder. While this removes the error, this solution is NOT ideal for performance reasons for this case. Flutter designed ListView with infinite size specifically to discourage this behaviour only when necessary!
Shrink wrapping the content of the scroll view is significantly more expensive than expanding to the maximum allowed size because the content can expand and contract during scrolling, which means the size of the scroll view needs to be recomputed whenever the scroll position changes.
In some cases/devices, this property caused glitchy scrolls and animation lag in my projects. Even if the lag isn't noticeable, this property does hinder scroll performance.
The OP shows that he is creating a small (finite) number of children for ListView. If we want to resolve the error for these lists, Google recommends using a Flexible widget, i.e. Expanded (a Flexible with flex 1) on the list itself.
Expanded(
child: ListView(
...
),
)
There are other layout issues here, but the vertical viewport unbounded height is because the ListView doesn't have a parent that bounds its size.
Two ways which we can solve the above issues
1. Using Flexible Widget,
The Flexible widget can be used inside Row, Column, and Flex.The flexibility to expand to fill the available space in the main axis (e.g., horizontally for a [Row] or vertically for a [Column])
But, remember, unlike Expanded, The Flexible widget does not require the child to fill available space. So, it's always better to use Flexible.
Column(
children: <Widget>[
Flexible(
child: ListView(...),
),
],
)
2. Using shrinkWrap: true
Using shrinkWrap as true, we tell the Listview to render its List to available space, not take below remaining screen.
Column(
children: <Widget>[
ListView(
shrinkWrap: true,
),
],
)
Also, physics attribute can be used to allow/disallow scrolling of Listview
Stop Scrolling
ListView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
),
Allow Scrolling
ListView(
shrinkWrap: true,
physics: ScrollPhysics(),
),
I faced this problem because I wanted to display a GridView inside a ListView, and the solution was by adding the following not only in the ListView but also in the GridView as well:
shrinkwrap: true
scrollDirection: Axis.vertical
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 100,
width: double.infinity,
color: Colors.pinkAccent,
),
);
},
),
If you still face this problem in 2021 ..here is the best and easy solution
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
// itemExtent: 400, use this if you give hight of your items
physics: ScrollPhysics(),)
their are many solution .. but they have problem.. like when we use ListView and use its property shrinkWrap .then one thing you many notice the scroll does not work on listView so you must use physics: ScrollPhysics()
Container(
height: 100,
child: ListView.builder(
scrollDirection: Axis.vertical,
physics: BouncingScrollPhysics(),
shrinkWrap: true,
itemCount: 5,
itemBuilder: (context, index) {
return Text("Hello World $index");
},
),
);
test this one for second listview
ListView.builder(
physics: NeverScrollableScrollPhysics(),
Just want to add my solution that worked in my case where I have vertical list view together with other buttons and texts on the same screen:
Flexible(
child:
ListView.builder(
// make sure to add the following lines:
shrinkWrap: true,
physics: ScrollPhysics(),
// the rest of your list view code
) // ListView
) // Flexible
This solution works well particularly when you have other widgets on the SAME SCREEN. In this particular case, "Expand" or "Flexible" alone will not work.
There is a widget called Center. It should help you achive what you want to do (center it vertically). Official Documentation for Center widget
Also meet this error, because the code like this
return Container(
child: Column(
children: [
ListView.separated(
itemBuilder: (context, index) {
return CircleManagerSettingItem(widget.membersInfo[index]);
},
separatorBuilder: (_, index) => DD(
height: 1,
paddingLeft: 10,
paddingRight: 10,
),
itemCount: (widget.membersInfo.length)),
],
),
);
remove the Column inside the Container then Ok.
in my case, I had to wrap the ListView inside a Container and give this container a specific Height
I have made a custom function to solve your problem.
You can directly use this function in your code base:
Widget horizontalListView(height, width, color, child, margin) {
return Column(
children: [
SizedBox(
height: 200,
child: ListView.builder(
itemCount: 10,
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemBuilder: (context,index) {
return Container(
height: height,
width: width,
margin: EdgeInsets.all(margin),
color: color,
child: child
);
}
),
),
],
);
})
P.S. Sorry for bad alignment of code.
You can put Expanded() as a parent of that widget too.
if you are using CustomScrollView
you can try SliverFillRemaining