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(...)
)
)
Related
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.
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 want to add a tapable icon right after a long text. I need to use Text widget since I'm using textDirection and maxLines params.
Any ideas?
The problem:
What I need to get:
as Karen said it we need to use RichText.
Text.rich(
TextSpan(
text:
"I want to add a tapable icon right after a long text. I need to use Text widget since I'm using textDirection and maxLines params.",
children: [
WidgetSpan(
child: Icon(
Icons.error_outline,
color: Colors.red,
),
),
]),
),
the one way is to get char code of an icon, take a look at this answer for more details.
https://stackoverflow.com/a/63479589/10127437
Use a stack, let me show you...
Container(//Stack Widget needs space to work so be sure to add a container that gives it
width: double.infinity, //this makes the container match with the screen width
height: 200,
color: Colors.red,
child: Stack(
children: [
Positioned(//This widget gives position to your button
top: 100,//This will create a space on top
left: 0,//This will create a space on left
child: Container(//Your Container and text widget here
margin: EdgeInsets.only(left: 100),
height: 100,
width: 200,
color: Colors.blue,
child: Text(
"This is my text, my text takes more than one line, I want to present my Icon at the end of this text"
),
)
),
Positioned(
top: 140,
left: 150,
child: IconButton(//Put here your icon widget
iconSize: 30,
icon: Icon(CupertinoIcons.arrow_left_circle_fill),
color: Colors.blue,
)
),
],
),
)
As I alluded to in my previous post on the subject (Multi-line flutter text field occupies all of Flexible space with ugly right padding) I'm a bit of a perfectionist. Unfortunately my flutter layout-fu isn't as strong as my ambition. I'm creating a messaging app, and I'm working on adding a timestamp to the message box. My code so far (thanks also to this answer: Complex alignment of a sub widget based on wrapping text like in the Telegram chat messenger) is this:
Code to create a row:
Widget getAppUserMessageRow() {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
SizedBox(width: AppState.i.chatItemUserLeftInset),
Flexible(
fit: FlexFit.loose,
child: message.cm.messageType.getMessageWidget(message),
),
],
);
}
Code to create the message box itself:
Widget build(BuildContext context) {
bool isFromAppUser = message.cm.isFromAppUser(AppState.i.activeUserId);
return Container(
padding: EdgeInsets.symmetric(
vertical: AppState.i.chatItemMessageVerticalInset),
child:
Container(
decoration: BoxDecoration(
color: isFromAppUser ? AppState.i.chatItemUserMessageBackgroundColour : Colors.white,
//.withOpacity(!message.isFromAppUser ? 0.1 : 0.8),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(isFromAppUser ? AppState.i.chatItemMessageBorderRadius : 0),
topRight: Radius.circular(isFromAppUser ? 0 : AppState.i.chatItemMessageBorderRadius),
bottomRight: Radius.circular(isFromAppUser ? AppState.i.chatItemMessageCurvedBorderRadius : AppState.i.chatItemMessageBorderRadius),
bottomLeft: Radius.circular(isFromAppUser ? AppState.i.chatItemMessageBorderRadius : AppState.i.chatItemMessageCurvedBorderRadius),
),
boxShadow: [
BoxShadow(
color: AppState.i.chatItemMessageBoxShadowColour,
spreadRadius: AppState.i.chatItemMessageBoxShadowSpreadRadius,
blurRadius: AppState.i.chatItemMessageBoxShadowBlurRadius,
offset: AppState.i.chatItemMessageBoxShadowOffset, // changes position of shadow
),
],
),
padding: EdgeInsets.symmetric(
vertical: AppState.i.chatItemMessageVerInset,
horizontal: AppState.i.chatItemMessageHorInset),
child: Stack(
children: [
Text(
// WTF? This calculates enough space for the message receipt time to wrap
message.cm.messageText + (isFromAppUser ? ' \u202F' : ' \u202F'),
style: TextStyle(
fontSize: AppState.i.chatItemMessageTextFontSize,
color:
isFromAppUser ? AppState.i.chatItemMessageUserTextFontColour : AppState.i.chatItemMessageOtherUserTextFontColour,
),
textWidthBasis: TextWidthBasis.longestLine,
),
// This Positioned item creates the message timestamp in the area gained by adding the spaces above, assuming the spaces forced
// The text field into adding an extra line.
Positioned(
width: 60,
height: 15,
right: 0,
bottom: -2,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
DateFormat.Hm().format(message.cm.messageDisplayTime(AppState.i.activeUserId)),
style: TextStyle(
fontSize: AppState.i.chatItemMessageTimeFontSize,
color: isFromAppUser ? AppState.i.chatItemMessageUserTimeFontColour : AppState.i.chatItemMessageOtherUserTimeFontColour,
),
textAlign: TextAlign.right,
),
!isFromAppUser ? Container() :
Padding(
padding: EdgeInsets.only(left: AppState.i.chatItemMessageReceiptLeftInset),
child: message.cm.getReadReceiptIcon()
)
],
),
),
],
),
)
);
}
Here are some examples of good and bad - look at the right-hand-side padding on the bad examples. The timestamp should be more right-aligned with the text above:
And more:
Adding spaces + a non-printable character to make room for the date timestamp + read receipt combo is a genius trick, but with a downside. It pushes out the right alignment when the text is close to the space that will be occupied by the timestamp. So in some cases as per the above, the box consumes unnecessary space.
So, can this be fixed, or can a different layout be used to achieve the effect I'm looking for? I can't use anything too computational, because right now the performance is pretty decent when scrolling long lists.
To make this slightly more concrete, some 'fixed' examples.
Example: Message box with Timestamp
I think you can try this way:
Calculate/Simulate text and check the message is overlapping the timestamp.
If it overlaps, add \n to change the line. Otherwise, return the original one.
You need the following things:
LayoutBuilder: BoxConstraint above the text widget
TextPainter: Simulate the layout (also need the TextStyle, textWidthBasis or there may affect the layout result)
Here is the code:
// assume 'message' is a String need to be displayed
...
LayoutBuilder(builder: (context, constraints) {
final reserve = ' \u202F';
final originalPainter = TextPainter(
text: TextSpan(text: text, style: TextStyle(fontSize: 20)),
textDirection: TextDirection.ltr,
textWidthBasis: TextWidthBasis.longestLine,
)..layout(maxWidth: constraints.maxWidth);
final reservePainter = TextPainter(
text: TextSpan(text: text + reserve, style: TextStyle(fontSize: 20)),
textDirection: TextDirection.ltr,
textWidthBasis: TextWidthBasis.longestLine,
)..layout(maxWidth: constraints.maxWidth);
final changeLine = reservePainter.width > originalPainter.width + 0.001 || reservePainter.height > originalPainter.height + 0.001;
return Text(
changeLine ? text+'\n' : text,
...
)
}
I have a column with multiple children which are Flexible widgets with different flex values. (My actual implementation is more complex with nested rows/columns but finding a solution to this simple case would help)
When I change the Flex values programmatically, the children adapt nicely to their parent WITH a nice animation which comes out of the box. I can make them taller/shorter with ease.
However, when I want one of the children to cover all of the Column all by itself and set flex=0 for other children, it does not work. In the documentation it says setting flex to 0 or null means that the Flexible widget is no more "flexible"
How would I go about with this? Any idea is welcome.
As you can see from the image: I want to set the flex value of the Green area from 3 to 0 and only be able to see Blue area covering the whole screen.
Screenshot:
You can however use Flexible instead of Expanded for 2nd child, that will work too.
Code:
#override
Widget build(BuildContext context) {
int flex1 = 1, flex2 = 0;
return Scaffold(
appBar: AppBar(),
body: Column(
children: <Widget>[
Expanded(
flex: flex1,
child: Container(
color: Colors.blue,
height: 100,
alignment: Alignment.center,
child: Text("Flex: $flex1", style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold)),
),
),
Expanded( // you can use Flexible also
flex: flex2,
child: flex2 == 0 ? SizedBox() : Container(
color: Colors.green,
alignment: Alignment.center,
child: Text("Flex: $flex2", style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold)),
),
),
],
),
);
}