skip to Main Content

I want to create a widget (blue background)as the picture depict:

  1. If the text count is not too much ( will not exceed the screen edge)
    , it will layout as a Row widget
  2. If the text count is too much ,all the text content is too long ,
    and it will exceed the screen edge if all the text are lay out. So
    it will only layout the Text1 and Text2 ,and add a "…" at last ,
    ignore the other text.

enter image description here

How to achieve this in Flutter ?(the left and right widget’s size is not fixed)

2

Answers


  1. For Text Widgets you can use TextSpan wrapped inside a RichText:

    Row(
          children: [
            Container(
              decoration: BoxDecoration(
                color: Colors.white,
                border: Border.all(color: Colors.black),
                shape: BoxShape.rectangle,
                borderRadius: BorderRadius.circular(10),
              ),
              width: 200,
              height: 200,
            ),
                      Flexible(child: 
                               Padding(
                        padding: const EdgeInsets.symmetric(horizontal: 8.0),
                        child: 
                               RichText(
                        overflow: TextOverflow.ellipsis,
                        text: const TextSpan(
                          children: [
                            TextSpan(text: "Text 1 "),
                            TextSpan(text: "Text 2 "),
                            TextSpan(text: "Text 3 "),
                            TextSpan(text: "Text 4 "),
                            TextSpan(text: "Text 5 "),
                            TextSpan(text: "Text 6 "),
                            TextSpan(text: "Text 7 "),
                            TextSpan(text: "Text 8 "),
                          ],
                          style: TextStyle(color: Colors.white, fontSize: 20,),
                        ),
                      ),
                                 ),
                               ),
            Container(
              decoration: BoxDecoration(
                color: Colors.white,
                border: Border.all(color: Colors.black),
                shape: BoxShape.rectangle,
                borderRadius: BorderRadius.circular(10),
              ),
              width: 200,
              height: 200,
            ),
          ],
        );
    

    If the widget are mixed, you can wrap the widgets inside a row with Flexible.

    Results:
    sample

    Login or Signup to reply.
  2. I used tuple but you can avoid it.

    enter image description here

    import 'package:flutter/material.dart';
    import 'package:tuple/tuple.dart';
    
    void main() {
      runApp(
        const MaterialApp(
          home: App(),
        ),
      );
    }
    
    class App extends StatelessWidget {
      const App({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Padding(
            padding: const EdgeInsets.all(5),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: const [
                Text('1st content will not exceed the edge'),
                SizedBox(height: 5),
                Example(
                  list: ['text1', 'long text2'],
                ),
                SizedBox(height: 10),
                Text('2st content will not exceed the edge'),
                SizedBox(height: 5),
                Example(
                  list: ['text1', 'long text2', 'text3', 'text4', 'text3'],
                ),
              ],
            ),
          ),
        );
      }
    }
    
    class Example extends StatelessWidget {
      const Example({
        super.key,
        required this.list,
      });
    
      final List<String> list;
    
      @override
      Widget build(BuildContext context) {
        const right = _SideWidget(title: 'Right');
        const left = _SideWidget(title: 'Left');
    
        final leftSize = measureWidget(left);
        final rightSize = measureWidget(right);
        return LayoutBuilder(builder: (context, constraints) {
          var remainingWidth =
              constraints.maxWidth - rightSize.width - leftSize.width;
          return Row(
            mainAxisSize: MainAxisSize.min,
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              left,
              CalculatedWidget(textList: list, maxWidth: remainingWidth),
              right,
            ],
          );
        });
      }
    }
    
    class CalculatedWidget extends StatelessWidget {
      const CalculatedWidget({
        super.key,
        required this.textList,
        required this.maxWidth,
      });
    
      final List<String> textList;
      final double maxWidth;
    
      @override
      Widget build(BuildContext context) {
        const paddingSize = 5.0;
        const separaterSize = 5.0;
        final othersWidget = getWidgetAndSize('...');
    
        final childrenData = <Tuple2<Widget, double>>[];
        final List<Widget> children;
        late var remainTextWidgetsWidth = maxWidth;
        var isShortened = false;
    
        for (final text in textList) {
          final textWidget = getWidgetAndSize(text);
    
          if (remainTextWidgetsWidth < textWidget.item2 + separaterSize) {
            isShortened = true;
            break;
          }
          remainTextWidgetsWidth -= (textWidget.item2 + separaterSize);
          childrenData.add(textWidget);
        }
    
        if (isShortened) {
          children = [];
          remainTextWidgetsWidth = maxWidth - othersWidget.item2 - separaterSize;
          var it = childrenData.iterator;
    
          bool isStoped = false;
    
          while (it.moveNext() && !isStoped) {
            final isWidthEnougth =
                remainTextWidgetsWidth > it.current.item2 + separaterSize;
            if (isWidthEnougth) {
              children.add(Padding(
                padding: const EdgeInsets.only(right: separaterSize),
                child: it.current.item1,
              ));
    
              remainTextWidgetsWidth -= (it.current.item2 + separaterSize);
            } else {
              isStoped = true;
            }
          }
        } else {
          children = childrenData
              .map((e) => Padding(
                    padding: const EdgeInsets.only(right: separaterSize),
                    child: e.item1,
                  ))
              .toList();
        }
    
        return Container(
          color: Colors.blue.shade300,
          padding:
              const EdgeInsets.fromLTRB(paddingSize, paddingSize, 0, paddingSize),
          child: Row(
            children: [
              ...children,
              if (isShortened) ...[
                othersWidget.item1,
                const SizedBox(width: paddingSize)
              ],
            ],
          ),
        );
      }
    
      Tuple2<Widget, double> getWidgetAndSize(String text) {
        final widget = Container(
          color: Colors.red,
          alignment: Alignment.center,
          padding: const EdgeInsets.all(5),
          constraints: const BoxConstraints(
            minWidth: 50,
          ),
          child: Text(text),
        );
        final size = measureWidget(widget);
        return Tuple2(widget, size.width);
      }
    }
    
    class _SideWidget extends StatelessWidget {
      const _SideWidget({
        required this.title,
      });
    
      final String title;
    
      @override
      Widget build(BuildContext context) {
        return Container(
          padding: const EdgeInsets.all(10),
          margin: const EdgeInsets.all(5),
          color: Colors.green,
          child: Text(title),
        );
      }
    }
    

    the measure widget method from this answer

    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    
    measureWidget(Widget widget,
        [BoxConstraints constraints = const BoxConstraints()]) {
      final PipelineOwner pipelineOwner = PipelineOwner();
      final _MeasurementView rootView =
          pipelineOwner.rootNode = _MeasurementView(constraints);
      final BuildOwner buildOwner = BuildOwner(focusManager: FocusManager());
      final RenderObjectToWidgetElement<RenderBox> element =
          RenderObjectToWidgetAdapter<RenderBox>(
        container: rootView,
        debugShortDescription: '[root]',
        child: Directionality(
          textDirection: TextDirection.ltr,
          child: widget,
        ),
      ).attachToRenderTree(buildOwner);
      try {
        rootView.scheduleInitialLayout();
        pipelineOwner.flushLayout();
        return rootView.size;
      } finally {
        // Clean up.
        element.update(RenderObjectToWidgetAdapter<RenderBox>(container: rootView));
        buildOwner.finalizeTree();
      }
    }
    
    class _MeasurementView extends RenderBox
        with RenderObjectWithChildMixin<RenderBox> {
      final BoxConstraints boxConstraints;
      _MeasurementView(this.boxConstraints);
    
      @override
      void performLayout() {
        assert(child != null);
        child!.layout(boxConstraints, parentUsesSize: true);
        size = child!.size;
      }
    
      @override
      void debugAssertDoesMeetConstraints() => true;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search