skip to Main Content

In Flutter, I want to insert a widget (an icon) at the end of a text block, but I want to ensure that the widget stays inline with the last word of the text and doesn’t wrap to a new line by itself. If the text is super long, the text should be cut off. Otherwise words can be split.

Text.rich(
  TextSpan(
    children: [
      TextSpan(
        text: title,
        style: textStyle,
      ),
      WidgetSpan(
        alignment: PlaceholderAlignment.middle,
        child: Padding(
          padding: const EdgeInsets.only(left: 6.0),
          child: icon,
        ),
      ),
    ],
  ),
)

I’ve tried putting the icon in a row with the last character but then it doesn’t move the last word itself onto the next line in some cases.

The first two images are as expected.
The last image here shows what I’m trying to avoid:

This is correct

This is correct

Here the image is on a new line without any words to the left

3

Answers


  1. Well, I believe it’s not possible to directly achieve this without manipulating the "String" that you want to place next to the icon. This is because spaces between words break once there’s no more space in the line.

    However, I came up with a sort of "workaround" that might help. I created two functions to manipulate the provided text: _onlyLastWords and _withoutLastWords. I made it so that if you want to increase or decrease the number of words, you only need to change the value of words.

    String _onlyLastWords(String text, {int words = 2}) {
      List<String> wordsOfText = text.split(" ");
    
      return " ${wordsOfText.sublist(wordsOfText.length - words).join(" ")}";
    }
    
    String _withoutLastWords(String text, {int words = 2}) {
      List<String> wordsOfText = text.split(" ");
      return wordsOfText.sublist(0, wordsOfText.length - words).join(" ");
    }
    

    With this, I structured it like this:

    class MyHomePage extends StatelessWidget {
      const MyHomePage({super.key});
    
      String _onlyLastWords(String text, {int words = 2}) {
        List<String> wordsOfText = text.split(" ");
    
        return " ${wordsOfText.sublist(wordsOfText.length - words).join(" ")}";
      }
    
      String _withoutLastWords(String text, {int words = 2}) {
        List<String> wordsOfText = text.split(" ");
        return wordsOfText.sublist(0, wordsOfText.length - words).join(" ");
      }
    
      @override
      Widget build(BuildContext context) {
        String text = "Lorem ipsum dolor sit amet, elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
        return Scaffold(
          backgroundColor: Colors.yellow,
          body: Center(
            child: SizedBox(
              width: 900,
              child: Text.rich(
                TextSpan(
                  children: [
                    TextSpan(
                      text: _withoutLastWords(text),
                    ),
                    WidgetSpan(
                      alignment: PlaceholderAlignment.middle,
                      child: Row(
                        mainAxisSize: MainAxisSize.min,
                        children: [
                          Text(_onlyLastWords(text)),
                          const Padding(
                            padding: EdgeInsets.only(left: 6.0),
                            child: Icon(Icons.import_contacts),
                          )
                        ],
                      ),
                    )
                  ],
                ),
              ),
            ),
          ),
        );
      }
    }
    

    The idea is to create an element with the last two words plus the icon, so that when there’s no space, there are always at least two words before the icon.

    result

    result2

    Login or Signup to reply.
  2. A simpler way would be to use Unicode like this –new Text(‘2018 u00a9 Author’s Name’),

    Login or Signup to reply.
  3. You can extract the last word and place it in the Row with the icon inside the WidgetSpan. In the code below, I have place the word hello with the icon ABC inside the Row and placed the Row inside the WidgetSpan.

    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return const MaterialApp(
          debugShowCheckedModeBanner: false,
          home: Scaffold(
            body: Center(
              child: Text.rich(
                TextSpan(
                  children: [
                    TextSpan(
                      text: 'My icon is always connectedd with hello.',
                    ),
                    WidgetSpan(
                      alignment: PlaceholderAlignment.middle,
                      child: Padding(
                        padding: EdgeInsets.only(left: 6),
                        child: Row(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Text('hello'),
                            Icon(Icons.abc),
                          ],
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
        );
      }
    }
    

    enter image description here

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