Hola, Amigos! Наш Flutter Team Lead Павел Гершевич примет участие в круглом столе на FlutterConf 🙂
Запускаешь новый проект — и снова тот же вопрос: какой state manager выбрать? Проверенный временем? Популярный в комьюнити? Или тот, который сейчас чаще всего советуют?
На FlutterConf вынесли тему на открытое обсуждение с аргументами и практическим опытом. Обсудим реальные кейсы, плюсы и минусы подходов, гибкость Flutter и попробуют понять, возможен ли тот самый «золотой стандарт».
В обсуждении участвуют:
— Павел Гершевич, Team Lead Flutter Amiga
— Анна Жаркова, руководитель группы разработки Usetech
— Олег Скирюк, Frontend Team Lead билайн
— Станислав Чернышев, доцент СПбГУАП, автор книги «Основы Dart»
— Николай Омётов, руководитель Flutter-отдела Mad Brains
— Андрей Смирнов, Dart Dependant Overthinking Specialist Яндекс
— Федор Благодырь, разработчик Яндекс
⚙️ Билеты уже в продаже. Круглый стол «Я твой State Manager труба шатал»
⚙️ Москва, 3-я ул. Ямского Поля, 26А
⚙️ 27 февраля, 18:05–19:20
⚙️ Зал 1
А с промокодом FLUTTER_MNOGO вас ждет скидка 20%
Запускаешь новый проект — и снова тот же вопрос: какой state manager выбрать? Проверенный временем? Популярный в комьюнити? Или тот, который сейчас чаще всего советуют?
На FlutterConf вынесли тему на открытое обсуждение с аргументами и практическим опытом. Обсудим реальные кейсы, плюсы и минусы подходов, гибкость Flutter и попробуют понять, возможен ли тот самый «золотой стандарт».
В обсуждении участвуют:
— Павел Гершевич, Team Lead Flutter Amiga
— Анна Жаркова, руководитель группы разработки Usetech
— Олег Скирюк, Frontend Team Lead билайн
— Станислав Чернышев, доцент СПбГУАП, автор книги «Основы Dart»
— Николай Омётов, руководитель Flutter-отдела Mad Brains
— Андрей Смирнов, Dart Dependant Overthinking Specialist Яндекс
— Федор Благодырь, разработчик Яндекс
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6🔥4🥰4😍1
Hola, Amigos! Сегодня обсудим pattern matching. Он сокращает boilerplate, улучшает код и повышает безопасность работы со state.
1. Деструктуризация с помощью паттернов
Пример с Records:
2. Деструктуризация объектов
Есть класс:
Можно извлечь свойства так:
Это эквивалентно:
3. Pattern Matching в switch
Традиционный подход:
Подход с pattern matching
4. Сопоставление коллекций
Паттерны умеют анализировать списки и map:
5. Guard-условия
А вы используете pattern matching?
1. Деструктуризация с помощью паттернов
Пример с Records:
var user = ('Naman', 29);
var (name, age) = user;
print(name);
print(age);
2. Деструктуризация объектов
Есть класс:
class User {
final String name;
final int age;
User(this.name, this.age);
}
Можно извлечь свойства так:
var User(:name, :age) = user;
Это эквивалентно:
var name = user.name;
var age = user.age;
3. Pattern Matching в switch
Традиционный подход:
if (state is Success) {
final data = state.data;
}
Подход с pattern matching
switch (state) {
case Success(data: var data):
render(data);
case Loading():
showLoader();
case Error(message: var msg):
showError(msg);
}
4. Сопоставление коллекций
Паттерны умеют анализировать списки и map:
switch(list) {
case []:
print("Empty");
case [first, second]:
print(first);
}
switch(json) {
case {'status': 'ok', 'data': var d}:
process(d);
case {'status': 'error', 'message': var m}:
showError(m);
}
5. Guard-условия
switch(user) {
case User(age: var age) when age > 18:
print("Adult");
case _:
print("Minor");
}
А вы используете pattern matching?
🔥14👍11❤6💯1
Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Сегодня мы поговорим про то, как лучше всего кешировать картинки и в каком качестве их выводить на экран.
Часто случается ситуация, что сервер отдает слишком большую картинку. А уж если их несколько на одном экране - могут возникнуть проблемы с производительностью, да и размер кеша может значительно вырасти.
Как и все, для кеширования и отображения картинок с интернета, мы используем хорошо знакомую всем библиотеку
Давайте разберемся с ними:
Теперь давайте рассмотрим, как их использовать. Для того, чтобы правильно подобрать размер, нам нужны: ширина и высота виджета и Device Pixel Ratio - характеристика устройства, которая показывает плотность пикселей. Если размер нам неизвестен либо высчитывается автоматически, то можно прибегнуть к получению
Делитесь в комментариях, пробовали ли вы такой способ оптимизации производительности приложения?
Часто случается ситуация, что сервер отдает слишком большую картинку. А уж если их несколько на одном экране - могут возникнуть проблемы с производительностью, да и размер кеша может значительно вырасти.
Как и все, для кеширования и отображения картинок с интернета, мы используем хорошо знакомую всем библиотеку
cached_network_image. Но не все знают, что при помощи нее можно настроить максимальный размер изображения, который будет закеширован и показан на экране. Для этого есть 4 свойства:
maxHeightDiskCache: 300,
maxWidthDiskCache: 300,
memCacheHeight: 300,
memCacheWidth: 300,
Давайте разберемся с ними:
maxHeightDiskCache и maxWidthDiskCache отвечают за кеш, который хранится в постоянной памяти. Уменьшая его размер, мы уменьшаем количество данных на устройстве пользователя. memCacheHeight и memCacheWidth отвечают за изображение, которое хранится в ОЗУ и выводится пользователю. Делается это при помощи встроенного в Flutter виджета - ResizeImage. Похожие свойства есть и в Image.network - cacheHeight и cacheWidth. Тут мы указываем именно размер виджета, а не изображения.Теперь давайте рассмотрим, как их использовать. Для того, чтобы правильно подобрать размер, нам нужны: ширина и высота виджета и Device Pixel Ratio - характеристика устройства, которая показывает плотность пикселей. Если размер нам неизвестен либо высчитывается автоматически, то можно прибегнуть к получению
constraints через LayoutBuilder. В итоге у нас получается такой код:
LayoutBuilder((context, constraints) {
final devicePixelRatio = MediaQuery.devicePixelRatioOf(context);
final cacheHeight = constraints.maxHeight * devicePixelRatio;
final cacheWidth = constraints.maxWidth * devicePixelRatio;
return CachedNetworkImage(
…
maxHeightDiskCache: cacheHeight,
maxWidthDiskCache: cacheWidth,
memCacheHeight: constraints.maxHeight,
memCacheWidth: constraints.maxWidth,
…
);
});
Делитесь в комментариях, пробовали ли вы такой способ оптимизации производительности приложения?
❤7🔥6👍4
Hola, Amigos! Сегодня мы посмотрим, каким образом мы можем грузить и показывать данные пользователям при помощи пагинации.
Пагинация - способ, который используется для разделения больших данных на небольшие группы (страницы) вместо загрузки всего сразу.
Например, для API в 10000 объектов, вместо огромного массива данных, мы будем запрашивать потихоньку: страница 1 (объекты 1-10), страница 2 (объекты 11-20) и т. д.
Это помогает нам:
• Уменьшить нагрузку на сеть. Меньше данных в запросе - выше скорость его выполнения на сервере и быстрее передача ответа;
• Оптимизировать производительность. Меньше объем данных - меньше занимаемой оперативной памяти, быстрее обработка;
• Улучшить UX. Пользователи будут видеть новые данные практически, а не когда мы получим и распарсим все объекты.
Для того, чтобы правильно сделать пагинацию в мобильном приложении, нужно знать, как она делается на сервере. Тут существует 3 способа:
Page-based (стандартный способ)
Тут мы запрашиваем данные, передавая
Offset-based (гибкий способ)
Тут запрос осуществляется с указанием сколько элементов нужно пропустить -
Cursor-based (профессиональный способ)
Самый расширяемый метод. Мы запрашиваем через курсор (обычно ID или timestamp последнего полученного объекта) и получаем данные, которые были добавлены в БД после тех данных, которые мы уже знаем.
Последний способ применяется редко, так как он может не подойти под некоторые случаи. Но у первых двух тоже есть недостаток - может возникнуть дублирование или пропажа данных.
С тем, как запрашивать данные, мы определились, теперь нужно понять, какая страница последняя? Тут есть 2 варианта - либо API вернет нам номер последней страницы при запросе, либо на последней странице придет число меньше указанного нами лимита.
Теперь поработаем с нашим State Manager. Нам нужны состояния - загрузки, успеха и ошибки. И обязательно хранить в них - список полученных данных, страницу (если применяем page-based или offset-based) и при возможности номер последней страницы. Кто-то делает отдельное состояние для загрузки новой страницы, но можно обойтись и без него. И нам нужно 2 функции - для инициализации экрана, чтобы подгрузить первые данные, и для подгрузки во время пагинации. Для BLoC советуем отбрасывать событие подгрузки, если мы находимся в состоянии загрузки.
В следующей части расскажем, как привязать это все к UI. А вы делитесь в комментариях, какой способ пагинации чаще всего используете на своих проектах?
Пагинация - способ, который используется для разделения больших данных на небольшие группы (страницы) вместо загрузки всего сразу.
Например, для API в 10000 объектов, вместо огромного массива данных, мы будем запрашивать потихоньку: страница 1 (объекты 1-10), страница 2 (объекты 11-20) и т. д.
Это помогает нам:
• Уменьшить нагрузку на сеть. Меньше данных в запросе - выше скорость его выполнения на сервере и быстрее передача ответа;
• Оптимизировать производительность. Меньше объем данных - меньше занимаемой оперативной памяти, быстрее обработка;
• Улучшить UX. Пользователи будут видеть новые данные практически, а не когда мы получим и распарсим все объекты.
Для того, чтобы правильно сделать пагинацию в мобильном приложении, нужно знать, как она делается на сервере. Тут существует 3 способа:
Page-based (стандартный способ)
Тут мы запрашиваем данные, передавая
page, страницу, и limit - количество элементов. Запрос будет выглядеть примерно так:
GET /posts?page=1&limit=10
Offset-based (гибкий способ)
Тут запрос осуществляется с указанием сколько элементов нужно пропустить -
offset.
GET /posts?offset=20&limit=10
Cursor-based (профессиональный способ)
Самый расширяемый метод. Мы запрашиваем через курсор (обычно ID или timestamp последнего полученного объекта) и получаем данные, которые были добавлены в БД после тех данных, которые мы уже знаем.
GET /posts?cursor=50&limit=10
Последний способ применяется редко, так как он может не подойти под некоторые случаи. Но у первых двух тоже есть недостаток - может возникнуть дублирование или пропажа данных.
С тем, как запрашивать данные, мы определились, теперь нужно понять, какая страница последняя? Тут есть 2 варианта - либо API вернет нам номер последней страницы при запросе, либо на последней странице придет число меньше указанного нами лимита.
Теперь поработаем с нашим State Manager. Нам нужны состояния - загрузки, успеха и ошибки. И обязательно хранить в них - список полученных данных, страницу (если применяем page-based или offset-based) и при возможности номер последней страницы. Кто-то делает отдельное состояние для загрузки новой страницы, но можно обойтись и без него. И нам нужно 2 функции - для инициализации экрана, чтобы подгрузить первые данные, и для подгрузки во время пагинации. Для BLoC советуем отбрасывать событие подгрузки, если мы находимся в состоянии загрузки.
В следующей части расскажем, как привязать это все к UI. А вы делитесь в комментариях, какой способ пагинации чаще всего используете на своих проектах?
👍9❤4🔥4🥰1
Hola, Amigos! Продолжаем разбираться с пагинацией. Сегодня рассмотрим, как подключить логику работы к пользовательскому интерфейсу.
Для того, чтобы мы могли вызывать подгрузку новой страницы, нам нужен триггер. В зависимости от функционала можно рассмотреть как навигацию по страницам, в таком случае необходимо будет сбрасывать предыдущие данные, так и реализацию бесконечного списка, который мы и разберем подробнее.
В бесконечном списке страницы грузятся в момент, когда пользователь практически доходит до конца. Для этого нам понадобится
Тут мы получаем текущее и максимальное положение скролла и сравниваем их. Совет - не нужно дожидаться, пока пользователь дойдет до самого конца списка, лучше начинать грузить пораньше, например, когда до максимума осталось 200 пикселей. Тут все зависит от высоты элементов списка и их количества на экране.
Следующий момент - виджет загрузки первой страницы. Каким он будет, зависит от вас. Часто используют
И в самом конце, нужно сделать отображение загрузки новой страницы. Для этого мы манипулируем количеством элементов в списке и его последним элементом:
Как и в случае с первоначальной загрузкой, вы можете выбрать то, что вам удобно и подходит.
Делитесь в комментариях, а каким виджетом вы показываете состояние загрузки на ваших проектах?
Для того, чтобы мы могли вызывать подгрузку новой страницы, нам нужен триггер. В зависимости от функционала можно рассмотреть как навигацию по страницам, в таком случае необходимо будет сбрасывать предыдущие данные, так и реализацию бесконечного списка, который мы и разберем подробнее.
В бесконечном списке страницы грузятся в момент, когда пользователь практически доходит до конца. Для этого нам понадобится
ScrollController, который мы подцепляем к виджету списка, это может быть ListView.builder или ListView.separated. Далее нужно добавить прослушку:
_scrollController.addListener(() {
final currentScroll = _scrollController.position.pixels;
final maxScroll = _scrollController.position.maxScrollExtent;
if (currentScroll >= (maxScroll - 200) && !isLoading) {
… // Вызов функции для подгрузки
}
});
Тут мы получаем текущее и максимальное положение скролла и сравниваем их. Совет - не нужно дожидаться, пока пользователь дойдет до самого конца списка, лучше начинать грузить пораньше, например, когда до максимума осталось 200 пикселей. Тут все зависит от высоты элементов списка и их количества на экране.
Следующий момент - виджет загрузки первой страницы. Каким он будет, зависит от вас. Часто используют
CircularProgressIndicator или какой-либо скелет, например, с Shimmer эффектом. Показываем мы его, если находимся в состоянии загрузки данных и у нас пока нет элементов списка:
if (isLoading && items.isEmpty) {
return …
}
И в самом конце, нужно сделать отображение загрузки новой страницы. Для этого мы манипулируем количеством элементов в списке и его последним элементом:
ListView.builder(
conroller: _scrollController,
…
itemCount: items.length + (isLoading && !isLastPage ? 1 : 0),
itemBuilder: (context, index) {
if (index < items.length) {
return … // Ваш элемент списка
}
return … // Ваш индикатор загрузки
},
);
Как и в случае с первоначальной загрузкой, вы можете выбрать то, что вам удобно и подходит.
Делитесь в комментариях, а каким виджетом вы показываете состояние загрузки на ваших проектах?
👍10❤🔥4❤3
Hola, Amigos! На связи Павел Гершевич, Mobile TeamLead в Amiga. В каждом приложении мы авторизуем пользователей, но не все встраивают механизмы обновления токенов.
Поэтому мы написали статью о том, как это делается, и поделились нашим опытом. Из нее вы узнаете:
Как работает авторизация запросов на сервере
🔵 Что хранит в себе JWT и как узнать, когда он закончит действовать
🔵 Как работает QueryInterceptor в Dio и чем он отличается от обычного
🔵 3 способа обработки токенов с завершившимся сроком действия
Ссылка на статью на Хабре
Делитесь в комментариях, а как вы поступаете при получении ошибки 401 Unauthorized?
Поэтому мы написали статью о том, как это делается, и поделились нашим опытом. Из нее вы узнаете:
Как работает авторизация запросов на сервере
Ссылка на статью на Хабре
Делитесь в комментариях, а как вы поступаете при получении ошибки 401 Unauthorized?
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤3🔥3
Hola, Amigos! На связи Павел Гершевич, Mobile TeamLead в Amiga. Для того, чтобы наши приложения работали лучше, можно отслеживать их жизненный цикл, чтобы не совершалось лишних действий. Для этого нам нужен
Рассказывайте в комментариях, а что вы делаете при изменении жизненного цикла приложения?
AppLifecycleState, который предоставляет добавление обратных вызовов для изменения состояния. Давайте посмотрим, какие состояния есть у Flutter-приложений:resumed - стандартное состояние, когда приложение находится в foreground. При переходе в него можно запускать остановленные потоки данных (Stream) и анимации, а также обновлять устаревшие данные.inactive - наше приложение видно, но пользователь не может с ним работать. Его можно увидеть, когда мы смотрим на все свернутые приложения. Также, например, открытие системного диалогового окна для выдачи разрешения переведет в это состояние. Тут можно остановить анимации. Но учитывайте, это состояние - временное.hidden - еще одно временное состояние. Наше приложение не видно, но оно еще не полностью на паузе. Оно добавлено в Flutter 3.13 чтобы мы могли сохранить состояние, пока операционная система не порезала ресурсы.paused - наше приложение полностью в фоне. Основной isolate еще работает, но Flutter Engine уже не занимается рендерингом. В этом состоянии можно отключиться от WebSockets, отменить работу таймеров, прослушку потоков данных. Но из этого состояния ОС может закрыть приложение.detached - крайнее состояние нашего приложения. Оно показывает, что приложение закрывается либо открывается (холодный запуск).Рассказывайте в комментариях, а что вы делаете при изменении жизненного цикла приложения?
1🔥11❤6👍5
Hola, Amigos! Мы все любим ускорять и упрощать написание шаблонного кода. Один из таких методов - Snippets.
Snippet - аббревиатура, которую IDE может преобразовать в код.
Показываем, как это сделать в Android Studio. А в следующей части расскажем про VS Code.
Рассказывайте в комментариях, используете snippets в работе?
Snippet - аббревиатура, которую IDE может преобразовать в код.
Показываем, как это сделать в Android Studio. А в следующей части расскажем про VS Code.
Рассказывайте в комментариях, используете snippets в работе?
1❤9👍8🔥5
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥16❤9👍7
Hola, Amigos! В прошлой части мы показали как создать свой собственный Snippet в Android Studio. Теперь пришла очередь сделать это в VS Code.
Делитесь в комментариях, какие Snippets для вас были бы самыми полезными?
Делитесь в комментариях, какие Snippets для вас были бы самыми полезными?
1🔥13❤4👍3