I have a text that can vary in height, followed by a PopupMenuButton. I want the text to be center-aligned on the vertical axis (so that there isn't a weird white space when there's only 1 ligne of text) but the PopupMenuButton to stay in the top right corner, no matter the height of the text. Thus using crossAxisAlignement doesn't work here since all widgets are affected. Using an Align widget on the text or PopupMenuButton doesn't work and textAlign doesn't either It seems any solution must be implemented on the Row level and not in its children, which makes sense.
For now I'm using a Row to contain these 2 widgets, but I'm not so sure it is what I need if I want the behaviour mentionned before. Is there a solution for the Row, or another widget I can use for this ?
Here is the code as it stands. Thanks.
Row(
children: [
Expanded(
child: RichText(
text: TextSpan(), // bunch of text in there
)
)
),
SizedBox( // To box in the 3 dots icon (material design forces 48px otherwise)
height: 24,
width: 24,
child: PopupMenuButton(
padding: EdgeInsets.all(0),
onSelected: menuSelect,
itemBuilder: (BuildContext context) {
return [
PopupMenuItem(value: 0, child: Text('Read', textAlign: TextAlign.center,),),
PopupMenuItem(value: 1, child: Text('Delete', textAlign: TextAlign.center,),)
];
},
),
)
],
)
Here's a way to do it :
Wrap your SizedBox with an Align(alignment: Alignment.topRight)
Wrap your Row with an IntrinsicHeight so that the cross-axis
doesn't stretch
Alternatively, you might want to look into the Stack widget if you're not familiar with it.
Related
I want to show a progress bar with a button on the right side as in the image below. The margin on the left and right should be visually equal. Instead, the spacing on the right is larger than on the left. This is because the IconButton I'm using adheres to material design and has a bunch of extra space around it.
My code places the progress bar in a Row. Above it I also have a label in a Row. I want the right-aligned label to be aligned to the button. What's the correct way to align taking into consideration any padding that material design might have added?
Here's what my code looks like:
return Container(
padding: 10,
child: Column(
children: [
Row(children: [Text("Left aligned text"), const Spacer(), Text("Right aligned text")]),
const SizedBox(height: 10),
Row(children: [
Expanded(
child: LinearProgressIndicator(backgroundColor: Colors.darkBlue, color: Colors.blue, value: 55, minHeight: 20)),
IconButton(
icon: Icon(Icons.stop_circle_outlined),
padding: const EdgeInsets.all(0),
)
])
],
));
I don't think there is a way to do this. I wound up just making my own version of IconButton without the padding.
I have an issue with Scaffold. I have an AppBar with text and a button. The body hosts a ListView.builder. Inside the ListView is a container and a number of TextButton Widgets.
The Text part of the TextButton can be wide, and causes the warning bar to appear. So, I wrapped in an Expanded widget to wrap it.
This results in an error...
The following assertion was thrown during performLayout(): RenderFlex
children have non-zero flex but incoming width constraints are
unbounded.
When a row is in a parent that does not provide a finite width
constraint, for example if it is in a horizontal scrollable, it will
try to shrink-wrap its children along the horizontal axis. Setting a
flex on a child (e.g. using Expanded) indicates that the child is to
expand to fill the remaining space in the horizontal direction. These
two directives are mutually exclusive. If a parent is to shrink-wrap
its child, the child cannot simultaneously expand to fit its parent.
Consider setting mainAxisSize to MainAxisSize.min and using
FlexFit.loose fits for the flexible children (using Flexible rather
than Expanded). This will allow the flexible children to size
themselves to less than the infinite remaining space they would
otherwise be forced to take, and then will cause the RenderFlex to
shrink-wrap the children rather than expanding to fit the maximum
constraints provided by the parent.
and also the contents of my AppBar disappear, and I can no longer click the back arrow.
Here is the scaffold to build the page...
late StateSetter _setState;
#override
Widget build(BuildContext context) {
return buildSubFormPage();
}
Widget buildSubPage() {
return StatefulBuilder(
builder: (bContext, StateSetter setBState) {
_setState = setBState;
return Scaffold(
appBar: new AppBar(
title: new Text("My list of items"),
actions: [
Padding(
padding: EdgeInsets.only(
right: 20.0),
child: openSubFormItem(
widget.field, 0,
"+ Add new", null),
)
],
),
body: ListView.builder(
padding: EdgeInsets.all(8.0),
//shrinkWrap: true,
itemCount: subFormItems.length,
itemBuilder: (context, index) {
return Container(
//width: screenWidth,
child: openSubFormItem(
widget.field, index, "Press Here",
subFormItems[index]),
);
},
)
);
}
);
}
Here is mostly the content of my openSubFormItem widget.
The building of the button text has been removed, but you can get the idea...
Widget openSubFormItem(PendigoFields field, int index, String buttonText, dynamic subFormItem) {
String outputText = "A long string of text, that will surely cause the warning bars if I can't make it wrap properly";
if (buttonText == "+ Add new") {
outputText = buttonText;
}
return Container(
//padding: EdgeInsets.all(4),
//width: screenWidth - 30,
child:
TextButton(
child: Row(
mainAxisSize: MainAxisSize.max,
//mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Flexible(
// fit : FlexFit.loose,
// child: Text(outputText, style: TextStyle(color: Colors.black,
// fontSize: 15.0,
// fontWeight: FontWeight.bold,),),
// ),
Expanded(child: Text(outputText, style: TextStyle(color: Colors.black,
fontSize: 15.0,
fontWeight: FontWeight.bold,),),
),
// Text(outputText, style: TextStyle(color: Colors.black,
// fontSize: 15.0,
// fontWeight: FontWeight.bold,),),
// I also have an IconButton here, removed for clarity
]
),
)
decoration: buttonText == "Press Here" ? BoxDecoration(
border: Border(
bottom: BorderSide(color: Colors.purple[400]!)),
)
: null
);
}
How do I get my AppBar to show the AppBar content (and button) and allow my ListView to be able to wrap the text if the text is too long?
As the error says: unbounded width, this is because the Row parent which is the TextButton does not have a specified width, and when using Expanded, the Row or the Column parent of it must know its constrains.
Give a width to the TextButton.
Also a note, don't create your widgets using methods as this is not recommend from flutter team, use separated widgets instead.
I'm trying to put an image inside a long text being wrapped in a Dart AlertDialog() widget. Let me illustrate the problem with some images.
What I want:
What I want
What I have:
What I have
(The coin Image() is flushed to the line after my text while I want it being part of it).
I'm currently trying to use the Wrap() widget, see the minimal code below
Wrap(children: [
Flexible(
child: Text('Are you sure you want to buy this item for ' +
item.price.toString())),
Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 0, 5),
child: Image.asset(
"path/to/my/image",
width: 30,
)),
Text('?'),
]),
It is supposed to place its children's widgets below each other when reaching the width of the Wrap() context. However, it's not working as I would like, since it put the Image widget below the Text() one being wrapped by Flexible() instead of putting it next to this last.
My opinion about this problem is that the wrapping of a text line still creates a "text box" that will go until the end of the context width (i.e. the end of the line) even if the last line of text stops earlier. This is due to the fact that the previous line had obviously reached the width of the Wrap() context. This is probably why the Image.assets() inserted as the next widget in the Wrap() children list is flushed to the next line anyway.
Of course I've tried several other approaches, namely to use the Row() widget with the Text() widget wrapped inside a Flexible() widget as follow:
Row(children: [
const Flexible(
child: Text(
'Are you sure you want to buy this item for ' + item.price.toString(),
)),
Row(children: [
Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 0, 5),
child: Image.asset(
"path/to/my/image",
width: 30,
)),
Text('?'),
]),
Which of course does not work since it places the Image() widget not next to the last word of the wrapped text but next to the whole generated Flexible() text zone.
Of course, the problem comes with the fact that the text is long and is, therefore, broken into several lines. The first approach would have worked perfectly if the text was not broken into several lines.
Is there any way to either
Limit the size of a wrapped line in order to avoid it being considered as a full line when wrapped;
Insert an image as a text character so that only the use of Flexible() do the trick to solve this problem;
Break a row widget into several lines automatically.
Thanks in advance for your time
Try Text.rich:
Text.rich(TextSpan(
children: <InlineSpan>[
TextSpan(text: 'Flutter is'),
WidgetSpan(
child: Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 0, 5),
child: Image.asset(
"path/to/my/image",
width: 30,
),),),
TextSpan(text: '?'),
],
)
I think the text widget extends to the end of the line which prevents the image from getting behind. To solve this problem I propose to split the first sentence into two blocks of text like this:
Wrap(children: [
Flexible(
child: Text('Are you sure you want to')),
Flexible(child: Text('buy this item for ' )),
Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 0, 5),
child: Image.asset(
"path/to/my/image",
width: 30,
Text('?'),
]),
Other way is to look on the way of Stack widget which seem to be a good solution
Display:
Code
Card(
child: Wrap(children: [
Text('Hi. What\'s your name?'),
Container(
constraints: BoxConstraints(minWidth: 300), child: TextField())
]))
Demo
https://dartpad.dev/65527c29343bd9afca842dff66907e6f?null_safety=true
What's needed
I would like for the TextField to run adjacent to right of the Text. It should have a minimum constraint width of 300. Then if the available space to the right of the Text is less than 300, it should wrap and place itself below the text.
Basically, I need to implement similar method to Expadable for the TextField that only covers the first run of the Wrap.
Problem
The TextField automatically extends to the largest possible width.
Because this is a Wrap, it automatically sends it below the initial Text where it has the most available width.
Use Row Widget instead of Wrap and expand the text
Card(
child: Row(children: [
Expanded(child: Text('Hi. What\'s your name?')),
Container(width: 300, child: TextField())
]),
),
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.')),
),
],
)