Исследование флаттера (4) - размер головоломки

Flutter

навигация

предисловие

Недавно, когда писал макет, обнаружил вот такую ​​ошибку:

Vertical viewport was given unbounded height.

Vertical viewport was given unbounded width.

Вероятно, это означает, что высота и ширина представления не ограничены.. Типичный сценарий выглядит следующим образом:

Column(
        children: <Widget>[
          ListView(
            children: <Widget>[
              Container(color:Colors.red, child:Text("1")),
              Container(color:Colors.orange, child:Text("2")),
            ],
          ),
        ],
      )

Если ListView добавлен в подпредставление представления столбца, будет сообщено об указанной выше ошибке:
Vertical viewport was given unbounded height.

анализировать

Для Колонны длина главной оси, то есть высота в вертикальном направлении, определяетсяMainAxisSizeрешенный,MainAxisSizeЕсть два типа, min и max. Давайте перейдем к исходному коду, чтобы увидеть их описания:

Обратите внимание на красную часть.Если ограничение дочернего элемента столбца не ограничено, реальный размер не может быть указан. Для ListView высота по вертикали равна double.infinity, то есть неограниченна, поэтому ограничение ListView не ограничено. Итак, каково решение?Давайте обратимся к исходному коду ListView, чтобы выяснить это.

Поискав несвязанное ключевое слово в исходном коде ListView, я быстро обнаружил, что есть такое свойство сжимающаяWrap:

Есть очень критический комментарий, Если вид прокрутки имеет неограниченные ограничения в [scrollDirection], то [shrinkWrap] должно быть истинным. Если ограничений в направлении прокрутки нет, то для параметра shrinkWrap должно быть установлено значение true, Оглядываясь назад, можно сказать, что внешний слой ListView — это Column, который также не имеет ограничений по высоте, поэтому ограничение Column также не ограничено, поэтому мы следуем подсказке, чтобы установить для свойства slimWrap ListView значение true. Результат налицо, естественно это нормально.
Так зачем устанавливать свойство в true?Продолжайте копаться в исходном коде, и вы можете заметить, что есть такой кусок кода:

    if (shrinkWrap) {
      return ShrinkWrappingViewport(
        axisDirection: axisDirection,
        offset: offset,
        slivers: slivers,
      );
    }
    return Viewport(
      axisDirection: axisDirection,
      offset: offset,
      slivers: slivers,
      cacheExtent: cacheExtent,
      center: center,
      anchor: anchor,
    );

Получается, что разные ViewPorts выбираются по усадкеWrap.

  • Когда shrikWrap истинно:
    После входа во ViewPort обнаруживается, что реальный объект рендеринга — это RenderViewPort, так что идем дальше.
  @override
  RenderViewport createRenderObject(BuildContext context) {
    return (
      axisDirection: axisDirection,
      crossAxisDirection: crossAxisDirection ?? Viewport.getDefaultCrossAxisDirection(context, axisDirection),
      anchor: anchor,
      offset: offset,
      cacheExtent: cacheExtent,
    );
  }

Поскольку это находится в фазе измерения, мы сначала находим PerformResize следующим образом:

Угадайте, что мы увидели, ошибка Вертикальный вьюпорт получил неограниченную высоту, упомянутая в предисловии. Затем посмотрите на код условия суждения:

  bool get hasBoundedHeight => maxHeight < double.infinity;

Так как maxHeight ListView равен double.infinity, естественно возвращается false, что вызывает указанную выше ошибку.

  • Когда shrikWrap истинно:
    можно увидеть б/уRenderShrinkWrappingViewportэтот класс, этот классОфициальное введениеЧасти следующие:
A render object that is bigger on the inside and shrink wraps its children in the main axis.

Уменьшить подпредставление по главной оси. Это должно быть понятно, когда вы это видите. Исходная главная ось ListView, то есть вертикальное направление не ограничено, и RenderShrinkWrappingViewport сжимает основную ось, так что направление главной оси может быть определено, чтобы решить проблему.. вопрос.

Конечно, помимо этого метода, мы также можем решить эту проблему напрямую, вложив макет с определенной высотой в ListView:

Column(
      mainAxisSize: MainAxisSize.min,
    children: <Widget>[
      Container(
      height: 100.0, child: ListView(
            shrinkWrap: false,
            children: <Widget>[
              Container(color:Colors.red, child:Text("1")),
              Container(color:Colors.orange, child:Text("2")),
            ],
          )
        )]
      ))

Так что, по сути, ключом к решению проблемы является определение высоты ListView, будь то усадочная упаковка или контейнер, каждый из которых должен дать конкретную высоту ListView.

Фактически, так много было проанализировано выше, включая базовую концепцию Flutter, то есть Constraint, что означает ограничение. Ограничения Flutter передаются от родительских узлов к дочерним узлам, и дочерние узлы изменяют свой размер в соответствии с ограничениями. Возьмем самый простой пример :

   return Scaffold(
        appBar: AppBar(title: Text("布局测试")),
        backgroundColor: Colors.green,
        body: Container(
            color: Colors.amber,
            child: Column(
              mainAxisSize: MainAxisSize.max,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Container(
                  color: Colors.blue,
                  height: 300.0,
                  child: Row(mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: <Widget>[
                    Container(color: Colors.teal, width: 100, height: 100),
                    Container(color: Colors.purple, width: 100, height: 100),
                  ]),
                ),

                Container(
                  height: 300,
                  width: 200,
                  color: Colors.lime,
                  child: Column(
                    children: <Widget>[
                      Container(color: Colors.purple, width: 100, height: 100),
                      Container(color: Colors.teal, width: 100, height: 100),
                    ],
                  ),
                )
              ],
            )));
  }

Эффект следующий:

Для самого внешнего столбца ограничение его родительского узла — это экран, а его вертикальное направление — максимальное, вы можете видеть, что высота самого внешнего столбца — это высота всего экрана (оранжевая часть), а ограничение внутреннего столбца Это Контейнер высотой 300, поэтому его высота равна 300 (желтая часть). Для Row его условием ограничения также является высота 300. Хотя ограничения в горизонтальном направлении нет, поскольку min используется в горизонтальном направлении, ширина Row соответствует общей ширине дочерних узлов, которая равна 200. Если max используется в горизонтальном направлении, то ширина строки равна ширине экрана.

Суммировать

Если что-то пойдет не так, узнайте причину из исходного кода 😎😆.

Ссылаться на

Ограничения флаттера
Знания о трепетании ограничения

склад

нажмитеflutter_demo, см. полный код.