How to use flex parameter in Flexible widget in Flutter? - flutter

When there is excess space between widgets, the flex property works as expected.
As mentioned in the documentation, the formula to use is as follows
remainingSpace * (flex / totalOfAllFlexValues)
But when you have to shrink the widgets, the flex property stops making sense.
Giving results like this.
Building on this, here the widgets overflow, so I use flex to shrink box 1 and 2
and while playing with the flex values, the following happens
class Caja extends StatelessWidget {
Color color;
int numero;
double ancho;
Caja(this.color, this.numero, this.ancho);
#override
Widget build(BuildContext context) {
return Container(
width: this.ancho,
height: this.ancho,
color: this.color,
child: Center(
child: Text(
this.numero.toString(),
style: TextStyle(
fontSize: 30.0
),
),
),
);
}
}
I user Flexible widget below
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Flexible(flex: 1, child: Caja(Colors.blue, 1, 200.0)),
Flexible(flex: 6, child: Caja(Colors.red, 2, 200.0)),
Caja(Colors.green, 4, 400.0),
],
)
Where does this new leftover space come from?
Can someone explain to me?

I changed the flex value of the second box to 1 - there was no white space. Then to 2 - there was no white space either. From a flex value of 3 upwards, there appears a white space and it gets bigger and bigger.
The documentation states, that a
"..., Flexible does not require the child to fill the available space."
If you examine the source code of the Flexible, the comments say:
"If non-zero, the amount of space the child's can occupy in the main
axis is determined by dividing the free space (after placing the
inflexible children) according to the flex factors of the flexible
children."
I think there might be a problem with the inflexible child - your green box. If you set the flex of your second box so high, you want your first box to be really small. But the flexible children just take the free space up to the inflexible child into consideration and not the free space after it.

Related

Flutter - Partition column into a variable size limited to half, and fill what's left with expanded

What I want to do:
I am trying to partition a column into two parts: upper (for settings) and lower (for data).
The settings part is relatively small, but might grow bigger depending on what the user adds (filters, etc...). As such, its size is unknown when building.
When the settings part grow bigger than half the column, they should be limited to half the column. The user should be able to scroll through the rest.
If it is smaller than half the column, the settings should only take the necessary space.
The data part should always fill the rest of the column: meaning, at least half of it, and if the settings container is smaller than half, then the data takes up this leftover space too.
My current "solution" is as follows:
import 'dart:io';
import 'package:flutter/material.dart';
import 'dart:math';
class TestWidget extends StatefulWidget {
final File file;
final FileInterpreter currentInterpreter;
final void Function({File? newFile, FileInterpreter<dynamic>? newInterpreter}) changeViewCallback;
const TestWidget({super.key, required this.file, required this.currentInterpreter, required this.changeViewCallback});
#override
State<TestWidget> createState() => TestWidgetState();
}
class TestWidgetState extends State<TestWidget> {
var rng = Random();
Widget? extensionPanel;
#override
Widget build(BuildContext context) => Container(
color: Colors.amber,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// just a button to test different settings sizes
Container(
color: Colors.lightBlue.shade100,
child: SizedBox(height: 25, child: Row(children: [const Text("quick settings go here"), IconButton(onPressed: () => setState(() {extensionPanel = Text("SETTINGS START${"\n - :D"*rng.nextInt(40)}\nSETTINGS END");}), icon: const Icon(Icons.keyboard_arrow_down_sharp))],)),
),
// extension of the quick settings
if (extensionPanel!=null) Flexible(
fit: FlexFit.loose,
child: Container(
color: Colors.blue,
child: SingleChildScrollView(controller: ScrollController(), child: extensionPanel,)
)
),
if (extensionPanel!=null) Container(
color: Colors.red,
child: Row(children: [const Expanded(child: Divider()), IconButton(onPressed: (){setState(() {extensionPanel=null;});}, icon: const Icon(Icons.keyboard_arrow_up)), const Expanded(child: Divider())],)
),
// Important info
Expanded(
child: Container(
color: Colors.green,
child: const Center(child: Text("I am centered, very big, and fill ALL the empty vertical space\nEven the space left behind by the settings container\n:D"),)
),
)
],
),
);
}
The widget contains sugar to collapse the settings panel and change the settings size randomly.
It gives me the following result:
When settings are big enough
When settings are not big enough
This solution contains multiple containers: the full column is orange, and contains:
Light blue: fixed size, allows to refresh the settings size.
Blue: the flexible, scroll-able settings. Should only take the necessary space, and AT MOST half the column.
Red: fixed size, to collapse the settings panel.
Green: The expanded data. Should fill ALL the remaining vertical orange space.
Analysis of the result, and issues
It is almost perfect, except:
✅ The good:
when there are a lot of settings:
-> they are limited to half the screen (blue container).
-> the green container uses the rest of the space (half the column).
-> I can scroll through the settings with no issues.
when there's not many settings:
-> the settings use up only what they need.
when I collapse the settings:
-> the green container takes all the space.
⛔ The bad:
when there's not many settings:
-> the green section uses only half the column (there's leftover orange space)
💡 What should happen:
any leftover orange space, should be given to the green container.
Other attempts
I tested multiple variations:
giving the green container more flex: ⛔ fills more space, but not all. Also, makes the settings section too small when there are a lot of settings.
giving the settings flexible container flex: 0: ⛔ if there are many settings, takes up more than half the space. Worst case, causes overflow. Also removes the scrollable property of the settings.
Using a SizedBox instead of Flexible: ⛔ I don't know how much space the settings bit will take... Different settings might need different sizes, that's why I want it flexible. Surely it cannot be this difficult?
Using two Expanded and forgetting about it: ⛔ Sure, but the green container displays a ton of very important information. Wasting almost half the column in empty space is very sad...
I also had a look at the solutions given in :
this stackoverflow forum
this github thread
Sadly, to no avail.
If you are using Flexible and Expanded in single Column or Row, then space will be allocated equally with flex value defaulted as 1.
You may have to remove Flexible widget for blue area, but surely you will face the other issue if there are many items.

Wrap Scaffold in Row

I want to built a split screen for larger devices. One half should be as small as possible but as large as needed and the other half should occupy the rest of the screen.
I figured I'd use a Row containing two Scaffolds (one wrapped in a Expanded widget) like so:
Row(children: [
Scaffold(appBar: MyAppBar1(), body: Container()),
Expanded(child: Scaffold(appBar: MyAppBar2(), body: Container())),
])
However, I get the following error message when wrapping a Scaffold inside a Row:
RenderCustomMultiChildLayoutBox object was given an infinite size during layout.
I know that I could just wrap the Scaffold inside a SizedBox with a fixed width, but I want the Scaffold to automatically take the right size.
Scaffold itself doesn't have a width constraint, so it will try to occupy as much space as possible (if I'm remembering correctly), and Row on its part won't try to constrain it, hence the error.
You have two options:
wrap the Scaffold in another Expanded, then use the flex argument on both the Expanded to give them size relative to each other (i.e., if one has flex: 1 and the other flex: 2, the second one will have twice the size of the first)
wrap the Scaffold in a ConstrainedBox and set the constraints yourself, which I guess in this case would be the maxWidth set on the smaller side
As others pointed out, anyway, if you don't really need another Scaffold, you can simply use two Column and get a similar result.
You do not need to use two Scaffolds. Use two columns inside one Column in Scaffold. Wrap the second Column with Expanded. This way your first part will take as much space as needed and the second part will take the remaining space.
home: Scaffold(
body: Column(
children: [
Column(
children: [],
),
Expanded(
child: Column(
children: [],
),
),
],
),
)

Why does ListView ignore constraints

Why does a ListView completely ignore imposed constraints?
#override
Widget build(BuildContext context) {
return Padding(
child: Container(
constraints: BoxConstraints(maxWidth: 500),
child: ListView(
children: buildList(data),
),
),
);
}
The displayed ListView does not seem to be bothered at all with the maxWidth:500 of the parent container (same if replaced with a ConstrainedBox) and all elements (which are Rows) go full screen width.
Why?
The solution is to work with width of children instead, but still I'd much rather constrain the entire listView, and also if there's a constraint, shouldn't everything inside be forced not to exceed it?
EDIT: After hours of trying various things it finally works. Turns out the list view needed to a child of a Center, otherwise it would not pay any attention to SizedBoxes, ConstrainedBoxes, Containers etc. you'd put on it or its children and stretch full screen no matter what.
What are the parent widgets of the Padding(child: Container(... list view ))? I mean are you sure that a parent is not imposing its constraints on its children? Try do replace your ListView with Container(color: Colors.red, height: 100) and see if it respects the constraints. If it does not, there probably is a parent widget with infinite width constraint or something like that!
A solution can be wrap your Container with an UnconstrainedBox but I would recommend first checking my first point :D

How do I make a child widget expand to fill a parent container inside of a stack when the child has no parameters to alter its layout?

I'm building a card game and using flame to pull the cards from a sprite sheet. The problem is that I set the width and height of the Container that holds the SpriteWidget, but the SpriteWidget expands to either the width or the height of the container, but not both. I want it to expand/stretch to be the same size as the parent container. Unfortunately, the SpriteWidget really has no parameters that could be used to change its size.
I've spent several hours scouring the internet for a solution and tried a number of widgets including FittedBox, Flex, Positioned.fill, etc., but I'm unable to achieve the desired effect. How can I make the SpriteWidget stretch to fill its parent when it has no parameters to do so?
class _PlayerHandPortraitLayout
extends WidgetView<PlayerHand, _PlayerHandController> {
#override
final state;
const _PlayerHandPortraitLayout(this.state) : super(state);
#override
Widget build(BuildContext build) {
return Stack(
children: state.displayHand().asMap().entries.map((cardItem) {
var index = cardItem.key;
var card = cardItem.value;
return Positioned(
left: index * CARD_OVERLAP_OFFSET,
child: Draggable<Container>(
childWhenDragging: Container(),
child: Container(
color: Colors.purple,
width: state.cardWidth,
height: state.cardHeight,
child: SpriteWidget(
sprite: state.spriteImages[card.suite.index][card.value.index],
),
),
feedback: Container(
color: Colors.yellow,
width: state.cardWidth,
height: state.cardHeight,
child: SpriteWidget(
sprite: state.spriteImages[card.suite.index][card.value.index],
),
),
),
);
}).toList(),
);
}
}
actually this will be not possible, SpriteWidget is designed to expand as long as it fits on the smallest dimension available on its parent, you can check on it source code here.
This is done so the Sprite will not get distorted when its parent has a different aspect ratio than the ratio of the Sprite.
If you have an use case where you would want the Sprite to get intentionally distorted, please open an issue on the Flame repository explaining the case, and we can try to take a look on it.

Scale down all children in widget by a factor based on the parent size

In Flutter I am implementing "what's new" screens which will show a preview of the real screens in the app.
For that I reuse the screen Widgets in the "what's new" flow.
The preview will be shown in a smaller Container and not cover the whole screen.
I want to scale down the text sizes, line heights, etc. for all widgets within this Container to match the smaller bounds.
Is there a way to do this without individually adding a smaller style for every Widget separately?
e.g. scale down all fontSizes by 20% for a Widget child regardless of the size set in the theme
I found the solution. Wrapping the child widget tree with Transform.scale(...) will scale all the Widgets down the tree according to the supplied scale factor.
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Transform.scale(
scale: 0.9,
child: MyScaledWidgetTree(),
),
Container(
child: ...,
),
],
)