Open fjelljager opened 1 year ago
Hi!
Hey! In this case, it is a misunderstanding of the layout in Flutter. If you notice, the same problem remains when replacing the TabbedView
with a pure Container
.
Scaffold(
body: Container(
child: Column(
children: [
Container(
color: Colors.blue,
height: 400,
),
Expanded(
child: Container(color: Colors.green),
)
],
crossAxisAlignment: CrossAxisAlignment.stretch,
),
padding: EdgeInsets.all(32)));
I don't know what other components would be on the screen or where this one would fit, but it might not be appropriate to use Column
in this case.
If the height is dynamic, you might have to use a LayoutBuilder
as well.
It is also possible to create your own layout.
Thank you for getting back to me!
I've read your answer and looked into it some more, I think you may have understood slightly what I'm trying to do.
It's not necessary only with a column, I'll show an example even closer to your original:
In your original example, I added a container behind the text in the first tab just to help illustrate so it looks like this:
tabs.add(TabData(
text: 'Tab 1',
leading: (context, status) => Icon(Icons.star, size: 16),
content: Padding(child: Container(
color: Colors.red,
child: Text('Hello'),
), padding: EdgeInsets.all(8))));
Note how the red container and whole TabbedView
expand to fill the screen.
Now I've simply replaced TabbedView
with a container and it behaves as I would like it to (i.e. the red container wraps tightly around the Text
instead of expanding to the edge of the screen):
@override
Widget build(BuildContext context) {
TabbedView tabbedView = TabbedView(controller: _controller);
Widget w =
TabbedViewTheme(child: tabbedView, data: TabbedViewThemeData.mobile());
return Scaffold(body: Container(
// child: w,
child: Container(
child: Container(
color: Colors.red,
child: Text('Hello'),
),
),
padding: EdgeInsets.all(32)));
}
Anyway, I forked the repository and found out how to fix it. I made the following changes to the code for TabbedView
:
// Layout delegate for [TabbedView]
class _TabbedViewLayout extends MultiChildLayoutDelegate {
@override
void performLayout(Size size) {
Size childSize = Size.zero;
if (hasChild(1)) {
childSize = layoutChild(
1,
BoxConstraints(
minWidth: size.width,
maxWidth: size.width,
minHeight: 0,
maxHeight: size.height));
positionChild(1, Offset.zero);
}
double height = math.max(0, size.height - childSize.height);
// ORIGINAL CODE
// layoutChild(2, BoxConstraints.tightFor(width: size.width, height: height));
// NEW CODE
final padding = 8.0;
layoutChild(2, BoxConstraints.tightFor(width: size.width, height: childSize.height + padding));
positionChild(2, Offset(0, childSize.height));
}
@override
bool shouldRelayout(covariant MultiChildLayoutDelegate oldDelegate) {
return false;
}
}
I only changed two lines, however it now behaves as I wanted it to (at least in the vertical direction).
I was thinking that maybe another parameter called Bool tightLayout
for TabbedView
could allow you to use TabbedView
in either way depending on your needs. The only thing to solve is getting the correct padding.
Please could I either a) ask for this as a feature request or b) try to implement it and then submit a pull request?
Thank you
Hi @fjelljager!
Are you wanting TabbedView to have the height according to your content right? If so, then you’re looking for something like:
IntrinsicHeight(child: tabbedView)
But that won’t work because CustomMultiChildLayout
will always take up the entire space of the parent container. That’s why in the example, it seems to work but in fact, continues to fill all the available space. If you create a wrapper container of another color you will see that it continues to fill everything. This would still be a problem if you were to put it inside a layout because the height will remain "infinite".
I believe it would be necessary to swap the use of CustomMultiChildLayout
for a layout made from scratch by extending MultiChildRenderObjectWidget
(as is done in TabsAreaLayout
). Then you could implement the computeMaxIntrinsicHeight
and computeMinIntrinsicHeight
methods.
The TabbedView
could take a boolean parameter to indicate whether or not the layout should have the "Intrinsic height" behavior.
Hi @caduandrade
Thank you for getting back to me!
Yes exactly, that's what I'm looking for. Thanks for pointing out that what I was saying didn't work, I went and tried it with a container behind and saw what you mean.
I've been doing some research into MultiChildRenderObjectWidget
like you suggested and tried to make a basic implementation. I had some limited success, i.e. it seemed like it would work (because it now does not render the coloured container behind). However I eventually go lost with the computeMaxIntrinsicHeight
method for TabsAreaLayout
and ContentArea
since it's quite complicated to understand what's going on. Please bear in mind I am quite new to Flutter so I I have not tried to go this deep into widgets before.
Here is the code I wrote so far:
Layout for TabbedView
class TabbedViewMultiChildRenderObjectWidget extends MultiChildRenderObjectWidget {
TabsArea? tabsArea;
TabbedViewMultiChildRenderObjectWidget({
Key? key,
required this.tabsArea,
required ContentArea contentArea,
}) : super(
key: key,
children:
(tabsArea == null) ? [contentArea] : [tabsArea!, contentArea],
);
@override
RenderObject createRenderObject(BuildContext context) {
return TabbedViewRenderObject(tabsAreaVisible: (tabsArea != null));
}
}
class TabbedViewParentData extends ContainerBoxParentData<RenderBox>
with ContainerParentDataMixin<RenderBox> {}
class TabbedViewRenderObject extends RenderBox
with
ContainerRenderObjectMixin<RenderBox, TabbedViewParentData>,
RenderBoxContainerDefaultsMixin<RenderBox, TabbedViewParentData> {
late bool tabsAreaVisible;
TabbedViewRenderObject({required bool tabsAreaVisible}) {
this.tabsAreaVisible = tabsAreaVisible;
}
@override
void setupParentData(covariant RenderObject child) {
child.parentData = TabbedViewParentData();
}
@override
void paint(PaintingContext context, Offset offset) {
defaultPaint(context, offset);
}
@override
void performLayout() {
RenderBox? contentArea;
if (this.tabsAreaVisible) {
// Case where tabs are visible
var tabsArea = firstChild;
if (tabsArea == null) {
size = constraints.smallest;
return;
}
tabsArea.layout(
BoxConstraints.tight(
Size(constraints.maxWidth,
tabsArea.getMaxIntrinsicHeight(constraints.maxWidth)
)),
parentUsesSize: true
);
contentArea = childAfter(tabsArea);
if (contentArea == null) {
size = constraints.smallest;
return;
}
// print(contentArea.getMaxIntrinsicHeight(constraints.maxWidth));
// ^Throws an error
// TODO: Find intrinsic max height of contentArea
contentArea.layout(BoxConstraints.tight(
Size(constraints.maxWidth, constraints.maxHeight - 300)),
parentUsesSize: true);
var contentAreaParentData =
contentArea.parentData as TabbedViewParentData;
contentAreaParentData.offset = Offset(
0,
tabsArea.size.height,
);
size = Size(
constraints.maxWidth, tabsArea.size.height + contentArea.size.height);
} else {
// Case where tabs aren't visible
contentArea = firstChild;
if (contentArea == null) {
size = constraints.smallest;
return;
}
contentArea.layout(constraints, parentUsesSize: true);
size = Size(constraints.maxWidth, contentArea.size.height);
}
}
}
Method for _TabsAreaLayoutRenderBox
(I couldn't work out how to do the calculation but I found that 28 works... I know this wouldn't be a correct for a production version but it's just to illustrate).
@override double computeMaxIntrinsicHeight(double width) {
// TODO: implement computeMaxIntrinsicHeight
// return super.computeMaxIntrinsicHeight(width);
return 28;
}
The content area also needs an implementation, but again I'm not too sure how to do it. Currently calling print(contentArea.getMaxIntrinsicHeight(constraints.maxWidth));
throws an error so it seems like it doesn't currently have an intrinsic height.
If you could provide any pointers that would be much appreciated :).
Hi, I have another question about
TabbedView
if you would be willing to help me out,I've been trying to implement
TabbedView
inside a column and would like it to resize so that the height equals the height of the selected tab content (instead ofdouble.infinity
).To illustrate: I extended the example code so that
TabbedView
is in a column with another Container. TheTabbedView
is constrained to a fixed height and everything works as expected:The problem occurs when I comment out
height: 400
for theSizedBox
around theTabbedView
. I was expecting theTabbedView
to shrink to the size of the content, but instead I get the following error:Please could you let me know if this is a bug in TabbedView or if I just have a bad understanding of the layout system in Flutter and need to revise that? If the latter then and suggestions would be appreciated. Thank you for your time in advance!