Думаю, что для всех разработчиков Flutter использование конфигурации shrinkWrap
в компоненте ListView
знакомо. Как показано на следующем рисунке, каждый раз, когда вы сталкиваетесь с подобной ошибкой unbounded
, первым шагом обычно является добавление shrinkWrap: true
к ListView
. Но почему сейчас говорят, что shrinkWrap
вскоре будет отброшен?
Сказать, что его полностью "отбросят", было бы некорректно. По планам команды, shrinkWrap
будет отключен из списка скроллируемых компонентов, так как большинство разработчиков используют это свойство, не понимая его реального значения, а также могут ненамеренно создавать проблемы производительности.
Конечно, данное предложение не означает полное удаление поддержки shrinkWrap
. Вместо этого планируется замена через новый виджет с более подходящим названием, таким как NonLazyListView
.
На данный момент этот запрос имеет приоритет P1, поэтому если все пойдет по плану, он будет реализован довольно быстро.
Тогда, почему shrinkWrap
может вызывать проблемы с производительностью? В каких случаях его следует использовать? Почему его решили повысить до уровня P1?Для начала нам нужно немного разобраться в реализации слайдера в Flutter и роли shrinkWrap
. В статье "Познакомьтесь с реализацией слайдера в Flutter" мы уже рассказывали, что слайдеры в Flutter состоят из трёх частей: Viewport, Scrollable и соответствующих Slivers.
Пример использования ListView
показывает изменения во время прокрутки:
Поэтому ListView
способен бесконечно прокручиваться благодаря тому, что есть фиксированное окно, где только те элементы, которые попадают внутрь или близко к нему, будут отображены и отрисованы, обеспечивая тем самым хорошую производительность списка. Однако это также создаёт проблему, как показано в коде на рисунке 1, из-за свойства Column
, невозможно напрямую вычислить размер Viewport
. Поэтому возникает ошибка.
| |
|
|
| ------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------- |Иногда мы решаем эту проблему, добавив
Expanded
к ListView
, как показано на рисунке 2, чтобы ListView
заполнял оставшееся пространство Column
, что позволяет получить фиксированный размер Viewport
.Однако когда нам требуется, чтобы ListView
не заполнял всё пространство и был центрирован, мы используем метод, показанный на рисунке 3, добавляя shrinkWrap: true
.
Хотя этот пример не имеет практического значения, он демонстрирует "основной" сценарий использования
shrinkWrap
, а также часто используется при вложении одногоListView
внутри другогоListView
, что является неправильным использованием.
Как реализован shrinkWrap
? Вкратце, когда shrinkWrap: true
, внутри скроллирующего компонента используется специальный ShrinkWrappingViewport
для его реализации.
Основное отличие между ShrinkWrappingViewport
и Viewport
заключается в следующем:
Viewport
заполняет весь доступный размер главной оси.ShrinkWrappingViewport
адаптируется к размеру элементов главной оси, а это повышает стоимость такого "сужения", так как размер окна определяется через дочерние элементы.Например, как показано на рисунке ниже, если в ListView
установить itemCount
равным 400, то благодаря действию shrinkWrap
можно заметить вывод всех 400 дочерних элементов.
Аналогично, в дереве виджетов Inspectors можно увидеть, что все 400 дочерних элементов были созданы, даже если они ещё не отображены в Viewport
. Таким образом, shrinkWrap
делает ListView
неэффективным для ленивой загрузки.С другой стороны, как показано на рисунке ниже, если удалить shrinkWrap
, то благодаря Expanded
ListView
получает фиксированный размер Viewport
. В этом случае, даже если itemCount
равно 400, будут созданы только необходимые 19 дочерних элементов. Даже если изменения происходят при прокрутке, обычный ListView
обычно имеет «фиксированную» длину, например, когда вы прокручиваете до индекса 160, начальный индекс ListTitle
будет равен 135, а не будет таким же, как в случае с shrinkWrap
, где все дочерние элементы остаются построенными.
Если углубиться в детали, одним из ключевых моментов является различие в реализации метода updateOutOfBandData
, в обычном Viewport
этот метод используется только для расчета maxScrollExtent
, тогда как в ShrinkWrappingViewport
он суммирует maxPaintExtent
каждого дочернего элемента, как показано на рисунке 1.
![]() |
![]() |
---|
После суммирования получается значение _shrinkWrapExtent
, которое затем преобразуется в размер ShrinkWrappingViewport
. Это также объясняет, почему ShrinkWrappingViewport
может изменять размер своего «окна» в зависимости от своих дочерних элементов.Поэтому ранее разработчики часто использовали простое свойство shrinkWrap
, чтобы решать проблемы, но мало кто задумывался над его принципами работы или пониманием его функциональности, что иногда приводило к скрытым производительным проблемам. Вот почему была выполнена эта доработка:> Перенос shrinkWrap
в новый компонент позволит более наглядно понять его функцию, а большинство случаев использования shrinkWrap
можно заменить другими реализациями.
Например, в случае вложенного ListView
, вместо того чтобы использовать shrinkWrap
, лучше использовать CustomScrollView
вместе с различными SliverList
или другими Sliver
.
Если количество дочерних элементов невелико, можно просто использовать SingleChildScrollView
с Column
, что обеспечивает аналогичное поведение, как и shrinkWrap
.
Таким образом, теперь вы знаете логику реализации и функцию shrinkWrap
. Основной целью этого нового изменения было помочь вам переоценить shrinkWrap
. С этого момента shrinkWrap
больше не будет называться таковым, и вы скорее всего будете реже им пользоваться.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )