flutter

Flutter: Text overlapping with Positioned widget in Stack layout


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


Solution

  • I would suggest to follow this 2 steps:

    1. Place the Text("Walk Walk Walk") widget inside an Expanded widget:

      Expanded(child: Text("Walk Walk Walk")),
      
    2. 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:

    Narrow screen example

    Wide screen example

    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!