我想创建一个小部件(蓝色背景),如图片所示:

  1. 如果文字数量不是太多(不会超过屏幕边缘) ,它将布局为Row小部件
  2. 如果文本数太多,则所有文本内容都太长, 如果所有文本都布局,则会超出屏幕边缘.所以 它将只对Text1和Text2进行布局,并添加"..."最后, 忽略其他文本.

enter image description here

如何在Fighting中做到这一点?(the left and right widget's size is not fixed)

推荐答案

我用了tuple元,但你可以避免.

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) {
        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),
    );
  }
}

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

Flutter相关问答推荐

出现异常.RangeError(RangeError(index):无效值:有效值范围为空:0).分度误差

点击表情符号按钮后,Flutter 文本域文本编辑控制器将文本恢复为原始值

分页控制器在到达页面末尾之前或在初始化页面时对每个索引调用API

如何使排内 children 的身高与其他排内 children 的身高相适应?

try 使用fl_Chart of Ffltter,但在导入它时遇到以下错误:Error:';TextScaler';is;t a type

如何对齐 AppBar Actions 中的文本?

谷歌 map 在 flutter 应用程序中显示错误的当前位置

了解多Provider 和流

如何使用 Material3 创建带有高度的完美白色提升按钮?

Flutter:如何设置 PersistentFooterButtons 背景?

如何在Flutter中的textField中在可编辑文本和前缀图标之间添加空格?

升级到 Flutter 3.10 后,Flutter 键盘填充不起作用. Flutter 3.10 如何防止 BottomSheet 被键盘覆盖?

Flutter:如何从运行时创建的列表中访问单个子部件的值

Flutter canvas 绘制不同 colored颜色 的原始点

如何使用 riverpod_annotation 创建非自动处置提供程序?

flutter 中 // 和 /// 有什么区别

如何验证非表单字段?

Android Studio 模拟器屏幕闪烁

将容器扩展到安全区域 - Flutter

阴影效果只出现在一张卡片的左边 在Flutter 中?