I'm working on a Flutter layout where I have a list of transport modes (like Walk, Car, Bus, Train), and I’m placing a profile avatar on top using a Stack
with a Positioned
widget
Here the profile avatar is not inside the 1th row
This is the Sample UI
Stack(
children: [
Container(
color: Colors.grey[300],
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text("Walk"),
Text("Car"),
Text("Bus"),
Text("Train"),
],
),
),
Positioned(
top: 0,
right: 0,
child: CircleAvatar(
radius: 24,
child: Icon(Icons.person),
),
),
],
)
Everything works fine until the text becomes too long, like in this case
If the text is too long this is how it paints problematic UI
I tried manually wrapping the text in sizedbox with some fixed width then there is not overlapping.
But this is not working for all the cases.
Stack(
children: [
Container(
color: Colors.grey[300],
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Row(
children: [
Icon(Icons.directions_walk),
SizedBox(width: 8),
// Manually restrict text width to avoid overlap
SizedBox(
width: 80, // <-- hardcoded width
child: Text("Walk Walk Walk"),
),
],
),
Row(
children: [
Icon(Icons.directions_car),
SizedBox(width: 8),
Text("Car"),
],
),
Row(
children: [
Icon(Icons.directions_bus),
SizedBox(width: 8),
Text("Bus"),
],
),
Row(
children: [
Icon(Icons.train),
SizedBox(width: 8),
Text("Train"),
],
),
],
),
),
Positioned(
top: 0,
right: 0,
child: CircleAvatar(
radius: 24,
child: Icon(Icons.person),
),
),
],
)
Is there a better way to lay this out so the Positioned
widget doesn't interfere with the text layout below — without hardcoding widths
I would suggest to follow this 2 steps:
Place the Text("Walk Walk Walk") widget inside an Expanded widget:
Expanded(child: Text("Walk Walk Walk")),
Add a SizedBox with a width of 32 at the end of the Row:
Row(
children: [
Icon(Icons.directions_walk),
SizedBox(width: 8),
// Expanded will use all the remaining space in the row
Expanded(child: Text("Walk Walk Walk")),
SizedBox(width: 32),
],
);
Why '32'? Your CircleAvatar has a radius of 24, so its width is 48. And you added a padding of 16 to your Column. So if you subtract 48 - 16 = 32. This is the needed space to avoid the overlapping.
This way you avoid overlapping while while taking advantage of all the remaining space, no matter the size of the screen:
Also, you should consider creating a variable for the avatar radius and the column padding, so that you can set the SizedBox width in the following way and avoid hardcoding values:
Row(
children: [
Icon(Icons.directions_walk),
SizedBox(width: 8),
// Expanded will use all the remaining space in the row
Expanded(child: Text("Walk Walk Walk")),
SizedBox(width: 2 * avatarRadius - padding),
],
);
Hope this help!