Search code examples
flutterdartflutter-column

Flutter align specific widget in column with min width


I'm implementing a chat on my flutter app, and I want my chat-time to be align at the right side of the column (all other widgets should be align left).

My problem is that when I'm using alignment.bottomRight in my column, it extends the column width to max (and I want to keep it min).

The problem:

enter image description here

What I'm trying to achieve:

enter image description here

my column:

return Bubble(
      margin: BubbleEdges.only(top: 5, bottom: 5),
      elevation: 0.6,
      alignment: Alignment.topLeft,
      nip: BubbleNip.leftTop,
      radius: Radius.circular(10),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          getUserChatTitle(document['userId']),
          Padding(
            padding: const EdgeInsets.only(top: 3, bottom: 5),
            child: SelectableLinkify(
              onOpen: onOpenLink,
              text: "Text Text Text",
              textAlign:
                  intl.Bidi.detectRtlDirectionality(document['content'])
                      ? TextAlign.right
                      : TextAlign.left,
              textDirection:
                  intl.Bidi.detectRtlDirectionality(document['content'])
                      ? ui.TextDirection.rtl
                      : ui.TextDirection.ltr,
              style: TextStyle(
                fontSize: 16,
                height: 1.35,
                letterSpacing: -0.1,
                fontFamily: 'Arimo',
              ),
            ),
          ),
          Align(
            alignment: Alignment.bottomRight,
            child: Text(
              formatTimestampToString(document['timestamp']),
              textAlign: TextAlign.left,
              style: TextStyle(
                color: Colors.grey,
                fontSize: 12,
              ),
            ),
          ),
        ],
      ),
    );

Solution

  • This is indeed not as easy, because you want the bubble to have an "undefined" width - it should adapt to the length of the actual message which is not known before actually placing / drawing the widget on runtime. My suggestion would be to make use of a Stack - I also used it inside a ListView.builder which will probably be the desired use case. Make sure to read my comments in the code block as well:

    ListView.builder(
      itemCount: 5,
      itemBuilder: (context, index) => UnconstrainedBox(
        /// Decide whether a message has been received or written by yourself (left or right side)
        alignment:
            index % 2 == 0 ? Alignment.centerLeft : Alignment.centerRight,
        /// ConstrainedBox to make sure chat bubbles don't go from left to right if they are too big - you can change it or even remove it if it's not what you want
        child: ConstrainedBox(
          constraints: BoxConstraints(
              maxWidth: MediaQuery.of(context).size.width / 1.5),
          child: Bubble(
            margin: BubbleEdges.only(top: 5, bottom: 5),
            elevation: 0.6,
            /// Alignment and nip has to be adjusted here too
            alignment:
                index % 2 == 0 ? Alignment.topLeft : Alignment.topRight,
            nip: index % 2 == 0 ? BubbleNip.leftTop : BubbleNip.rightTop,
            radius: Radius.circular(10),
            child: Stack(
              children: [
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('TestUser'),
                    Padding(
                      padding: const EdgeInsets.only(top: 3, bottom: 5),
                      child: Text(
                        'Text ' * Random().nextInt(50),
                        style: TextStyle(
                          fontSize: 16,
                          height: 1.35,
                          letterSpacing: -0.1,
                        ),
                      ),
                    ),
                    /// Adjust the space you want between the actual message and the timestamp here, you could also use a Text widget to use the same height as the actual timestamp - as you like
                    SizedBox(height: 18.0),
                  ],
                ),
                Positioned(
                  bottom: 0,
                  right: 0,
                  child: Text(
                    '08.09.2021',
                    style: TextStyle(
                      color: Colors.grey,
                      fontSize: 12,
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    ),