Hola, Amigos! На связи Ярослав Цемко, Flutter dev продуктового агентства Amiga. Я участвую в разработке мобильного приложения Bravo — e-com проект по продаже карнавальной продукции, воздушных шаров и товаров для праздника. Это одна из крупнейших в России и в мире компаний в данной отрасли. 

Сегодня расскажу про решение, которое делает анимацию загрузки картинок вместо пустого экрана или троббера (вращающееся колечко).

Но вначале хочу поделиться с вами телеграм-каналом Flutter. Много, где мы с командой рассказываем интересные случаи из практики и обсуждаем с другими мобильными разработчиками свой опыт. Нас 1750, присоединяйтесь)

Мы решили внедрить shimmer. Это графический элемент, который отображает силуэт загружающегося элемента, чтобы экран не был пустым во время загрузки. 

На docs.flutter.dev есть пример реализации.

В данном случае виджет можно использовать таким образом:

Для начала необходимо обернуть место, где мы собираемся использовать шиммер виджетом Shimmer, поскольку он отвечает за что, чтобы во всех дочерних шиммерах одна полоска проходила по всем виджетам. 

У него есть параметр shimmerGradient в котором мы задаем цвет градиента, например:

LinearGradient shimmerGradient = LinearGradient(
 colors: [
   Color(0xFFEBEBF4),
   Color(0xFFF4F4F4),
   Color(0xFFEBEBF4),
 ],
 stops: [
   0.1,
   0.3,
   0.4,
 ],
 begin: Alignment(-1.0, -0.3),
 end: Alignment(1.0, 0.3),
 tileMode: TileMode.clamp,
);

Затем можно использовать на каждом элементе, где нам нужно использовать shimmer c помощью ShimmerLoading. 

У него есть параметр isLoading, который отвечает за отображение шиммера, и используется как флаг идет загрузка или нет. Так можно управлять отображением самого шиммера.

Существует также плагин cached_network_image.

Его можно использовать в связке с плагином cached_network_image, но не все так просто.

Если у нас cached_network_image, то отследить загрузку становится затруднительно, потому что у этого виджета нет параметра, где мы могли бы проверить статус загрузки. В данном конкретном случае это не подойдет, однако у cached_network_image есть параметр placeholder, который отображает заданный в этом параметре виджет, пока загружается изображение. Мы можем туда передать виджет ShimmerLoading, с параметром isLoading равном true.

return CachedNetworkImage(
 imageUrl: widget.imageUrl,
 placeholder: (context, url) {
   return ShimmerLoading(
       isLoading: true,
       child: Container(
         decoration: const BoxDecoration(
           color: Colors.white,
         ),
       ));
 },
);

Однако при запуске без изменений у нас выскакивает следующая ошибка

The following _TypeError was thrown building ShimmerLoading(dirty, state: _ShimmerLoadingState#b0084):

Null check operator used on a null value

if (!widget.isLoading) {
 return widget.child;
}
// Вот здесь выскакивает ошибка.
final offsetWithinShimmer = shimmer.getDescendantOffset(
 descendant: context.findRenderObject() as RenderBox,
);

Для того, чтобы все заработало, нужно добавить проверку на наличие виджета и renderObject в контексте

if (!widget.isLoading
   || context.findRenderObject() == null
   || Shimmer.of(context) == null) {
 return widget.child;
}

И тогда все начинает работать поскольку в случае если renderObject не находится, то просто возвращается дочерний виджет. Проблема в cached_network_image заключается в том, что в момент, когда изображение загружается, то когда происходит замена с placeholder на изображение, которое нужно загрузить, пропадает на секунду этот самый RenderObject и это вызывает ошибку. Тем не менее с помощью этого небольшого исправления все начинает работать.

Пример можно посмотреть по ссылке.

На этом всё! Ждем вас во Flutter. Много, присоединяйтесь к нашему сообществу мобильных разработчиков.

Комментарии (2)


  1. PackRuble
    16.11.2023 18:23

    Стойкое ощущение костыля... А чем пакет shimmer плох? (ладно, такой популярный пакет и с НЕудалённым примером счётчика прямо в lib... ужос ..., кажется иногда и своё решение не так уж и плохо, чем тащить невесть какие пакеты в проект)


  1. Rusrst
    16.11.2023 18:23

    Я все понимаю, но шиммер можно уже в 23 году и самостоятельно написать и воткнуть в любом виде - примеров для view (drawable) и compose уже больше одного, бери и переноси - не хочу. О чём статья то?