Hola, Amigos! На связи Ярослав Цемко, Flutter dev продуктового агентства Amiga. Я участвую в разработке мобильного приложения Bravo — e-com проект по продаже карнавальной продукции, воздушных шаров и товаров для праздника. Это одна из крупнейших в России и в мире компаний в данной отрасли.
Сегодня расскажу про решение, которое делает анимацию загрузки картинок вместо пустого экрана или троббера (вращающееся колечко).
Но вначале хочу поделиться с вами телеграм-каналом Flutter. Много, где мы с командой рассказываем интересные случаи из практики и обсуждаем с другими мобильными разработчиками свой опыт. Нас 1750, присоединяйтесь)
![](https://habrastorage.org/getpro/habr/upload_files/ebf/125/58e/ebf12558edb8f401f6ac1df8cb47c1d1.png)
Мы решили внедрить 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)
Rusrst
16.11.2023 18:23Я все понимаю, но шиммер можно уже в 23 году и самостоятельно написать и воткнуть в любом виде - примеров для view (drawable) и compose уже больше одного, бери и переноси - не хочу. О чём статья то?
PackRuble
Стойкое ощущение костыля... А чем пакет shimmer плох? (ладно, такой популярный пакет и с НЕудалённым примером счётчика прямо в lib... ужос ..., кажется иногда и своё решение не так уж и плохо, чем тащить невесть какие пакеты в проект)