Flutter, Access the padding value of parent container widget - flutter

I have a GridView widget nested inside of a SizedBox widget nested inside of a Container widget
Container(
padding: EdgeInsets.all(10),
alignment: Alignment(0, 0),
child: SizedBox(
width: min(MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height) -
20,
height: min(MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height) -
20,
child: GridView.count(
padding: EdgeInsets.all(0),
crossAxisCount: sqrt(tiles.length).round(),
mainAxisSpacing: 5,
crossAxisSpacing: 5,
children: tiles,
shrinkWrap: true,
),
),
),
The Container Widget has an edge inset of 10, so in the SizedBox I want the width and height to be the min screen width/height minus this edge inset (so minus 20 since it's on each side).
How can I read values of a parent widget so that I can know it's edgeInset? Can a MediaQuery accomplish this through the context?
I know that I can just store the edge inset as a variable earlier on and use that in both places, but as I am trying to learn Flutter I am more curious if there is a way to read the values/properties of parent widgets so that their children can reference them on the fly.

If you provide some Widget a global key
final _key = GlobalKey();
Table(
key: _key,
children: _getTableRows(),
);
then you can calculate the size of the widget like this:
void _getSize(_) {
final RenderBox _boardRender = _key.currentContext.findRenderObject();
final boardSize = MediaQuery.of(_key.currentContext).size;
final boardOffset = _boardRender.localToGlobal(Offset.zero);
//print('$boardSize:$boardOffset:$cellSize');
}
You may look at https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html

Container has a somewhat complicated behaviour so the answer to your question would depend on who the parent of the container is.
From the documentation:
Container tries, in order: to honor alignment, to size itself to the child, to honor the width, height, and constraints, to expand to fit the parent, to be as small as possible.
For example: If you place your container in the body paramenter of a Scaffold and remove the SizedBox then GridView will fill the screen and will have the padding you specified. (In which case you don't even need Container and you can just use Padding widget instead.)
More broadly speaking Flutter works in a different paradigm where you don't need to "know" specifics of your parent as you potentialy can place any widget in any other widget.

Related

IntrinsicHeight inside a Stack

I have a widget tree that has a variable height.
I need to create another widget that will be on top of it under certain circumstances.
I need this other widget to take up the full height of the stack, depending on the first widget's height.
So it goes like this:
Stack(
children[
Widget2(),
SomeVariableHeighWidget1()
],
)
I've tried wrapping Widget2 in IntrinsicHeight, for example
Widget2 = IntrinsicHeight(
child: Container(
color: Colors.green,
),
),
but I see nothing. If I set a fixed height to the Container I can see it, and it does take up the full width, but I can't get it to take up the full height. I also tried Expanded but it is incorrect use of parent data widget.
Thank you

Flutter SliverToBoxAdapter which fits child size

I want to create a scrollable content which include:
A header widget that includes several child widgets - which it's height is unknown
A list of rows widgets
The correct way to do that is using a CustomScrollView, like this:
Widget _buildView(BuildContext context) {
return (
slivers: <Widget>[
SliverToBoxAdapter(
child: Container(
height: 128.0,
child: _buildHeader(context),
),
),
_buildList(context),
],
);
}
My problem is that my unlike this code sample, my header's height is unknown, and should fit its children's height (which can change).
How do I achieve that?
Since the Sliver needs to adapt on the header's height, you can consider using ConstrainedBox on your header. Depending on your use case, you can set a minHeight and maxHeight to let the viewport know the widget's size to be rendered.

Two ListViews in the same column - Vertical viewport was given unbounded height [duplicate]

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

Flutter SizedBox vs LimitedBox

I have read docs for both SizedBox and LimitedBox, and didn't find any practical difference between the two.
Can anyone give an example when one may fail and other work?
So what I have found is, LimitedBox is only usable when the child is given unconstrained width/height by its parent.
And SizedBox simply creates a box with given width/height and doesn't allow child to go beyond given dimensions.
Example: (LimitedBox)
#override
Widget build(BuildContext context) {
return Scaffold(
body: LimitedBox(
maxHeight: 50, // no impact because it's child `Text` width/height isn't unconstrained
child: Text(
"A",
style: TextStyle(fontSize: 200),
),
),
);
}
Example (SizedBox)
#override
Widget build(BuildContext context) {
return Scaffold(
body: SizedBox(
height: 50, // has impact, it won't let Text to be of more than 50 logical pixels high
child: Text(
"A",
style: TextStyle(fontSize: 200),
),
),
);
}
Note: If anyone has better answer, I am happy to accept theirs.
LimitedBox is a SizedBox unless the parent widget imposes a constraint.
From Doc
A box that limits its size only when it's unconstrained.
Reference
Flutter doc
LimitBox:
LimitBox works only when it's size is unconstrained If this widget's
maximum width is unconstrained then its child's width is limited to
[maxWidth]. Similarly, if this widget's maximum height is
unconstrained then its child's height is limited to [maxHeight].
Example:
Center(
child: LimitedBox(
maxWidth: 50,
child: Container(
color: Colors.red,
width: 20,
height: 100,
)
),
),
Output:
This restricts Container Widget with a max-width as 50 but it's going to show widget till container width which is 20.
SizeBox:
It comes with fixed sizes that restrict its child to render on the
limited area. If either the width or height is null, this widget will
try to size itself to match the child's size in that dimension. If the
child's size depends on the size of its parent, the height and width
must be provided.
Example:
Center(
child: SizedBox(
width: 50,
child: Container(
color: Colors.red,
width: 20,
height: 100,
)
),
),
Output:
This will also behave like LimitedBox but the only difference here Container will be rendered till width 50 of SizeBox, it will not consider it's own width which is 20 and drawn till the parent widget.
Can anyone give an example when one may fail and other work?
They'll both work, but they'll do different things. The example from the LimitedBox documentation says:
This is useful when composing widgets that normally try to match their parents' size, so that they behave reasonably in lists.
Imagine that you are creating a reusable widget and don't have control over where it's used. You could use a SizedBox, but then your widget will always have the specified size, even if its size is already constrained by a parent widget.

How can I layout a widget based on the size of its child?

I'm trying to build a container with width = (its child width + 10). the child is also a container with Text widget that can have variant text content.
I tried to extract child to a Statefull widget but I failed to expose its size.
Here is my minimal code:
final GlobalKey _childKey = GlobalKey();
_getChildWidth() {
final Size size = _childKey.currentContext.size;
final width = size.width;
return width;
}
Widget child = Container(
key: _keyChild,
child: Text("text here"),
);
Widget parent = Container(
width: _getChildWidth + 10,
child: child,
);
It sounds like you are looking for the Padding widget, although Container also has a padding property: https://api.flutter.dev/flutter/widgets/Container/padding.html
One option would be to create a container, place a horizontal padding property of 5 (5 left + 5 right) and a Text child, like this:
Container(
padding: EdgeInsets.symmetric(horizontal: 5.0),
child: Text('text here'),
);