Scott
Scott

Reputation: 25

In Flutter, display a single word in a long text to the side of the text

Using Flutter, I have a long text that has citations inline, that I would like displayed to the side of the text, but kept on the same line where the original citation was, even when the text is resized. As well as avoiding line breaks. For example the original text might be:

Lorem ipsum dolor sit amet, consectetur *132a* adipiscing elit, sed do eiusmod 
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 
nostrud exercitation ullamco laboris nisi ut *133c* aliquip ex ea commodo consequat. Duis 

And I would like it displayed as:

132a Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
     tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 
133c quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure

While allowing the citations to stay inline with the original location when the text is resized. And avoiding line breaks.

I've tried using the RichText widget, using TextSpan for the text and WidgetSpan for the citations and with other widgets such as Stack, Position and OverflowBox

Center(
    child: Column(
    mainAxisAlignment: MainAxisAlignment.start,
        children: <Widget>[
        RichText(
           text: TextSpan(
             children: [
               TextSpan(text: textA),
               WidgetSpan(child: Text('132a')), // I've tried many widgets here
               TextSpan(text: textB),
            ])),
          ],
        ),
      ),

With CSS I could use float: left to get a similar effect, but I've been unable to figure out a way to do this with the Flutter UI.

Upvotes: 1

Views: 1115

Answers (3)

ronb
ronb

Reputation: 261

I'm a little late with an answer, but it is possible to layout your data like you want, allowing citations to stay inline with the original location when the text is resized, for example: enter image description here

Here's what the code would look like:

import 'package:float_column/float_column.dart';

class InlineFloats extends StatelessWidget {
  const InlineFloats({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return DefaultTextStyle(
      style: const TextStyle(fontSize: 18, color: Colors.black, height: 1.5),
      child: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.all(8),
          child: FloatColumn(
            children: [
              WrappableText(
                text: _process(_str),
                margin: const EdgeInsetsDirectional.only(start: 60),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

TextSpan _process(String str) {
  return TextSpan(
    children: str.split(' *').expand(
      (str) {
        final s = str.split('* ');
        if (s.length == 2) {
          final cit = s.first;
          return [
            TextSpan(text: ' (${cit.substring(cit.length - 1)})'),
            WidgetSpan(
              child: Floatable(
                  float: FCFloat.left, clear: FCClear.both, child: Text(cit)),
            ),
            TextSpan(text: ' ${s.last}'),
          ];
        } else {
          return [TextSpan(text: str)];
        }
      },
    ).toList(),
  );
}

// cspell: disable
const _str = '''
Lorem ipsum dolor sit amet, consectetur *132a* adipiscing elit, sed do eiusmod...

It uses a new flutter package I recently wrote called float_column, which provides a new FloatColumn widget where text can wrap around floated inline widgets, similar to how CSS float works.

You can try it out here: https://ronjb.github.io/float_column, and in it, switch to the 'Inline' tab to see an example similar to your question.

Upvotes: 1

ambiguous58
ambiguous58

Reputation: 1411

Here is the full example on dartpad

To make the problem easier, I would definitely convert the text (using regex, especially if the text is long) into a map.

Your map should look like this:

final Map<String, String> myMap = {
  '132a':
      "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type",
  '132b':
      "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type",
  '132c':
      "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type",
  '132d':
      "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type",
};

Then,

return ListView.builder(
  itemCount: myMap.entries.length,
  itemBuilder: (context, index) {
    return Padding(
      padding: EdgeInsets.all(8.0),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(myMap.keys.elementAt(index)),
          SizedBox(width: 20),
          Expanded(
            child: Text(
              myMap.values.elementAt(index),
            ),
          ),
        ],
      ),
    );
  },
);

Upvotes: 0

Ignacior
Ignacior

Reputation: 957

My solution, but with Rows and Expanded:

Center(
  child: Column(
    mainAxisAlignment: MainAxisAlignment.start,
    children: <Widget>[
      Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            '132a',
          ),
          Expanded(child: Text(textA, overflow: TextOverflow.clip))
        ],
      ),
      Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('133c'),
          Expanded(child: Text(textB, overflow: TextOverflow.clip)),
        ],
      ),
    ],
  ),
),

enter image description here

Upvotes: 1

Related Questions