When adding text to a CircleAvatar, its position in the Layout changes slightly.
Since my CircleAvatars are displayed in a dynamic ListView, where some will be having text and others just an image, they need to be consistent in size and position.
I'm putting just the code example of the CircleAvatar here, since i think its positioning should be completely independent of its contents.
CircleAvatar(
backgroundColor: getColor(),
radius: radius,
child: Text(
initials,
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
fontSize: radius * 0.8,
),
),
);
I tried the following things to get rid of this difference:
fit everything in a ConstrainedBox/SizedBox
set minRadius/maxRadius of the CircleAvatar
centering the text
setting TextStyle.height
placing the Text via Stack
reducing the Size of the Text (changes the amount of the offset, but even a tiny size already messes up the position)
However, nothing of that worked and I couldn't figure out where this difference comes from.
I don't think that it should matter, but it is used in the subtitle of several ListTiles.
Dumbed down example, can easily be tested in DartPad:
ListView(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: const [
Card(
child: ListTile(
title: Text("Item1"),
subtitle: CircleAvatar(
backgroundColor: Colors.white,
radius: 12,
),
),
),
Card(
child: ListTile(
title: Text("Item2"),
subtitle: CircleAvatar(
backgroundColor: Colors.white,
radius: 12,
child: Text(
"_",
style: TextStyle(fontSize: 10),
),
),
),
),
]),
The layout looks like this
Overlay of two exact same ListTiles to emphasize the change in position, one with a Text(), and one without
I solved it by simply adding an empty Text to the CircleAvatar that shouldn't contain any.
Not optimal, but at least it's consistent.
Related
I have this small code:
Container(
color: Colors.blue,
child: Row(
children: [
Expanded(
child: Container(
color: Colors.red,
child: const Text('Red text that can be long long long long long'),
),
),
Flexible(
child: Container(
color: Colors.green,
child: const Text('Small text'),
),
),
],
),
),
which renders into this:
Notice how the long text is wrapped even though there is still space around the small text.
I would like the small text to only take the size it needs to let the long text take the rest. And if the small text becomes bigger, its maximum size would be the size of the Flexible widget in the row (half of the Row in my example since both Expanded and Flexible have flex: 1).
If the small text is small enough, it should look like:
| Red text that can be long long long long | Small text |
| long | |
And if the "small" text is long too:
| Red text that can be long | Small text that is also |
| long long long long | very long |
Is there a way to do that?
You can use ConstrainedBox:constraints: BoxConstraints.loose( Size.fromWidth(maxWidth / 2) for green.
body:LayoutBuilder(
builder: (context, constraints) => Row(
children: [
Expanded(
child: Container(
color: Colors.red,
child: const Text(
'Red text that can be long long long lbe long long long lbe long long long long long'),
),
),
ConstrainedBox(
constraints: BoxConstraints.loose(
Size.fromWidth(constraints.maxWidth / 2)),
child: Container(
color: Colors.green,
child: const Text('Smalled g long th xt'),
),
),
],
),
),
In the end I used Yeasin Sheikh's answer so I'm validating it.
But I'm writing this comment to add more precisions on what I have found during my investigations.
At first, I wanted a simple widget like Flexible or Expanded (let's say LooseFlexible) to wrap my widget with:
View upvote and downvote totals.
I have this small code:
Container(
color: Colors.blue,
child: Row(
children: [
Expanded(
child: Container(
color: Colors.red,
child: const Text('Red text that can be long long long long long'),
),
),
LooseFlexible(
flex: 1
child: Container(
color: Colors.green,
child: const Text('Small text'),
),
),
],
),
),
but it seems it would go against the way Flutter works.
I took a look a the flutter class RenderFlex, which is the render object the Flex class (a class that Row and Column are extending) returns in the method createRenderObject.
It has a method _computeSizes, and inside it goes through the children (one by one):
if it does not have a flex value ("normal widget"):
It gives to the child no max constraint for the layout and gets its size.
if it has a flex value (from Flexible or Expanded):
it accumulates in a totalFlex value the some of all the flex (and does not render the child).
Then it goes through all the widgets that have a flex value and gives them as a max constraint (flex / totalFlex) * (maxConstraintOfParent - totalSizeOfNonFlexChildren) and layout them with it.
So each child is layout only once.
If my widget LooseFlexible were to exist, and if we had this code:
Row(
children: [
Expanded(flex: 1),
LooseFlexible(flex: 1),
Expanded(flex: 1),
],
),
Let's say the row has a maxWidth of 300. There is no non-flex children and totalFlex = 3.
The 1st Expanded would be laid out with a maxWidth = 100. Since it is an Expanded its width will be 100.
The Flexible widget is also laid out with a maxWidth = 100. But its size could be 50 (and not 100).
Now comes the turn of the last Expanded, if we want it to take the entire remaining space, its size would be 150 which is not the same as the 1st Expanded with the same flex = 1 value. So the first child would need to be re-laid out, which is not following the flutter way of rendering children only once.
Row(
children: <Widget>[
Expanded(
child:Container(
color: Colors.green,
child: const Text('Small text'),
),
),
Expanded(
child: Container(
color: Colors.red,
child: const Text('Small text'),
),
),
]
)
I am using listview.builder to show the api data, i want to set the text padding, here is the output
i want to set the approved and not approved text after the date.
here is my code
ListTile(
leading: CircleAvatar(
radius: 25,
backgroundColor:snapshot.data[index].type=="Sick"?Color(int.parse(annualboxcolor)):Color(int.parse(maternityboxcolor)),
child: Container(
padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
child:Column(children: <Widget>[
Text(snapshot.data[index].noofdays.toString(),style: TextStyle(fontSize: 15.0,fontWeight: FontWeight.bold,color:Colors.white),),
Text(int.parse(snapshot.data[index].noofdays)>1?"Days":"Day",style: TextStyle(fontSize: 10.0,color:Colors.white),)
]
),
)),
title: Text(snapshot.data[index].type),
subtitle: Text(snapshot.data[index].fromdate+" To "+snapshot.data[index].todate),
trailing: Text(snapshot.data[index].Status=="ON"?"Approved":"Not Approved"),),
Container(padding: EdgeInsets.fromLTRB(80, 55, 0, 0),
child:Text(snapshot.data[index].Status=="ON"?"Approved":"Not Approved",
style: TextStyle(color:snapshot.data[index].Status=="ON"?Colors.green:Colors.red ),),),
i used container and set its padding but i want to do this in listtile
Instead using trailing, set isThreeLine to true, that makes the ListTile to display 3 lines of text instead of 2. After that, make subtitle a two line string with the data and the approved state, something like this:
ListTile(
// your existing code
isThreeLine: true,
// concatenate you strings, this is just an example,
// mind the `\n`, it makes new line
subtitle: Text("2021-02-02 To 2021-03-03\nNot Approved"),
// don't use trailing
// trailing:
)
If you need different styling, there is another way, using a Column in subtitle, and add separate Text widgets:
ListTile(
// ...
isThreeLine: true,
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("2021-02-02 To 2021-03-03",
style: TextStyle(color: Colors.green)),
Text("Not Approved",
style: TextStyle(color: Colors.red))
],
)
)
Try below code hope it's helpful to you use isThreeLine instead of trailing
ListTile(
leading: CircleAvtar(
child:Text('Sick'),
),
title: Text('Sick'),
subtitle:Text('Your Date\n Approved'),
isThreeLine:true,
),
I am using SfRadialGauge plugin its working fine but there I am facing 2 issues.
Its showing lot of space on top, if I give height to container the sizes of SfRadialGauge is reducing also the top space I don't want to reduce size I just want to remove the space
Same space in GaugeAnnotation. I have column inside GaugeAnnotation and need to show little on top.
My code
Widget getSemiCircleProgressStyle() {
return Padding(
padding: const EdgeInsets.all(0.0),
child: Container(
child: SfRadialGauge(axes: <RadialAxis>[
RadialAxis(
showLabels: false,
showTicks: false,
startAngle: 180,
canScaleToFit: true,
endAngle: 0,
radiusFactor: 0.8,
axisLineStyle: AxisLineStyle(
thickness: 0.1,
color: const Color.fromARGB(30, 0, 169, 181),
thicknessUnit: GaugeSizeUnit.factor,
cornerStyle: CornerStyle.startCurve,
),
pointers: <GaugePointer>[
RangePointer(
value: 100,
width: 0.1,
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
animationDuration: 100,
animationType: AnimationType.linear,
cornerStyle: CornerStyle.bothCurve)
],
annotations: <GaugeAnnotation>[
GaugeAnnotation(
positionFactor: 0,
widget: Container(
child: Stack(
alignment: Alignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(0.0),
child: RatingBarIndicator(
rating: 0.2,
itemBuilder: (context, index) => Icon(
Icons.star,
color: Color(0xffF4D03F),
),
itemCount: 1,
itemSize: 45.0,
direction: Axis.vertical,
),
),
RichText(
text: new TextSpan(
// Note: Styles for TextSpans must be explicitly defined.
// Child text spans will inherit styles from parent
style: new TextStyle(
fontSize: 14.0,
color: Colors.black,
),
children: <TextSpan>[
new TextSpan(
text: '2',
style: new TextStyle(
fontSize: 40, fontFamily: 'UbuntuRegular')),
new TextSpan(
text: ' /5',
style: TextStyle(
fontSize: 25,
color: Colors.grey[400],
fontFamily: 'UbuntuRegular')),
],
),
),
Text(
'FIFTEEN DAYS SCORE',
style: TextStyle(
color: Colors.grey[400], fontFamily: 'UbuntuMedium'),
)
],
)
],
),
))
]),
])),
);
}
This is how its look like
I have marked the space with red pen. IF any one can help to reduce these spacing I am stuck on this from a day :(
Been having the same problem too.
I found that the solution is to set :
canScaleToFit: false
But, this only works if you use the default circular gauge. For the semi-circular gauge, it will still left some blank spaces at the bottom.
I had the same problem, I guess I'm late but hopefully this can be useful to others.
Premises
Note. At the moment I can't find a way to have FULL control of this widget's white space. I only have found the following workaround to get rid of the white space above and under the gauge.
The library's devs mentioned two things:
"... For better understanding, say if the container has different width and height (say 300x800), we will always take the lowest dimension (here it is the width) to render the radial gauge ..."
"... We are sorry that we cannot provide any workaround to trim ..."
"... as this is the default rendering behaviour of the Radial gauge, we cannot consider your suggestion as a feature request."
The first statement is really useful, as it enables a workaround (see below).
The second statement.. I disagree, that's not something "impossible" to work around with.
The third statement... well that's just depressing.
Workaround
Thanks to the Flutter DevTools, I noted that the Gauge renders a NeedlePointer Widget, which is the one rendering the cursed white space. As the devs mentioned, the gauge takes as much space as it can in the smallest direction.
This means that as long as you restrict height and width, you should be fine. I don't know in which context your Gauge is, but in mine the following code works great.
return LayoutBuilder(
(context, constraints) => Container(
padding: const EdgeInsets.all(0),
height: constraints.maxWidth / 1.5, // WORKAROUND
child: Column(
children: [
Flexible(
child: SfRadialGauge(...),
),
MaybeSomeLabelsWidgets(),
SomeOtherWidgets(),
],
),
),
);
So, the fundamental part here is the fact that height is restricted and that there's a Flexible Widget that lets the Gauge take minimum space. LayoutBuilder in my case/context is useful just because I wanted to render the plot with a "3x2" feeling.
As a last note, keep the canScaleToFit=true, so that your plot is centered (why is it called canScaleToFit? That's another mistery I guess...).
Let me know if this helps.
A simple workaround is to use the ClipRect widget. The following will discard the top 20% of the SfRadialGuage.
ClipRect(
child: Align(
alignment: Alignment.bottomCenter,
heightFactor: 0.8,
child: SfRadialGauge(...)
)
)
i'm trying to make nice Card()
there's one problem.
whenever I write so many words in subtilte propertie
leading, title, trailing position changes.
I want these position fixed
as you can see the Star and 'coding' and 'three dots' s postion is moved
how can I do this?
here's my code
Card(
child: ListTile(
leading: Text(
'⭐️',
style: TextStyle(fontSize: 35.sp),
),
title: Text('coding'),
subtitle: Text('flutterflutterflutter'),
trailing: Icon(Icons.more_vert),
),
),
Card(
child: ListTile(
leading: Text(
'⭐️',
style: TextStyle(fontSize: 35.sp),
),
title: Text('coding'),
subtitle: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'flutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutterflutter '),
],
)),
trailing: Icon(Icons.more_vert),
),
),
This has nothing do with your layouts, and more to do with the size of the subtitle text.
In this case, I would suggest to add a maximum line number count on the Subtitle attribute of the ListTile Widget, thus so that if the number of words exceeds a certain point, it will not ruin the position of the Star widget & the '3 dots' widget.
Looking at this, 3 lines as the max line number should be enough.
I was working with a drawer which had a list of tiles. Each individual tile has an image and a text following it. So I put the above mentioned widgets in a row. To my surprise, I found that there is some unexplained padding between the text and the title. I am attaching a screenshot of a tile for better understanding :
Any suggestion is welcome!. Thank you!.
I have added rowChildren which is a list of widgets because my list tile title may contain text and some image :
child = new Container(
child: new Column(
children: <Widget>[
new ListTile(
dense: true,
leading: new Image.asset(
leading,
color: Colors.white,
)
title: new Row(
children: rowChildren,
),
)
],
),
);
and this is the flutter inspector screenshot corresponding to the image I shared :
I added debugPaintSizeEnabled=true and got this :
how about using contentPadding
ListTile(
contentPadding:EdgeInsets.all(0),
dense: true,
leading: new Image.asset(
leading,
color: Colors.white,
),
title: new Row(
children: rowChildren,
),
),
I fixed it!!!.The solution is, Instead of giving the leading image in leading attribute, add it in the same rowChildren. The gap disappears!
List<Widget> rowChildren = [];
rowChildren.add(
PaddingUtils.embedInPadding(
new Image.asset(
$imagePath,
),
new EdgeInsets.only(right: 8.0)),
);