ar
Feedback
Flutter. Много

Flutter. Много

الذهاب إلى القناة على Telegram

Заказать мобильную разработку: https://amiga.ru//?utm_source=tg Заказать рекламу в канале @amiga_agency_bot Новости Flutter-разработки, дайджесты мероприятий, личный опыт.

إظهار المزيد
3 013
المشتركون
-224 ساعات
-47 أيام
-1030 أيام
أرشيف المشاركات
Hola, Amigos! Продолжаем рассказывать вам о полезных плагинах для Android Studio и VS Code. Сегодня на очереди плагин, которы
+4
Hola, Amigos! Продолжаем рассказывать вам о полезных плагинах для Android Studio и VS Code. Сегодня на очереди плагин, который заменит нам ручное использование CLI-инструмента - Flutter Intl. Он позволяет автоматизировать нашу работу с i18n. Делитесь в комментариях, что лучше, плагин или CLI? #плагины

Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Для многих задач уже есть готовые библиотеки, но иногда нужн
Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Для многих задач уже есть готовые библиотеки, но иногда нужно удостовериться, а действительно ли та или иная библиотека нужна в разрабатываемом приложении? Ловите наш чек-лист по выбору библиотек и проверке необходимости их применения. Начнем с того, что нужно определиться, на самом ли деле нужна библиотека. Если это библиотека какого-то сервиса, причем официальная, то ее можно добавлять. Но для других случаев зададим себе несколько вопросов: ⚪️Сколько времени в будущем сэкономит использование библиотеки? ⚪️Насколько сложно будет написать такой функционал своими силами? ⚪️Есть ли у команды опыт для реализации такого функционала? Если библиотека будет реально экономить время разработчиков, требуемый функционал достаточно сложен или у команды нет необходимого опыта для его реализации - будем выбирать и искать пакет. Если вы уже знаете саму библиотеку и использовали ее, то берите. Если еще нет или вы в первый раз ищите под такой функционал, то нужно смотреть на: ⭕️Дату последнего обновления библиотеки. Чем она ближе к текущей, тем лучше ⭕️Частоту обновления. Это может означать, что разработчик не забросил эту библиотеку и активно ее развивает ⭕️Количество скачиваний на pub.dev. Если их мало, то с такой библиотекой лучше не связываться ⭕️Issues на Github, если код открыт. Поможет понять, какие есть проблемы. ⭕️Прочитать ReadMe на предмет документации. Чтобы там не было всего пару строк, а было понятно, как использовать библиотеку Если у вас большой опыт в Dart и Flutter, дополнительно можно посмотреть: 🟡Зависимости. Там не должно быть ничего лишнего 🟡Код библиотеки. Он должен быть понятен вам 🟡API Reference. Если библиотека плохо документирована, то лучше от нее отказаться Рассказывайте в комментариях, а как вы выбираете библиотеки для своих проектов?

Hola, Amigos! Продолжаем рубрику с полезными extensions. Сегодня поговорим про расширение DateTime. Можно получить информацию
Hola, Amigos! Продолжаем рубрику с полезными extensions. Сегодня поговорим про расширение DateTime. Можно получить информацию совпадает ли дата с сегодняшней, вчерашней или завтрашней:

extension DateTimeX on DateTime {
  bool get isToday {
    final now = DateTime.now();
    return day == now.day && month == now.month && year == now.year;
  }

  bool get isTomorrow {
    final now = DateTime.now();
    return day == now.day + 1 && month == now.month && year == now.year;
  }

  bool get isYesterday {
    final now = DateTime.now();
    return day == now.day - 1 && month == now.month && year == now.year;
  }
}
Можно получить сколько дней разницы до сегодня:

extension DateTimeX on DateTime {
  int get daysDiff => this.difference(DateTime.now).inDays;
}
Делитесь в комментариях, про расширения для каких классов вы еще хотите узнать? #extensions

Hola, Amigos! Начинаем новую рубрику, где будем рассказывать вам про полезные Snippet’ы, которые вы можете добавить к себе в
Hola, Amigos! Начинаем новую рубрику, где будем рассказывать вам про полезные Snippet’ы, которые вы можете добавить к себе в IDE для ускорения разработки. В дальнейшем ее найдете по хештегу #snippet. Сегодня поговорим про сниппеты для жизненного цикла StatefulWidget. Сниппет для быстрого добавления initState - initS:

@override
void initState() {
  super.initState();

  // Your code here
}
Сниппеты для быстрого добавления dispose - dis и disF:

@override
void dispose() {
  // Your code here

  super.dispose();
}

@override
Future<void> dispose() async {
  // Your code here

  super.dispose();
}
Пишите в комментариях, про какие сниппеты рассказать в следующий раз?

Hola, Amigos! Наш Mobile Team Lead Павел Гершевич примет участие в активности под названием “Квартирник” на конференции CodeF
Hola, Amigos! Наш Mobile Team Lead Павел Гершевич примет участие в активности под названием “Квартирник” на конференции CodeFest 16. Квартирник - особый формат, где спикеры и эксперты общаются с залом на определенную тему. Павел вместе с Егором Поповым из Открытых Мобильных Платформ обсудят и ответят на вопросы касаемо кроссплатформы: Почему одни фреймворки постоянно на слуху, а про другие мы не видим множества статей и докладов? Есть ли границы у кроссплатформы? Можем ли мы написать одно приложение под все существующие устройства? В чем преимущества и недостатки кроссплатформы? Готовьте вопросы и приходите пообщаться с Павлом лично. Встречаемся 30 мая в 15:00 (11:00 МСК) на "Квартирнике" в Новосибирске! Ссылка на квартирник

Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Иногда бэкенд не может помочь с поиском по строкам или его н
Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Иногда бэкенд не может помочь с поиском по строкам или его нужно реализовать полностью на локальных данных. Мы столкнулись именно с такой задачей. В новой статье на Хабре разбираем, как прокачать локальный поиск с помощью математических алгоритмов и делимся нашим опытом реализации на Dart & Flutter. 🔗 Ссылка на статью Делитесь в комментариях, сталкивались ли вы с похожими кейсами?

Дайджест марта-апреля Hola Amigos! Собрали в одну подборку все полезные посты марта и апреля, которые вы могли пропустить. Вы
Дайджест марта-апреля Hola Amigos! Собрали в одну подборку все полезные посты марта и апреля, которые вы могли пропустить. Выбирайте, что интересно, и переходите по ссылкам для полезного выходного чтива: 🟡Кеширование картинок 🟡Пагинация на Flutter. Часть 1 и часть 2 🔴Статья про обновление токена при помощи Dio 🟡AppLifecycleState 🟡Создаем собственные snippets. В Android Studio и в VS Code 🟡Выбираем лицензию для библиотеки 🟡Полезные extensions для String 🟡Обфускация сборок 🟡Плагин Bloc для IDE 🟡RichText vs Text.rich 🟡Совершаем действия при тряске телефона Всем хорошего кода!

Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Сегодня мы разберем один интересный кейс - как сделать так,
+4
Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Сегодня мы разберем один интересный кейс - как сделать так, чтобы какое-то действие в приложении совершалось тогда, когда мы трясем телефон. В этом нам конечно же помогут 2 популярные библиотеки - sensors_plus и rxdart. Пишите в комментариях, какое действие вы бы завязали на такой триггер? P.S. А мы завязали один из триггеров открытия Amiga Dev Screen, который уже скоро вы сможете добавлять в свои проекты.

Hola, Amigos! Сегодня мы посмотрим, чем отличаются 2 виджета для вывода текста - RichText и Text.rich, а также разберемся, ка
Hola, Amigos! Сегодня мы посмотрим, чем отличаются 2 виджета для вывода текста - RichText и Text.rich, а также разберемся, какой из них лучше использовать. RichText из них основной. Он позволяет нам отрисовывать текст с применением нескольких стилей, а также добавлением виджетов внутрь. Простой пример применения - выделение части текста жирным или курсивом:

RichText(
  text: TextSpan(
    children: [
      TextSpan(text: ‘Обычный текст. ‘, style: TextStyle(fontSize: 14, color: Colors.black)),
      TextSpan(text: Жирный текст. ‘, style: TextStyle(fontSize: 14, color: Colors.black, fontWeight: FontWeight.bold)),
      TextSpan(text: ‘Снова обычный текст.‘, style: TextStyle(fontSize: 14, color: Colors.black)),
    ],
  ),
);
С другой стороны, нам нужно каждый раз применять стиль текста к RichText, а также передавать ему textScaler. Эту проблему можно решить при помощи Text.rich, но он уже менее гибкий, чем RichText, так как в нем сложнее сделать что-то по-максимуму кастомное.

Text.rich(
  TextSpan(
    children: [
      TextSpan(text: ‘Обычный текст. ‘),
      TextSpan(text: Жирный текст. ‘, style: TextStyle(fontSize: 14, color: Colors.black, fontWeight: FontWeight.bold)),
      TextSpan(text: ‘Снова обычный текст.‘),
    ],
  ),
);
Получается, что для большинства случаев нам подойдет именно Text.rich, а RichText нужен для максимальной кастомизации. Рассказывайте в комментариях, какой из них чаще используете в своей работе?

Hola, Amigos! Мы вводим еще одну новую рубрику и раз в месяц мы будем делиться с вами плагинами для Android Studio и VS Code,
+5
Hola, Amigos! Мы вводим еще одну новую рубрику и раз в месяц мы будем делиться с вами плагинами для Android Studio и VS Code, которыми пользуемся сами. Сегодня посмотрим на Bloc - плагин от разработчиков одноименной библиотеки. Он позволяет: • Создавать классы Bloc, State и Event в пару кликов с уже добавленным под ваше название шаблонным кодом. Для Android Studio также доступно создание с использованием библиотек equatable и freezed; • Оборачивать виджеты в BlocProvider, BlocListener, BlocBuilder, BlocConsumer и BlocSelector, как мы это делаем с оборачиванием в другие виджеты через плагин Flutter; • Добавлять шаблонный код для bloc при помощи специализированных snippets. Делитесь в комментариях, используете ли этот плагин? P.S. Вы сможете находить посты этой рубрики через #плагины

Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Сегодня поговорим о том, как защитить свой код от любопытных
Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Сегодня поговорим о том, как защитить свой код от любопытных глаз и сделать реверс-инжиниринг практически невозможным, используя обфускацию. Обфускация - процесс преобразования кода, чтобы он перестал быть понятным для человека, но его работоспособность не изменилась. Как обфусцировать сборку? Делать это, разумеется, необходимо при сборке для публикации. Например, мы собираем Android App Bundle:

flutter build appbundle --obfuscate --split-debug-info=/symbols
Параметр obfuscate запускает саму обфускацию, а уже split-debug-info выдает специальные файлы, которые помогут расшифровать информацию. Для этого мы указываем директорию, куда их нужно сложить. Как расшифровать Stack Trace? Для расшифровки также существует команда, в которую нужно будет передать файл Stack Trace и файл для расшифровки.

flutter symbolize \
  -i <stack-trace-file> \
  -d <obfuscated-symbols-file>
Сам файл расшифровки нужен для нужной нам архитектуры. Например, для Android на arm64 нужен будет файл app.android-arm64.symbols. Добавляем файл в Crashlytics Если мы используем Firebase Crashlytcs или любую подобную систему, то и там мы будем видеть нерасшифрованный Stack Trace. Чтобы этого не случалось, нужно выполнить команду для загрузки символов. Для Firebase она такая:

firebase crashlytics:symbols:upload --app=<firebase-app-id> <path-to-symbols>/symbols
Учтите, что Firebase App ID должен еще и соответствовать платформе - отдельно для Android, отдельно для iOS. Если вы уже автоматически отправляете dSYM файлы для iOS в Crashlytics, то вам ничего не нужно делать, так как символы уже будут загружены. Делитесь в комментариях, используете ли обфускацию на проектах?

Hola, Amigos! Мы постоянно стремимся уменьшить написание boilerplate кода, поэтому вводим новую рубрику - полезные extensions
Hola, Amigos! Мы постоянно стремимся уменьшить написание boilerplate кода, поэтому вводим новую рубрику - полезные extensions, где будем рассказывать о небольших кусочках кода, которые спасают нас каждый день. А начинаем мы со String. Первое расширение позволит нам ускорить проверку на пустую строку.

extension NullableStringX on String? {
  bool get isNullOrBlank => this == null || this.trim().isEmpty();
}
Пример:

final String? s = null;
s.isNullOrBlank; // true

'   '.isNullOrBlank // true
'test'.isNullOrBlank // false
Второе позволяет сделать первую букву строки заглавной, а также сделать это для предложений.

extension StringX on String {
  String capitalize() => this.trim().isEmpty ? this : '${this.trim()[0].toUpperCase()}${this.trim().substring(1).toLowerCase()}';

  String capitalizeSentense() => this.split('.').map((e) => e.capitalize()).join('. ').trim();
}
Пример:

'мое ПрЕдЛоЖеНиЕ РАЗНОГО размера'.capitalize() // Мое предложение разного размера

'три. слова. точка.'.capitalizeSentense() // Три. Слова. Точка.
Еще одно позволяет скопировать в Clipboard для дальнейшей вставки вместо дополнительного создания объектов в коде.

extension StringX on String {
  void copyToClipboard() => Clipboard.setData(ClipboardData(text: this));
}
В следующий раз мы разберемся с расширениями, которые помогут нам с валидацией данных и конвертацией. Рассказывайте в комментариях, используете ли вы extensions на проектах?

Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Не всё, что нам нужно, упаковано в библиотеки, поэтому иногд
Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Не всё, что нам нужно, упаковано в библиотеки, поэтому иногда мы пишем решения сами. После этого возникает желание завернуть код в пакет и отправить на pub.dev, чтобы облегчить работу другим разработчикам. А может, вы сами создаете полезное SDK и внедряете поддержку Flutter. И перед публикацией встает вопрос: какую лицензию выбрать? Давайте разбираться. Сегодня мы рассмотрим лицензии для Open Source проектов, которые чаще всего используют библиотеки под Dart и Flutter - MIT (uuid, flutter_svg), BSD-3-Clause (библиотеки от команд Dart и Flutter) и Apache 2.0 (clock, rxdart). Все они относятся к категории разрешительных: позволяют использовать, модифицировать и распространять код. Но отличия у них всё-таки есть. Давайте разбираться. Лицензия MIT запрещает предъявлять иски к авторам. Для ее использования нужно оставить уведомления в коде и файл с текстом самой лицензии. Можно сказать, что она является базовой. Ее стоит выбирать для небольших проектов и примеров кода. Лицензия BSD-3-Clause максимально похожа на MIT, но в нее добавлен пункт о запрете рекламы от имени автора. Ее можно выбрать, если вы не хотите, чтобы ваше имя использовали для рекламы чужого продукта. Лицензия Apache 2.0 является самой защищенной, хотя у нее нет полноценного пункта о запрете рекламы от имени автора (только при согласовании), но она максимально защищает патенты. Еще одно отличие - нужно указывать изменения в коде, если мы модифицируем библиотеку. Такую лицензию выбирают, если проект серьезный и имеет патенты. Еще одно важное отличие - совместимость с лицензиями GPLv3. MIT и BSD-3-Clause имеют совместимость только с GPLv2, поэтому их можно использовать в проектах с GPLv3, но не наоборот. А вот у Apache 2.0 есть полная совместимость. P.S. В 2024 году мы анонсировали набор библиотек Amiga Dev Screen. Сейчас мы его активно заканчиваем, а также выбрали для него лицензию MIT.

Hola, Amigos! В прошлой части мы показали как создать свой собственный Snippet в Android Studio. Теперь пришла очередь сделат
+4
Hola, Amigos! В прошлой части мы показали как создать свой собственный Snippet в Android Studio. Теперь пришла очередь сделать это в VS Code. Делитесь в комментариях, какие Snippets для вас были бы самыми полезными?

3️⃣0️⃣0️⃣0️⃣ 🦋
3️⃣0️⃣0️⃣0️⃣ 🦋

Hola, Amigos! Мы все любим ускорять и упрощать написание шаблонного кода. Один из таких методов - Snippets. Snippet - аббреви
+5
Hola, Amigos! Мы все любим ускорять и упрощать написание шаблонного кода. Один из таких методов - Snippets. Snippet - аббревиатура, которую IDE может преобразовать в код. Показываем, как это сделать в Android Studio. А в следующей части расскажем про VS Code. Рассказывайте в комментариях, используете snippets в работе?

Hola, Amigos! На связи Павел Гершевич, Mobile TeamLead в Amiga. Для того, чтобы наши приложения работали лучше, можно отслежи
+5
Hola, Amigos! На связи Павел Гершевич, Mobile TeamLead в Amiga. Для того, чтобы наши приложения работали лучше, можно отслеживать их жизненный цикл, чтобы не совершалось лишних действий. Для этого нам нужен AppLifecycleState, который предоставляет добавление обратных вызовов для изменения состояния. Давайте посмотрим, какие состояния есть у Flutter-приложений: resumed - стандартное состояние, когда приложение находится в foreground. При переходе в него можно запускать остановленные потоки данных (Stream) и анимации, а также обновлять устаревшие данные. inactive - наше приложение видно, но пользователь не может с ним работать. Его можно увидеть, когда мы смотрим на все свернутые приложения. Также, например, открытие системного диалогового окна для выдачи разрешения переведет в это состояние. Тут можно остановить анимации. Но учитывайте, это состояние - временное. hidden - еще одно временное состояние. Наше приложение не видно, но оно еще не полностью на паузе. Оно добавлено в Flutter 3.13 чтобы мы могли сохранить состояние, пока операционная система не порезала ресурсы. paused - наше приложение полностью в фоне. Основной isolate еще работает, но Flutter Engine уже не занимается рендерингом. В этом состоянии можно отключиться от WebSockets, отменить работу таймеров, прослушку потоков данных. Но из этого состояния ОС может закрыть приложение. detached - крайнее состояние нашего приложения. Оно показывает, что приложение закрывается либо открывается (холодный запуск). Рассказывайте в комментариях, а что вы делаете при изменении жизненного цикла приложения?

Hola, Amigos! На связи Павел Гершевич, Mobile TeamLead в Amiga. В каждом приложении мы авторизуем пользователей, но не все вс
Hola, Amigos! На связи Павел Гершевич, Mobile TeamLead в Amiga. В каждом приложении мы авторизуем пользователей, но не все встраивают механизмы обновления токенов. Поэтому мы написали статью о том, как это делается, и поделились нашим опытом. Из нее вы узнаете: Как работает авторизация запросов на сервере 🔵 Что хранит в себе JWT и как узнать, когда он закончит действовать 🔵 Как работает QueryInterceptor в Dio и чем он отличается от обычного 🔵 3 способа обработки токенов с завершившимся сроком действия Ссылка на статью на Хабре Делитесь в комментариях, а как вы поступаете при получении ошибки 401 Unauthorized?

Hola, Amigos! Продолжаем разбираться с пагинацией. Сегодня рассмотрим, как подключить логику работы к пользовательскому интер
Hola, Amigos! Продолжаем разбираться с пагинацией. Сегодня рассмотрим, как подключить логику работы к пользовательскому интерфейсу. Для того, чтобы мы могли вызывать подгрузку новой страницы, нам нужен триггер. В зависимости от функционала можно рассмотреть как навигацию по страницам, в таком случае необходимо будет сбрасывать предыдущие данные, так и реализацию бесконечного списка, который мы и разберем подробнее. В бесконечном списке страницы грузятся в момент, когда пользователь практически доходит до конца. Для этого нам понадобится 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 … // Ваш индикатор загрузки
  },
);
Как и в случае с первоначальной загрузкой, вы можете выбрать то, что вам удобно и подходит. Делитесь в комментариях, а каким виджетом вы показываете состояние загрузки на ваших проектах?

🎬🎬🎬🎬🎬🎬🎬🎬🎬🎬🎬🎬🎬 📱 Мобильная разработка в ритме города: Day&Night* 2026 Приглашаем мобильных разработчиков на главную конференцию Городских сервисов Яндекса. Саша Аникин расскажет про будущее городов и роботакси, а Кирилл Нейман разберёт техническую архитектуру машины с голосовым управлением. Всё остальное время займут тематические клубы. Мобильное направление курируют Саша Борисков — руководитель разработки клиентской платформы в Еде и Илья Царев — руководитель разработки в Яндекс Go. Обсудим: 🔶 Архитектуру супераппов, песочницы и внедрение ИИ-агентов в мобильную разработку. 🔶 Как делегировать нейросетям рутинную часть разработки. А для души будут клубы музыки и винила и активного образа жизни, где можно выдохнуть и пообщаться с единомышленниками. 🍸 Завершим вечер нашей традиционной вечеринкой до 2 ночи. 🚀 Регистрация открыта — успейте подать заявку! Все заявки проходят модерацию, обязательно дождитесь обратной связи. *День и Ночь