I used tuple but you can avoid it.

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;
}