TextSpan's text doesn't show if it overflows - flutter

That's how it looks when I have a WidgetSpan and a TextSpan, and the text inside the TextSpan is too long:
problem
A similar question has already been asked here TextSpan's text doesn't show if it overflows and is placed after WidgetSpan, but the one answer isn't suitable for my problem.
I have two Text.rich() widgets (which each host one WidgetSpan and one TextSpan) inside an Expanded widget, both Expanded with flex:1, because I want them to be the same size and take as much space as they can. That's why a row is not an option, you can't (as far as I know) put two rows side by side, at this happens...
using rows
This is how it's supposed to look like, just with the TextOverflow.ellipsis, if the text is too long:
target UI
Here is my code:
Row(
children: [
Expanded(
flex: 1,
child: Text.rich(
TextSpan(children: [
WidgetSpan(
child: Container(
margin: const EdgeInsets.only(right: 4.0),
child: SvgPicture.asset("assets/icons/building.svg", color:
htLightGrey),
),
),
TextSpan(
text: incident.building.name,
style: const TextStyle(
fontWeight: FontWeight.normal,
fontSize: 16.0,
color: htLightGrey, /*overflow: TextOverflow.ellipsis*/
),
),
]),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Expanded(
flex: 1,
child: incident.department != null
? Text.rich(
TextSpan(children: [
WidgetSpan(
child: Container(
margin: const EdgeInsets.only(right: 4.0),
child: SvgPicture.asset("assets/icons/department.svg",
color: htLightGrey),
),
),
TextSpan(
text: incident.department!.name,
style: const TextStyle(fontWeight: FontWeight.normal,
fontSize: 16.0, color: htLightGrey, overflow:
TextOverflow.ellipsis),
),
]),
maxLines: 1,
overflow: TextOverflow.ellipsis,
)
: const SizedBox.shrink(),
)
],
)

To achieve your target Ui, use the Wrap widget instead of the Row widget. Here, when the contents of the widget overflow/exceed it will break it to the next line, hence you'll achieve your target Ui.

While we like to archive this UI, we are using Row as parent widget, which is perfect. Then we can split the row into tree part, left two part will get same and maximum available width. For this scenario, widget structure as follows.
Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
flex: 1,
child: Container(
color: Colors.green,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, //start from left
children: [
buildItem(
text:
"incident.buildi cident.buildi cident.buildi cident.buildi cident.building.name"),
buildItem(text: "incide ing.ssss"),
],
),
),
),
Expanded(
flex: 1,
child: Container(
color: Colors.red,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildItem(text: "i ent.building.name"),
buildItem(text: "incident.building.ssss"),
],
),
),
),
IconButton(
onPressed: () {},
iconSize: 33,
icon: Icon(Icons.arrow_right),
),
],
),
And inside Text.rich add use alignment: PlaceholderAlignment.middle, on WidgetSpan to set the middle.
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.start,
And you will get
I am using extra container to point the UI, simply remove the container from the column. Also, you can use Row instead of Rich Text.
Test widget
class _TestGroundState extends State<TestGround> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
flex: 1,
child: Container(
color: Colors.green,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start, //start from left
children: [
buildItem(
text:
"incident.buildi cident.buildi cident.buildi cident.buildi cident.building.name"),
buildItem(text: "incide ing.ssss"),
],
),
),
),
Expanded(
flex: 1,
child: Container(
color: Colors.red,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildItem(text: "i ent.building.name"),
buildItem(text: "incident.building.ssss"),
],
),
),
),
IconButton(
onPressed: () {},
iconSize: 33,
icon: Icon(Icons.arrow_right),
),
],
),
],
));
}
Text buildItem({
required String text,
}) {
return Text.rich(
TextSpan(
children: [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Container(
child: Icon(Icons.ac_unit),
),
),
TextSpan(
text: text,
style: TextStyle(
fontWeight: FontWeight.normal,
fontSize: 16.0,
),
),
],
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.start,
);
}
}
More about flutter layout

Related

Align a text in Flutter to make a restaurant menu

I'm trying to make a menu for a reastaurant like this one : -
I'm using Flutter and this is my code:
return Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: Row(
children: [
Expanded(child: Text( text, style: style, )),
const SizedBox( width: 15 ),
Text( '\$ $price', style: style,),
],
),
),
Divider( color: Colors.white.withOpacity( 0.5 ),),
],
);
But this is the result that I'm getting : -
Please, any help will be welcome.
And thanks in advance.
The result that you want to achieve can be done just by using the row widget, you don't have to wrap it up with ListTile. I am writing the code snippet of Row widget for you. You can copy paste it and it will work. Additionally I see that you are generating it in the column widget. If you have a dynamic array then I suggest that you use ListView Divider instead. It will have a divider to add between your rows as well. You can find more about it on flutter official documentation.
Row(
children:[
//You may add some padding to Text for better looking UI.
Text("Chicken Masala"),
Expanded(child:Divider()),
Text("180")
])
Just replace your column's children with this row and you will be able to see it.
I solved your issue like this:
Output of your issue : -
body: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: Row(
children: [
const Text(
'Cheese Burger',
),
Expanded(child: Text('.' * 100, maxLines: 1)),
const Text(
'\$ 10',
),
],
),
),
const SizedBox(
height: 10,
),
ListTile(
title: Row(
children: [
const Text(
'Chicken Masala Soup',
),
Expanded(child: Text('.' * 100, maxLines: 1)),
const Text(
'\$ 10',
),
],
),
),
],
),
#Ramji, #Dhruvil Patel, I try your code and it work fine, but it was making an overflow when the text was long, so I made some changes in your example and I got what I was looking for.
Thanks a lot for you help.
Here is what I dit.
return SizedBox(
width: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Expanded( child: Text( text, style: style, maxLines: 2, overflow: TextOverflow.ellipsis, ) ),
],),
Row(
children: [
Expanded(
child: Text(
'.' * 150,
style: style,
maxLines: 1,
overflow: TextOverflow.fade,)),
SizedBox(
width: 80,
child: Text(
'\$ $price',
style: style,
textAlign: TextAlign.right,)),
],
),
// Divider( color: Colors.white.withOpacity( 0.5 ),),
],
),
);

How to fix Bottom Overflowed by 10 Pixels?

As you can see in the these two photos below, this problem happened only when the address contains many words, if the address contains fewer words I don't see any issues, I got stuck here and can't think anymore :)
what shall I do in this case, please?
Code:
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
UserAvatarView(
urlPrefix: serverUrl,
url: null,
cornerRadius: 60,
size: 20),
const SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
order.service.name,
style: Theme.of(context).textTheme.titleMedium,
),
Text(
"${(driverDistance / 1000).round()} KM away",
style: Theme.of(context).textTheme.labelMedium,
)
],
),
const Spacer(),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: CustomTheme.primaryColors.shade200,
borderRadius: BorderRadius.circular(12)),
child: Text(
NumberFormat.simpleCurrency(name: order.currency)
.format(order.costBest),
style: Theme.of(context).textTheme.headlineMedium,
),
)
],
),
const Divider(),
...order.addresses.mapIndexed((e, index) {
if (order.addresses.length > 2 &&
index > 0 &&
index != order.addresses.length - 1) {
return const SizedBox(
width: 1,
height: 1,
);
}
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Padding(
padding: const EdgeInsets.all(0),
child: Icon(
getIconByIndex(index, order.addresses.length),
color: CustomTheme.neutralColors.shade500,
),
),
Expanded(
child: Text(e,
overflow: TextOverflow.visible,
style: Theme.of(context).textTheme.bodySmall),
),
if (index == order.addresses.length - 1)
Text(
order.durationBest == 0
? ""
: durationToString(
Duration(seconds: order.durationBest)),
style: Theme.of(context).textTheme.bodySmall)
],
).pOnly(right: 0),
if (index < order.addresses.length - 1)
DottedLine(
direction: Axis.vertical,
lineLength: 25,
lineThickness: 3,
dashLength: 3,
dashColor: CustomTheme.neutralColors.shade500)
.pOnly(left: 10)
],
);
}).toList(),
const Spacer(),
Row(
children: order.options
.map((e) => OrderPreferenceTagView(
icon: e.icon,
name: e.name,
))
.toList()),
ElevatedButton(
onPressed: !isActionActive
? null
: () => onAcceptCallback(order.id),
child: Row(
children: [
const Spacer(),
Text(S.of(context).available_order_action_accept),
const Spacer()
],
).p4())
.p4()
],
),
Photo of a few address words:
Photo of many address words:
Try to wrap your Text() widget with a Flexible(). This should size your widget to make the Text fit.
More here
So i fixed it by adding maxLines and added softWrap and set it to true, (thanks to #Marcel Dz) for his suggestion, but it doesn't work with only Wrap the text with Flexible.
The problem is that the address is changeable, every address has a different number of words, when the address is long it's mean more words and these words are pushing the Button to the bottom because there's no more free space to show the full words of address.
here is the piece of code that i modified:
Expanded(
child: Text(e,
overflow: TextOverflow.visible,
maxLines: 3,
softWrap: true,
style: Theme.of(context).textTheme.bodySmall),
),
Another solution with Flexible:
Flexible(
child: Text(e,
overflow: TextOverflow.visible,
maxLines: 3,
softWrap: true,
style: Theme.of(context).textTheme.bodySmall),
),
Result:
Now it's working perfectly, and i can have as many words as i need, both Expended and Flexible can be used.

I want to align the starting place of two texts

I have one "row" and two "text" widgets inside. I want to align the starting place of two texts. ie "m. name" and "m105" should line up.
Container(
child: Row(
children: [
Container(
child: Align(
alignment: Alignment.topLeft,
child: Text(
"M. Adı: ",
style: TextStyle(fontWeight: FontWeight.bold),
),
),
),
// SizedBox(
// width: 20,
// ),
Expanded(
child: Text(
"$productName",
overflow: TextOverflow.ellipsis,
maxLines: 3,
softWrap: true,
),
),
],
),
),
Try
Row() widget attribute
crossAxisAlignment: CrossAxisAlignment.start
Text() widget attribute
textAlign: TextAlign.start,
/// Try something like that
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"M. Adı: ",
style: TextStyle(fontWeight: FontWeight.bold),
),
),
SizedBox(width: 5),
Flexible(
child: Text(
"$productName",
textAlign: TextAlign.start,
),
),
],
),
Add crossAxisAlignment: CrossAxisAlignment.center, to your Row widget it should be aligned as you desired or still if it doesnt works just try changing center with other values check which one works for you...
and even after adding above property to your Row widget then you would have to remove alignment of topleft in your text of m.name

How to solve for 'text overflow.ellipse' in flutter?

I am unable to make my text stop extending the screen and overflowing. My code is as follows:
class OrderTileDisplay extends StatelessWidget {
final MenuOrderData orderItem;
final editable;
OrderTileDisplay(this.orderItem, {this.editable = true});
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
GestureDetector(
onTap: (){},
child: Container(
color: Colors.transparent,
margin: EdgeInsets.symmetric(vertical: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Text(
orderItem.quantity.toString() + 'x',
style: Title16,
),
SizedBox(
width: 8,
),
Text(
orderItem.name, // OVERFLOWS WHEN IT IS A LONGER SENTENCE
style: Title18bold,
overflow: TextOverflow.ellipsis,
),
],
),
Row(
children: [
editable? Icon(Icons.edit, color: Colors.grey[200], size: 20,) : Container(),
SizedBox(width: 8,),
Text("£" + Provider.of<CP>(context).getTotalForItem(orderItem).toStringAsFixed(2),
style: Title18bold,),
],
),
],
),
),
),
Container(... and other stuff)
I have tried putting Text inside a container then Expanded widget but I keep getting errors. Not sure how to solve it.
Try to wrap your Row and Text inside a Flexible:
Flexible( //flexible here
child: Row(
children: [
Text(
orderItem.quantity.toString() + 'x',
style: Title16,
),
SizedBox(
width: 8,
),
Flexible( //and here
child: Text(
orderItem.name, // no more overflow
style: Title18bold,
overflow: TextOverflow.ellipsis, //working as expected
),
),
],
),
),
As the documentation states, TextOverflow says how overflowing text should be handled, but it will not work if it's parent don't have a fixed size. In this case, Flexible is been used to restrict the total size of the text to prevent overflow, when it reaches a very large width.

How to remove the space between rows in card (Flutter)

I used a card widget in my flutter App that has two rows, the problem that I have is a large space appeared between the two rows as you can see in the following image
how can fix this problem ?
my code that i tried it here :
return Card(
child: Container(
alignment: FractionalOffset.center,
child: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
child: ListTile(
title: Text(snapshot.data[index].data["title"],
textDirection: TextDirection.rtl,
style: TextStyle(fontSize: 14.0,
fontFamily: 'Cairo',
fontWeight: FontWeight.bold),),
onTap: () => navigateToDetails(snapshot.data[index]),
),
),
),
//SizedBox(width: 1.0),
Expanded(
flex: 1,
child: Container(child: Image.network(snapshot.data[index].data["image_url"],height: 80.0,width: 80.0)),
),
],
),
),
);
add MainAxisAlignment:MainAxisAlignment.center property to your Row widget
followed code should work for you check this out.
return Card(
child: Container(
alignment: FractionalOffset.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 1,
child: Container(
child: ListTile(
title: Text(
snapshot.data[index].data["title"],
textDirection: TextDirection.rtl,
style: TextStyle(
fontSize: 14.0,
fontFamily: 'Cairo',
fontWeight: FontWeight.bold),
),
onTap: () => navigateToDetails(snapshot.data[index]),
),
),
),
//SizedBox(width: 1.0),
Expanded(
flex: 1,
child: Container(
child: Image.network(snapshot.data[index].data["image_url"],
height: 80.0, width: 80.0)),
),
],
),
),
);
you are using expanded widget inside row so that both the widget try to get same width and also try to cover whole width possible.
so that, if you want to remove space between row widget then remove expanded widget and play with MainAxisAlignment property of Row to arrange or manage the space.
you can use values like:
MainAxisAlignment.spaceBetween and MainAxisAlignment.spaceEvenly