Flutter SliverToBoxAdapter which fits child size - flutter

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.

Related

How to ignore bottomNavigationBar with the Align widget?

Say there is a Scaffold with a bottomNavigationBar property that is populated. If you use a Stack with a Align widget inside its body set to Alignment.center, the Align widget gets centered accounting to the bottomNavigationBar and centers in the remaining space minus that.
How do I get it so that it centers according to the total screen height, using all these widgets?
Stack(
body: Align widget with center alignment and some child,
bottomNavigationBar: Bottom app bar
)
I'm not entirely sure what you mean in your question - but I think you want the body of a Scaffold (not Stack) to center a widget for the whole height available on the device.
Here's how you could do that using a Scaffold, SizedBox and Center widgets along with a MediaQuery
Scaffold(
body: SizedBox(
height: MediaQuery.of(context).size.height,
child: Center(
child: Text("CENTERED")),
),
bottomNavigationBar: BottomNavigationBar(items: []),
)
Scaffold
Sized Box
Center
Media Query
Hopefully that puts you on the right path.

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 - How does Column constrain its height inside a ListView when its default mainAxisSize = max?

What magic allows ListView to constrain its children to their minimal intrinsic size during layout?
According to Column layout algorithm, it tries to be as tall as possible (by default).
Inside a ListView, during performLayout the height constraint for a Sliver child is double.INFINITY (if I'm not mistaken).
How does Column constrain its height to its intrinsic height (i.e. the minimum required to contain its children) when its incoming height constraint inside a ListView is infinity?
Here's sample code I played with trying to understand this.
Uncommenting child: MyColumn() while commenting out child:ListView() shows Column expanding to its usual max height. But inside a ListView it's magically constrained to its minimal height. I'm wondering how this is done.
import 'package:flutter/material.dart';
class FlexInListViewPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Column in ListView'),
),
body: Container(
decoration: BoxDecoration(
border: Border.all(width: 3, color: Colors.lightGreenAccent)
),
//child: MyColumn() // ← uncomment while commenting out ↓ to see Column & its default height
child: ListView(// imposes tight constraint of screen width to children
children: [ /// RenderSliverList performLayout doesn't impose a constraint height during layout
/// but asks children to report back size after layout complete
/// this allows ListView to pass a height constraint to Column
Container(child: Text('child'), color: Colors.yellowAccent,),
Container(child: MyColumn(), color: Colors.orangeAccent.withOpacity(.5),)
],
),
),
);
}
}
class MyColumn extends StatelessWidget {
#override
Widget build(BuildContext context) {
/// Column
/// 1: layout fixed-height (zero flex factor) children in unbounded vertical space
/// 2: Calculate/reserve remaining space for flex-height children
/// 3: No flex children, skip flex-kids layout
/// 4: Width is either widest child, or width imposed by parent (ListView: screen width)
/// 5: Height of Column is by default mainAxisSize: max, try to fill all vert. space
/// 5: If mainAxisSize: min & incoming vert. constraints allow,
/// height of Column is sum of children heights
return Column(
children: [
Container(child: Text('inside Column'), color: Colors.lightBlueAccent.withOpacity(.3),)
],
);
}
}
What the above code renders:
Notes
I've seen the code for parentUsesSize where layout size is reported back up from children to parent, but I don't understand how Column is sizing itself to minimal height when its constraint is unbounded. Is there some non-obvious information being passed around, something like "minimal needed layout size" vs. "desired layout size"?
im assuming this is what you want to achieve,
Inside your ListView add shrinkWrap: true and primary: false
ListView(// imposes tight constraint of screen width to children
primary: false,
shrinkWrap: true,
children: [ /// RenderSliverList performLayout doesn't impose a constraint height
during layout
/// but asks children to report back size after layout complete
/// this allows ListView to pass a height constraint to Column
Container(child: Text('child'), color: Colors.yellowAccent,),
Container(child: MyColumn(), color: Colors.orangeAccent.withOpacity(.5),)
],

Flutter, Access the padding value of parent container widget

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.

Flutter: Expanded vs Flexible

I've used both Expanded and Flexible widgets and they both seem to work the same.
What is the difference between Expanded and Flexible?
Scaffold(
appBar: AppBar(),
body: Column(
children: <Widget>[
Row(
children: <Widget>[
buildExpanded(),
buildFlexible(),
],
),
Row(
children: <Widget>[
buildExpanded(),
buildExpanded(),
],
),
Row(
children: <Widget>[
buildFlexible(),
buildFlexible(),
],
),
],
),
);
Expanded is just a shorthand for Flexible
Using Expanded this way:
Expanded(
child: Foo(),
);
is strictly equivalent to:
Flexible(
fit: FlexFit.tight,
child: Foo(),
);
You may want to use Flexible over Expanded when you want a different fit, useful in some responsive layouts.
The difference between FlexFit.tight and FlexFit.loose is that loose will allow its child to have a maximum size while tight forces that child to fill all the available space.
Widget under Flexible are by default WRAP_CONTENT although you can change it using parameter fit.
Widget under Expanded is MATCH_PARENT you can change it using flex.
Expanded - it is Flexible with set fit
class Expanded extends Flexible {
const Expanded({
Key key,
int flex = 1,
#required Widget child,
}) : super(
key: key,
flex: flex,
fit: FlexFit.tight,
child: child
);
}
You may use Flexible to resize the widgets in rows and columns. It's mainly used to adjust the space of the different child widgets while keeping the relation with their parent widgets.
Meanwhile, Expanded changes the constraints sent to the children of rows and columns; it helps to fill the available spaces there. Therefore, when you wrap your child in an Expanded widget it fills up the empty spaces.
Providing these videos from the Flutter's Official YouTube channel just to help out people, who might look for this in the upcoming future...
Expanded:
Flexible:
Expanded() is nothing more than Flexible() with
Flexible (fit: FlexFit.tight) = Expanded()
but, Flexible uses fit :FlexFit.loose by default.
FlexFit.tight = Wants to fit tight into parent taking as much space as possible.
FlexFit.loose = Wants to fit loose into parent taking as little space as possible for itself.
Expanded changes the constraints of a child widget so it fills any empty space. Expanded widget is a specialised Flexible widget with a set fit - Flexible(fit: FlexFit.tight. Expanded widgets also have a flex property.
Flexible makes the child widget flexible and resizable. You can add the flex or fit property to adjust the size and spacing.
Flexible fit properties include:
FlexFit.loose - The widget’s preferred size is used. (Default)
FlexFit.tight - Forces the widget to fill all of its extra space.
Flexible default will share the available space of the parent widget, but will NOT force the child to fit the space.
Expanded will share the available space of the parent widget, and force the child widget to change its width/height to fill the available space.
In fact, Expanded extends Flexible, which is a Flexible with FlexFit.tight. See the official document.
Here is a Container widget and three Flexible Widgets(flex = 1, fit = FlexFit.loose) in a row. We can see that the three flexible widgets share the same maxWidth (1/3 of the available screen width), and the blue one wants bigger than it, and the others want smaller. But as we can see, the blue guy has maxWidth as its width and the other widgets' width just fit their content.
Here is the code of the image above up:
Row(
mainAxisSize: MainAxisSize.max,
children: [
Container(
color: Colors.teal,
child: Text(
'Container Text ',
)),
Flexible(
child: Container(
color: Colors.blue,
child: Text(' Text.Flexible Text.Flexible Text.Flexible.')),
),
Flexible(
child: Container(
color: Colors.yellow, child: Text('Flexible Text.')),
),
Flexible(
child: Container(
color: Colors.lightGreen, child: Text('Flexible.')),
),
],
)