skip to Main Content

enter image description here

Goal is to center first widget and put to left second widget. However, if centered widget is too long, it should wrap, not overlap on left widget. That’s why Stack is not working.

return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(38.0),
        child: Column(
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text("Center me"),
                Text("I am left"),
              ],
            ),
            Row(
              children: [
                Expanded(
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [Text("Not very centered")]
                  ),
                ),
                Text("I am left"),
              ],
            ),
            Stack(
              children: [
                Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [Text("Center me, I should wrap because I am too loooooooooooooong ")],
                ),
                Positioned(
                  right: 0,
                  child: Text("I am left"),
                ),
              ],
            )
          ],
        ),
      ),
    );

I think it’s a very basic common used UI, it would be nice to have some sort of MainAxisAligment.centerAndLeft or some Flexible extension for this.

(The trick that I am currently using to make this work is to have left widget in a variable and put it on both side, then use spaceBetween , and make Opacity to zero for the right one, but it is an overkill, and reduce available space if the left widget is too long)

2

Answers


  1. Cache your text widget into a local variable:

    Widget textWidget = Text("I am left");
    

    And put the text variable to build method and wrap with visibility widget below:

    Row(
      children: [
        // put the cached text widget to add some equal space
        // and set visibility to false and set some property
        // below to maintain its size with Visibility widget
        Visibility(
          visible: false,
          maintainSize: true,
          maintainAnimation: true,
          maintainState: true,
          child: textWidget,
        ),
        const Expanded(
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text("Now very centered"),
            ],
          ),
        ),
        // put the cached text widget
        textWidget,
      ],
    ),
    

    The complete code:

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        // cache the text widget
        Widget textWidget = Text("I am left");
    
        return MaterialApp(
          home: Scaffold(
            body: Padding(
              padding: const EdgeInsets.all(38.0),
              child: Column(
                children: [
                  Row(
                    children: [
                      Expanded(
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            Text("Not very centered"),
                          ],
                        ),
                      ),
                      Text("I am left"),
                    ],
                  ),
                  Row(
                    children: [
                      // put the cached text widget to add some equal space
                      // and set visibility to false and set some property
                      // below to maintain its size with Visibility widget
                      Visibility(
                        visible: false,
                        maintainSize: true,
                        maintainAnimation: true,
                        maintainState: true,
                        child: textWidget,
                      ),
                      const Expanded(
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            Text("Now very centered"),
                          ],
                        ),
                      ),
                      // put the cached text widget
                      textWidget,
                    ],
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }
    

    Result:
    enter image description here

    Login or Signup to reply.
  2. So Adan kinda did what you want but I think you wanted something like if text is too long it should go to the left side. So I added some stuff to Adan’s code. Let me explain what I did.

    I added centerText to an element because I assume that the information will come from backend. I added a LayoutBuilder widget to the Expanded so that I can get the box’s constraints. In the initState method, I called checkOverflow after rendering done. That code will create a TextSpan with TextPainter so normally how much space as width that Text element will get. After that if it does, it will set the state and the Visible element will be gone, your text will still centered but that will take much more space. If the text exceeeds that too, then it will became ‘…’

    This is the full code:

    class _HomePageState extends State<HomePage> {
      final String centerText = 'Now very centered';
      late BoxConstraints constraints;
    
      bool isOverflow = false;
    
      @override
      void initState() {
        super.initState();
    
        WidgetsBinding.instance.addPostFrameCallback((_) {
          checkOverflow(context, constraints);
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            body: SafeArea(
              child: Padding(
                padding: const EdgeInsets.all(38.0),
                child: Column(
                  children: [
                    Row(
                      children: [
                        // if text is not overflow make it centered with the same
                        // text widget element. If there is an overflow make this
                        // widget not showable so Expanded can grow
                        if (!isOverflow)
                          const Visibility(
                            visible: false,
                            maintainAnimation: true,
                            maintainState: true,
                            maintainSize: true,
                            child: Text('I am left'),
                          ),
                        Expanded(
                          child: LayoutBuilder(
                            builder: (context, constraints) {
                              this.constraints = constraints;
    
                              return Container(
                                color: Colors.amber,
                                child: Text(
                                  centerText,
                                  maxLines: 1,
                                  overflow: TextOverflow.ellipsis,
                                  textAlign: TextAlign.center,
                                ),
                              );
                            },
                          ),
                        ),
                        // put the cached text widget
                        const Text('I am left'),
                      ],
                    ),
                  ],
                ),
              ),
            ),
          ),
        );
      }
    
      checkOverflow(BuildContext context, BoxConstraints constraints) {
        // Build the textspan
        var span = TextSpan(
          text: centerText,
        );
    
        // Use a textpainter to determine if it will exceed max lines
        var tp = TextPainter(
          maxLines: 1,
          textAlign: TextAlign.left,
          textDirection: TextDirection.ltr,
          text: span,
        );
    
        // trigger it to layout
        tp.layout(maxWidth: constraints.maxWidth);
    
        // whether the text overflowed or not
        var exceeded = tp.didExceedMaxLines;
        debugPrint(exceeded.toString());
    
        setState(() {
          isOverflow = exceeded;
        });
      }
    }
    

    These are the examples:
    Short Text
    enter image description here

    Long Text No Overflow
    enter image description here

    Long Text With Overflow
    enter image description here

    I hope this is what you wanted. Happy coding!!!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search