Flutter. Много @flutter_amiga Channel on Telegram

Flutter. Много

@flutter_amiga


Заказать мобильную разработку: https://amiga.agency/?utm_source=tg
Заказать рекламу в канале @amiga_agency_bot

Новости Flutter-разработки, дайджесты мероприятий, личный опыт.

Flutter. Много (Russian)

Если вы увлечены мобильной разработкой и хотите быть в курсе всех последних новостей и тенденций в мире Flutter, то канал 'Flutter. Много' (@flutter_amiga) именно для вас! Здесь вы найдете свежие дайджесты мероприятий, интересные статьи о личном опыте разработки на Flutter, а также информацию о том, как заказать мобильную разработку у профессионалов. Кроме того, у вас есть возможность заказать рекламу в канале @amiga_agency_bot и привлечь внимание к вашему продукту или услуге. Присоединяйтесь к нам, чтобы быть в курсе всего, что происходит в мире Flutter!

Flutter. Много

10 Jan, 12:49


Hola, Amigos! Сегодня поговорим о подходах к проектированию состояний в BLoC. В Flutter существует два основных подхода для создания состояний: single state и state machine.

📍 Single state
Это класс, содержащий все возможные поля, описывающие состояние BLoC. При изменении любого из этих полей мы создаем новое состояние, полностью обновляя текущий state.

Пример:

class MyBlocState{
final List<Todo> todos;
final bool loading;
final String? failure;

}

Использование метода emit для изменения состояния:

emit(MyBlocState.copyWith(
loading: true,
));


📍 State machine
В этом подходе каждое состояние представлено отдельным классом, унаследованным от общего родительского. Похожим способом в BLoC обычно создают события:

abstract class MyBlocState{}

class LoadingState extends MyBlocState{}
class LoadedState extends MyBlocState{
final List<Todo> todos;

}
class FailureState extends MyBlocState{
final String failure;

}


Для использования такого подхода в BLoC можно эмитить конкретные состояния:

emit(LoadingState());


Какой из этих подходов выбрать — зависит от сложности логики вашего приложения. State machine лучше подходит для сложных сценариев с разными типами состояний, тогда как single state — для простых случаев с минимальными изменениями.

Какой подход вам ближе? Пишите в комментариях!

Flutter. Много

31 Dec, 09:00


Hola, Amigos! С наступающим Новым годом🎄

2024 год подходит к концу — быстрый, шумный и полный интересных задач и открытий! Мы вместе с вами писали тысячи строк кода и учились новому. А сейчас отличное время, чтобы сделать паузу и набраться сил для будущих свершений🥂

Желаем, чтобы в 2025 у вас все легко складывалось: код работал стабильно, проекты вдохновляли, а идеи приходили сами собой❤️

Не забывайте находить баланс, отдыхать и наполняться энергией, и до встречи в Новом году!

Flutter. Много

24 Dec, 10:40


Hola, Amigos! На связи команда агентства продуктовой разработки Amiga.

Павел Гершевич, наш Mobile Team Lead написал подробную статью для тех, кто работает с BLoC. В ней вы найдете:

⚙️ Разбор встроенных трансформеров — concurrent, sequential, droppable, restartable.

⚙️ Пример создания кастомного трансформера с debounce для оптимизации работы с событиями.

⚙️ Идеи для разработки сложных трансформеров для объединения и обработки нескольких потоков событий.

Ссылка на статью на Habr ⬅️

Делитесь в чате, кто уже создавал свои трансформеры?

Flutter. Много

20 Dec, 10:15


Hola Amigos! Мы так долго ждали и наконец можем поделиться с вами классной новостью😍

🔥Вышел свежий выпуск Flutter Dev Podcast с участием руководителя нашей команды мобильной разработки Павла Гершевича, в котором он вместе с коллегами по рынку обсудил, что нужно знать о многомодульности во Flutter.

В выпуске:

⚙️ Когда и зачем переходить на многомодульность

⚙️ Как правильно подготовить архитектуру приложения

⚙️ Какие инструменты, например Melos, помогут на этом пути

Если вы хотите сделать разработку масштабируемой и разобраться, как модули помогут упростить поддержку больших приложений, обязательно слушайте на любимой платформе:

Слушать на сайте | Яндекс.Музыка | Spotify | YouTube | Звук | Apple Podcasts | Deezer | CastBox | Overcast | Pocket Casts | Podcast Addict | VK | Саундстрим | Mave-плеер

Делитесь впечатлениями и задавайте вопросы в чате ❤️

Flutter. Много

12 Dec, 11:24


Hola, Amigos! На связи мобильная команда агентства продуктовой разработки Amiga. Сегодня вышли Dart 3.6 и Flutter 3.27, но эти обновления совсем небольшие, поэтому делимся с вами в карточках самым важным:

- Обновленная работа с монорепозиториями;
- Отступы между виджетами в Row и Column;
- Разделители в больших числах
Pub.dev теперь показывает количество скачиваний пакетов;
- Много-много других небольших новинок.

Делитесь в чате, успели уже попробовать данное обновление?

Flutter. Много

09 Dec, 14:00


Hola, Amigos! Хотим поделиться с вами классной новостью. 6 декабря прошла церемония награждения премии Tagline Awards 2024 — главной награды за достижения в digital-сфере.

В премии участвует 500+ известных брендов и агентств, а награды вручаются за уровень работ, качество их исполнения и эффективность решения коммерческих задач🔥

Делимся победами! В этом году мы заняли:

🏆 2 место в номинации «Лучшее мобильное MVP-приложение» с кейсом разработки мобильного приложения для сети «Аптека Ваша №1»

🏆 3 место в номинации «Лучший маркетплейс» с кейсом создания первого маркетплейса горного оборудования в России

Спасибо команде за работу и заказчикам за доверие❤️ Такие достижения мотивируют расти и не останавливаться!

Читайте кейсы по ссылкам и пишите свое мнение в чате, мы будем рады вашему фидбеку)

Flutter. Много

06 Dec, 11:21


Hola, Amigos! На связи Михаил Чернецов, Flutter Dev в Amiga. Сегодня поговорим об одном из плагинов из набора plus_plugins — connectivity_plus.

Connectivity Plus — простой плагин для проверки сети, позволяющий использовать асинхронную функцию для определения соединений. Например:

final List<ConnectivityResult> connectivityResult = await Connectivity().checkConnectivity();


Однако лучшим способом будет прослушивание потока данных и сохранение в отдельный Singleton — тогда доступ к подключениям можно получить синхронно:

Connectivity().onConnectivityChanged.listen(
(List<ConnectivityResult> result) {
MyConnectivitySingleton.instance.setValue(result);
}
);


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

AppLifecycleListener(
onShow: () async {
await MyConnectivitySingleton.instance.refresh();
},
);


А как вы работаете с изменением состояния сети? Делитесь в чате.

Flutter. Много

29 Nov, 10:45


Дайджест ноября

Hola, Amigos! Собрали в одну подборку все полезные посты ноября, которые вы могли пропустить. Выбирайте, что вам интересно, и переходите по ссылкам.

⚪️ Chopper

⚪️ Retrofit

⚪️ Chopper или Retrofit

⚪️ 3 способа хранения ключей и различных переменных

⚪️ Flavors во Flutter. Android. iOS

⚪️ Команда Flutter. Михаил Чернецов

⚪️ Импорты во Flutter

⚪️ Лайфхаки в импортах во Flutter

Всем хорошего кода! 🙂

Flutter. Много

28 Nov, 10:18


Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Мы уже поговорили про импорты, но вы подсказали, что есть еще пару лайфхаков, связанных с ними.

Механизм Part Of

Все наши файлы на Dart это небольшие библиотеки, но иногда они становятся настолько большими, что их хочется поделить. В этом случае можно применить ключевые слова part и part of.

Этот механизм часто используется библиотеками для генерации кода. Думаю, вы видели файлы my_class.g.dart, если такими пользуетесь.

Давайте рассмотрим как работать с данным механизмом на примере BLoC. У нас есть файл my_bloc.dart, который будет основным, и файлы my_event.dart и my_state.dart, которые будут его частями.

В my_bloc.dart мы пропишем:

part ‘my_event.dart’;
part ‘my_state.dart;


И уже в файлах-частях вместо импортов будет строчка:

part of ‘my_bloc.dart’;


Таким образом эти 3 файла будут распознаваться как один и тем самым импорты мы можем писать только в my_bloc.dart, и он же будет импортироваться в другие файлы.

Part of имеет смысл применять, только если это неотделимые друг от друга классы. Например, события и состояния для BLoC или обработка JSON для моделей, в остальных случаях лучше использовать export.

Выбор импорта в зависимости от условий

Допустим, вы делаете приложение и на мобильные платформы, и на веб, но некоторые импорты нужно менять в зависимости от платформы. Это бывает при использовании dart:io и dart:web Тут к нам на выход приходит использование if.

Для этого нужно будет подготовить 3 файла — один под МП, второй под веб, третий если ни один из них не подходит. И создать третий файл для экспорта:

export ‘my_none.dart’
if (dart.library.io) ‘my_mobile.dart’
if (dart.library.js_interop) ‘my_web.dart’;


Импортируя уже этот файл, мы точно получим нужный нам инстанс, который будет работать.

Делитесь в чате, пробовали ли вы использовать эти 2 механики на своих проектах? И пишите, что еще хотели бы узнать, а мы соберем информацию об этом❤️

Flutter. Много

26 Nov, 10:43


Hola, Amigos! На связи Михаил Чернецов, Flutter Dev в Amiga. Сегодня мы расскажем о возможности импортов во Flutter.

Классы для экспорта

Часто случается, когда при импорте нескольких файлов из одной папки увеличивается количество строк:

import “class_a.dart”;
import “class_b.dart”;
import “class_c.dart”;
import “class_d.dart”;


Вместо этого мы можем создать в этой же папке специальный файл для экспорта. Например, my_imports.dart. После чего внесем наши файлы с ключевым словом export в начале:

export “class_a.dart”;
export “class_b.dart”;
export “class_c.dart”;
export “class_d.dart”;


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

import “my_imports.dart”;


Еще одна возможность, которая появляется — использовать любой класс из тех, что мы экспортировали, без необходимости импортировать их еще раз.

Ключевые слова as, hide и show

Наверное, вы уже видели такую запись:

import “dart:math” as math;


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

Допустим, у нас в 2 пакетах есть класс MyClass, и нам нужно использовать оба. В таком случае мы можем добавить ключевое слово as к импортам и использовать их вместе:

import 'my_package.dart' as package1;
import 'other_package.dart' as package2;

package1.MyClass();
package2.MyClass();


Также этот способ подходит, если в пакете присутствуют методы, и вы хотите знать, что они оттуда. Например, это используется при работе с пакетами math, http и dio.

Есть еще 2 ключевых слова, которые позволяют изолировать видимость классов, методов, переменных, расширений и миксинов из импортов:

1. hide. Скрывает, что нам не понадобится. Например, если нам не нужен какой-то определенный класс или его имя дублируется.

2. show. Отображает только то, что нам необходимо. Это позволяет нам использовать что-то определенное, без доступа к остальному.

import “my_model.dart” hide ModelA;
import “my_sevice.dart” show MyService;


Пишите в чате, как вы организовываете импорты на своих проектах?

Flutter. Много

22 Nov, 13:07


Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Продолжаем разбираться с Flavors. Сегодня посмотрим, как это делать в iOS.

Нам нужно сделать несколько шагов:

1) Создать копию Target у приложения.

2) Переименовать все схемы запуска через Runner -> Manage Schemes.

3) Дублировать конфигурации в проекте и переименовать их.

После этого мы сможем менять Bundle ID, логотип, название приложения и многое другое.

Смотрите в карточках, как это делается, и делитесь в чате, пробовали так на своих проектах?

Flutter. Много

21 Nov, 13:02


Hola, Amigos! С вами Михаил Чернецов, Flutter Dev в Amiga. Мы продолжаем рубрику о нашей команде и ее пути в мире разработки. Сегодня я расскажу немного о себе и своем опыте🔥

С Flutter я познакомился в 2021 году, когда начал писать свой дипломный проект. Сразу зацепило, насколько доступна и понятна была официальная документация. Она позволяла быстро войти в работу и буквально на практике изучать фреймворк, а большое количество обучающих материалов помогало углубиться в детали и разобраться с архитектурой приложений.

Со временем я понял, что одного Flutter иногда недостаточно. Это подтолкнуло меня изучать взаимодействие с нативными платформами, а также разбираться в backend-разработке — сейчас я постепенно осваиваю эти направления.

Из полезной литературы могу порекомендовать:

📚 «Грокаем алгоритмы» — чтобы прокачать алгоритмическое мышление.
📚 «Чистый код» — чтобы научиться писать понятный и поддерживаемый код.

Желаю всем вдохновения и успехов! 🚀

Flutter. Много

19 Nov, 12:16


Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Продолжаем разбираться с Flavors. Сегодня посмотрим, как это делается для Android.

Большую часть изменений мы будем делать в файле android/app/build.gradle, в блоке android. Давайте добавим Flavor:

android {

flavorDimensions “default”

productFlavors {
dev {
dimension “default”
applicationIdSuffix “.dev”
resValue “string”, “app_name”, “My App Dev”
}
}
}


Далее для замены иконки создадим папку app/src/dev и скопируем туда папки res из app/src/main, которые содержат иконку, и заменим их.

Получается, что одно и то же приложение при запуске через Flavor будет иметь разные иконку, название и applicationId.

Делитесь в чате, использовали Flavors на Android?

Flutter. Много

15 Nov, 11:13


Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Мы уже говорили про многомодульность и переменные окружения, а сегодня посмотрим на случай, когда нужно сделать изменения по нативной части или заменить что-то в приложении полностью. Например, название и иконки.

В этом нам помогут Flavors - механизм создания нескольких разных сборок из одной кодовой базы. Изначально они появились в нативных приложениях.

Зачем нам нужны Flavors?

🟡 Для разделения приложения по разным окружениям. Например, можно сделать версии для разработки, тестирования и релиза.

🟡 Для White Label приложений, где сборки различаются темами, иконками, ресурсами и переменными окружения.

🟡 Если у вашего приложения есть 2 версии - бесплатная и платная.

Что можно поменять через Flavors?

Bundle ID приложения. Например, добавить к нему суффикс, чтобы вместо com.sample.example стало com.sample.example.dev, или полностью его изменить, сделать com.myapp.example.
Иконку и название приложения. Это полезно, чтобы разграничить версии для разных окружений.
Переменные окружения для нативных частей. Для замены некоторых частей приложения, где это необходимо.
Тему приложения и ресурсы (assets). Для изменения внешнего вида приложения.


Давайте посмотрим, как это делается во Flutter-части наших приложений. Для начала создадим дополнительную точку входа для нашего приложения - main_dev.dart. Это полная копия нашего main.dart, но тут мы уже можем изменять тему и любые части, которые нужно.

Далее изменим pubspec.yaml, чтобы он добавил ресурсы, которые различаются для различных Flavors:

flutter:
assets:
- assets/common/ # Для общих ресурсов
- path: assets/main
flavors:
- main
- path: assets/dev
flavors:
- dev


И теперь мы можем запустить наш код:

flutter run --flavor dev --target lib/main_dev.dart


В следующих постах мы рассмотрим как нам сделать Flavors для Android и iOS.

Flutter. Много

14 Nov, 11:48


Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Сегодня мы рассмотрим 3 способа хранения ключей и различных переменных, которые зависят от окружения.

1. Использовать библиотеку flutter_dotenv. Для этого требуется создать файл .env в корне проекта:


MY_VAR=”MY VALUE”


И добавить его в assets в pubspec.yaml


assets:
.env


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

2. Использовать функционал Dart define:


flutter run --dart-define=MY_VAR=’MY VALUE’


И потом получать в нужном месте через


String.fromEnvironment(‘MY_VAR’);
bool.fromEnvironment(‘MY_SEC_VAR’);


Но данный способ не подойдет, если у вас много переменных.

3. Использовать Dart define from file. Для этого требуется создать файл с расширением .json в корне проекта:


{
“MY_VAR”: “MY VALUE”
}


И потом запустить приложение с командой:


flutter run --dart-define-from-file=my_env.json


Делитесь в чате, как вы храните такие переменные?

Flutter. Много

11 Nov, 14:49


CrossConf 2024

Hola, Amigos! С радостью делимся результатами крупнейшей ежегодной конференции по кроссплатформенной разработке и последним трендам в IT!

Мы заняли 3 место в номинации «Лучший материал» со статьей Александра Чаплыгина «Камера и Flutter». Если еще не читали, то можете исправить это по ссылке

И 2 место в номинации «Лучшая компания-просветитель в сфере кроссплатформенных технологий», в том числе благодаря нашему каналу Flutter.Много.

Спасибо, что читаете и поддерживаете нас! ❤️ Без вас этой победы бы не было. Будем и дальше делиться с вами полезными материалами и новыми событиями. Оставайтесь с нами!

Flutter. Много

08 Nov, 16:01


Hola, Amigos! На связи Flutter-команда агентства продуктовой разработки Amiga.

Раскрываем карты! Несколько месяцев мы держали в секрете одну разработку — это наша собственная библиотека для помощи в разработке и тестировании - amiga_dev_screen. Она предоставляет возможности для логирования ошибок, кастомных событий и запросов к API, а также с ее помощью можно менять окружение и добавлять прокси для использования с снифферами🔥

Сегодня мы подробно рассказали о ней на конференции CrossConf. Чтобы узнать подробнее о других решениях, ожидайте запись с выступления, она будет скоро.

Сейчас библиотека проходит финальные этапы доработки — релиз будет в декабре этого года.

Делитесь в чате, ждете ли вы выход библиотеки?

Flutter. Много

07 Nov, 09:31


Hola, Amigos! На связи Михаил Чернецов, Flutter dev в Amiga, и Павел Гершевич, Mobile Team Lead в Amiga. Недавно мы рассматривали два пакета Chopper и Retrofit, сегодня разберемся с их преимуществами и недостатками.

Chopper

🦋 В преимущества использования Chopper можно записать более простую, по сравнению с Retrofit, обработку ошибок, так как он под капотом использует пакет http, а не dio.

🦋 Из коробки есть готовые интерсепторы для логирования запросов к API.

🦋 Chopper используется достаточно популярной библиотекой swagger_dart_code_generator, которая помогает нам практически не писать код для работы с сетью, а использовать для генерации Swagger.

🦋 В минусы ему можно записать то, что нельзя обозначить тип возвращаемых данных в самом сервисе заранее, а нужно писать собственный конвертер JSON, из-за чего могут попадаться ошибки при парсинге.

Retrofit

🦋 У Retrofit уже встроена поддержка парсинга JSON из коробки, которую можно также вынести в отдельные изоляты.

🦋 Retrofit использует Dio, для которого существует большое количество дополнительных пакетов - от логирования (pretty_dio_logger) и провайдера для изображений (dio_image_provider) до кеширования (dio_cache_interceptor), работы с cookies (dio_cookie_manager) и обеспечения безопасности через SSL Pinning (http_certificate_pinning).

🦋 Но работа с Dio делает немного сложнее добавление своих интерсепторов, так как dio имеет более комплексную логику обработки.

🦋 Также мы не можем добавить свой собственный JSON конвертер к Retrofit, и приходится дополнять код необходимыми работами по созданию моделей, которые не будут использоваться в логике приложения. Этот подход все равно будет надежнее, так как мы избежим ошибок при парсинге.

Делитесь в чате, что используете на своих проектах. Chopper или Retrofit?

Flutter. Много

05 Nov, 09:02


Hola, Amigos! На связи Михаил Чернецов, Flutter dev в Amiga. В прошлый раз мы рассмотрели Chopper, аналог Retrofit для нативного Android, теперь же посмотрим на пакет Retrofit, работающий на схожих принципах.

Retrofit под капотом использует Dio - пакет для взаимодействия с сетью. Для начала работы нам нужно создать абстрактный класс, из которого будет сгенерирован код для запросов к API:

part "my_service.g.dart";

@RestApi()
abstract class MyService {
factory MyService (Dio dio) = _MyService;

@GET('/todos')
Future<List<Todo>> getTodos();

@Post(‘/todos’)
Future<void> addTodo({
@Body required CreateTodoBody body,
});
}


Для создания общей логики обработки запросов используется класс Interceptor с методами onRequest, onResponse и onError, который обрабатывает запросы, ответы и ошибки. Лучше использовать разные интерсепторы для различной логики вместо создания одного для обработки всего и сразу.

Чтобы отменить запрос, например, если у пользователя пропало интернет-соединение, нужно сделать это в методе onRequest:

return handler.reject(MyException(),requestOptions: options);


И для использования нашего сервиса необходимо передать в него Dio:

 Dio dio = Dio();
dio.options.baseUrl = ‘http://example.com/api’;
final client = MyService(dio);

await client.getTodos();


Делитесь в чате, что вы используете - Chopper или Retrofit?

Flutter. Много

01 Nov, 09:31


Hola, Amigos! На связи Михаил Чернецов, Flutter dev в Amiga. Сегодня поговорим про то, как мы работаем с сетевыми запросами, а именно об одной из библиотек, которая нам это облегчает - Chopper.

Chopper уже достаточно давно существует и был создан как аналог библиотеки Retrofit, которая активно используется на Android. Он использует генерацию кода для упрощения работы с HTTP-клиентом.

Для использования Chopper, необходимо создать сервисы для работы с Rest API:

part "my_service.chopper.dart";

@ChopperApi(baseUrl: "/todos")
abstract class MyService extends ChopperService {

static MyService create([ChopperClient? client]) =>
_$MyServiceService(client);

@Get()
Future<List<Todo>> getTodos();

@Post()
Future<void> addTodo({
@Body Map<String,dynamic> body,
});
}


Также необходимо создать класс самого клиента:

final chopper = ChopperClient(
baseUrl: "http://example.com/api",
services: [
MyService .service,
],
interceptor: [
MyInterceptor(),
],
);


Также для общей логики всех запросов можно использовать Interceptor, аналог middleware на Backend. В Chopper существует Request и Response Interceptor’ы.

class HeaderInterceptor extends RequestInterceptor{
@override
FutureOr<Request> onRequest(Request request)async {
return request.copyWith(
headers: {
…request.headers,
‘my_header’: ‘abc’
},
);
}
}


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

Flutter. Много

31 Oct, 10:04


Дайджест октября

Hola, Amigos! Собрали в одну подборку все полезные посты и статьи октября, которые вы могли пропустить. Выбирайте, что вам интересно, и переходите по ссылкам.

⚪️ Базовые концепции пакета rxdart. Часть 1

⚪️ Базовые концепции пакета rxdart. Часть 2

⚪️ Инструмент Dart FFI

🔴 Запись доклада «Логирование на Flutter или какие метрики помогут в оптимизации»

⚪️ Platform Channels

⚪️ Pigeon для Platform Channels

⚪️ Команда Flutter. Павел Гершевич

Всем хорошего кода! 🙂

Flutter. Много

29 Oct, 09:30


Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. В прошлом году мы уже знакомили вас с нашей командой и рассказывали об их пути во Flutter, но время идет и команда меняется, поэтому возвращаем рубрику о наших классных разработчиках! Начнем, конечно же, с меня🔥

Я познакомился с Flutter в далеком 2018, когда он был еще в Alpha-версии. До него и параллельно с ним я работал с нативным Android на Java и Kotlin, попутно переместившись из мобильной разработки на Web, где занимался React.js. Тогда не было особо источников для расширения знаний о фреймворке, поэтому учился по официальной документации и YouTube-каналу Flutter. Уже позже я познакомился с тем, что начало появляться в сети, и сейчас продолжаю следить за YouTube, Medium, VC и Habr. Еще слушаю доклады на конференциях и общаюсь со спикерами на совместных ивентах — это всегда прокачивает хард-скиллы.

В свободное время я не только выступаю на крупных конференциях — DUMP, CodeFest, Mobius и других — но и веду занятия по мобильной разработке у студентов крупнейшего ВУЗа Сибири - Сибирского Федерального Университета.

Для тех, кто только начинает свой путь с Dart и Flutter, могу посоветовать:
Курс LazyLoad Dart & Flutter;
Книгу «Основы Dart»;
Конечно же, дождаться выхода книги «Основы Flutter».

Желаю вам успешного профессионального роста! Не забывайте приходить в чат, если возникают вопросы или трудности, я всегда буду рад поделиться опытом и помочь!

Flutter. Много

25 Oct, 09:30


Hola, Amigos! Снова вещаем о Flutter на классных мероприятиях и зовем вас присоединиться! В этот раз выступим на конференции CrossConf по кроссплатформенной разработке и трендам IT, которая пройдет в Москве 8 ноября🔥

Конференция состоит из нескольких потоков:

- Crossplatform / Ecosystem
- Flutter
- Kotlin
- Product & Design
- Management

Павел Гершевич, Flutter Team Lead в Amiga, выступит с докладом «UI логгера на Flutter: Как сделать работу с логами удобнее и быстрее?».

В своем выступлении он расскажет о Dev Screen, зачем он нужен и как его внедрять, а также о том, как эффективно с ним работать при релизе приложения. Доклад будет особенно полезен Flutter-разработчикам.

Программа лежит по ссылке, а мы ждем вас 8 ноября в 12:45 во втором зале!

Flutter. Много

24 Oct, 11:39


Hola, Amigos! На связи Михаил Чернецов, Flutter dev в Amiga. Сегодня мы посмотрим на еще один способ интеграции нативного кода в Flutter - Pigeon.

Pigeon - инструмент, создающий интерфейсы для работы с нативным кодом при помощи кодогенерации. Это помогает нам не прописывать обработку Platform Channels напрямую.

-----------------------------------------------

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


import 'package:pigeon/pigeon.dart';

@HostApi()
abstract class CalculateHostApi {
int calculate(int a, int b);
}


Стоит учитывать, как данные передаются в платформу и какие типы данных поддерживаются платформой. Это можно узнать по ссылке.
Для получения сообщений от платформы необходимо использовать аннотацию @FlutterApi.
Так же можем описать классы, которые будут использоваться, Pigeon также сгененирует платформенных код для них.

Создадим файл generateCalculate.sh для того, чтобы запускать Shell-скрипт.


flutter pub run pigeon \
--input "pigeons/calculate.dart" \
--dart_out "lib/calculate.g.dart" \
--kotlin_out "android/app/src/main/kotlin/com/example/pigeon_example/Calculate.g.kt" \
--swift_out "ios/Runner/Calculate.g.swift"


Здесь мы обозначаем директории, в которых будет генерироваться Pigeon-файлы.
Если их не добавить, то данные файлы не сгенерируются. Для использования плагина на уровне Flutter-приложения теперь вызываем сгенерированный класс:


await CalculateHostApi().calculate(3, 7);


-----------------------------------------------

Для настройки Pigeon на Android теперь необходимо сделать две вещи:


1️⃣ Создать файл Calculate.kt и реализовать код нашего плагина:


class Calculate : FlutterPlugin, CalculateHostApi{
// Функция которую мы реализуем
override fun calculate(a: Long, b: Long): Long {
return a + b
}
// Подключение плагина к движку
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
CalculateHostApi.setUp(binding.binaryMessenger,this)
}

override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
CalculateHostApi.setUp(binding.binaryMessenger,null)
}
}


2️⃣ Добавить в плагины наш плагин в функции configureFlutterEngine в MainActivity:

flutterEngine.plugins.add(Calculate())



Для iOS необходимо внести изменения в файл AppDelegate.swift, проставить CalculateHostApi в наследники и вызвать CalculateHostApiSetup в функции application:

let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
CalculateHostApiSetup.setUp(
binaryMessenger: controller.binaryMessenger,
api: self
)


И в этом же классе реализуем функционал нашего плагина:

func calculate(a: Int64, b: Int64) -> Int64{
return a + b}


Тем самым мы реализовали Platform Channel с помощью Pigeon в нашем приложении.
Делитесь в чате, пробовали ли вы Pigeon для своих Platform Channels?

Flutter. Много

22 Oct, 11:29


Hola, Amigos! На связи Павел Гершевич, Mobile Team Lead в Amiga. Мы продолжаем рассказывать вам про вызов нативного кода из Flutter. Сегодня поговорим про Platform Channel, что это такое и зачем оно нужно?

Platform Channel - основной способ вызова нативного кода из Android и iOS на Flutter. Существует 2 типа - Method Channel и Event Channel. Основная разница в том, как они взаимодействуют с нашим приложением. Method Channel служит для вызова функций, а Event Channel это Stream, в который добавляются события.

Давайте посмотрим, как их создавать, на примере Method Channel. Для начала дадим нашему каналу имя и запишем его вместе с пакетом нашего приложения:

final channelName = ‘sample.example.com/my_method_channel’;

Далее подготовим метод, который обратится в нативный код:

Future<int> nativeSum(int a, int b) async {
await platfom.invokeMethod<int>(‘nativeSum’,{‘a’: a, ‘b’: b});
}

Если вызвать этот метод сейчас, то мы получим исключение, что такой Method Channel не найден, так как нам нужно добавить нативный код.

Для Android мы сначала добавляем конфигурацию в MainActivity.kt, создавая константу с названием канала:

private val CHANNEL = “sample.example.com/my_method_channel”

И добавляя сам канал методе configureFlutterEngine:

MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler(this)

Теперь нам надо добавить интерфейс MethodCallHandler к нашему классу MainActivity. После чего у нас появится метод для обработки обращений из Dart. Давайте напишем обработку для такого метода.

when (call.method) {
“nativeSum” -> {
val a = call.argument<Int>(“a”) ?: 0
val b = call.argument<Int>(“b”) ?: 0
result.success(a + b)
}
else -> result.notImplemented()
}

И давайте теперь сделаем это для iOS на Swift. Тут все изменения будут происходить в файле AppDelegate.swift. Создаем канал:

let myChannel = FlutterMethodChannel(name: “sample.example.com/my_method_channel”, binaryMessenger: controller.binaryMessenger)


И затем добавим обработку вызовов:

myChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in

switch call.method:
case “nativeSum”:
let args = call.arguments as? [String, Any]
let a = args[“a”] as? Int
let b = args[“b”] as? Int
result(a + b)
default:
result(FlutterMethodNotImplemented)
})

Таким образом, мы можем добавлять нативные библиотеки в свои проекты, выносить часть сложных вычислений, чтобы их распараллелить, или вызывать какие-либо способности устройства.

Делитесь в чате, пробовали ли вы писать свои платформенные каналы?

Flutter. Много

17 Oct, 09:02


Hola, Amigos! Наконец можем поделиться с вами записью доклада Паши Гершевича, нашего Mobile Team Lead Amiga, с конференции DevFest, которая прошла в сентябре в Омске🔥

Напомним, тема доклада: «Логирование на Flutter или какие метрики помогут в оптимизации». Разобрали самое важное:

⚙️ Зачем нужны логи разработчикам и тестировщикам
⚙️ Как сделать логи информативнее
⚙️ Какие метрики логировать в приложении и как это делать лучше всего
⚙️ Где сохранять логи, чтобы их можно было спокойно посмотреть

Запись выступления ждет вас по ссылке. Ставьте реакции, если понравился доклад Паши, и делитесь в чате, какая тема выступления вас бы точно заинтересовала!

Flutter. Много

16 Oct, 09:02


Hola, Amigos! На связи Михаил Чернецов, Flutter dev в Amiga. Сегодня мы поговорим про Dart FFI - инструмент, который позволяет нам встраивать нативный код, написанный на C или C++, в наши приложения, созданные на Dart.

Это позволяет ускорить некоторые сложные вычисления. При этом использование FFI, даже с интерфейсом, получается быстрее, чем вычисление на чистом дарте. Также некоторые библиотеки изначально написаны на C, и переписывание их на Dart будет дополнительной работой, требующей знания как и C, так и Dart.

В самом начале нам нужно получить собранную библиотеку - файл DLL. Для разных операционных систем он будет разным. Для Windows - .dll, для macOS - .dylib, для мобильных ОС - .so. Его можно создать при помощи CMake.

Для использования FFI нам нужно добавить 3 библиотеки - сам dart:ffi, dart:io для работы с платформой и нахождения наших файлов и path для помощи в этом.

Для подключения библиотеки C необходимо объявить сигнатуру функции на языке С:


typedef calculator_func = Int32 Function(Int32 a, Int32 b);


Int32 является нативным типом.

Объявим также тип для функции на языке Dart


typedef Calculator = int Function(int a, int b);


Загрузим библиотеку:


final dylib = ffi.DynamicLibrary.open(...);
final Calculator function = dylib
.lookup<NativeFunction<calculator_func>>(...)
.asFunction();


И теперь мы можем вызвать эту функцию:


print(function(7, 3));


Следует также учитывать, что в FFI нет типа String, и придется использовать Array<UInt32> для трансляции строки в Unicode.

Для веб-приложений существует возможность использовать JS interop, которая позволяет использовать внешние JavaScript библиотеки в веб-приложении. И для этого уже существуют разработанные решения, например, пакет web.

Также существуют Java/Kotlin и Objective C/Swift интеропы, но эти фичи экспериментальны. Еще можно использовать Platform Channel для взаимодействия с нативным кодом.

А вы пробовали работать с Dart FFI на своих проектах? Делитесь в чате.

Flutter. Много

14 Oct, 09:01


Hola, Amigos!

На связи Павел Гершевич, Mobile Team Lead агентства продуктовой разработки Amiga. Мы продолжаем изучать концепции и полезные функции в rxdart.

В прошлый раз мы забыли про троттлинг. Он похож на debounce, но возвращает не последнее, а первое значение, что полезно при обработке различных случаев, например, отправки форм. Для него можно использовать методы throttle и throttleTime.

Еще давайте затронем фильтрацию данных внутри стримов, так как это часто необходимо. Да, в Dart есть метод where, но его может не хватать или он будет слишком большим в коде. Поэтому в rxdart есть расширение whereType, которое позволяет фильтровать по типу без применения cast, и его дополнение - whereNotNull, которое убирает все нулевые значения из потока данных.

Еще одно полезное расширение - distinctUnique. Оно позволяет нам сохранять в стриме только уникальные значения. Очень похоже на применение toSet().toList() для списков.

И самое главное, что добавляет rxdart - это новые StreamController, которые здесь называются Subjects. Их 2 - BehaviorSubject и ReplaySubject. Давайте посмотрим на каждый из них.

BehaviorSubject - контроллер, который при добавлении слушателя сразу же передает ему последнее значение, которое попало в стрим. Также ему можно задать изначальное значение при помощи конструктора BehaviorSubject.seeded.

ReplaySubject - контроллер, который при добавлении слушателя сразу же передаст все значения, которые в него попадали. Но если нам нужно возвращать последние N значений, то мы можем применить в его конструкторе параметр maxSize.

Делитесь в чате, как часто вы используете стримы на своих проектах?

Flutter. Много

11 Oct, 10:01


Hola, Amigos!

На связи Павел Гершевич, Mobile Team Lead агентства продуктовой разработки Amiga. Сегодня рассмотрим некоторые базовые концепции пакета rxdart. Он позволяет нам работать с потоками данных еще эффективнее, так как добавляет много всего полезного.

RxDart - пакет от компании ReactiveX, которые до него уже сделали много популярных библиотек для других языков программирования. Например, RxJava или RxSwift. Но так как в Dart уже есть стримы, их не добавляли, но дополнительно улучшили, добавив методы расширений и новые типы потоков данных.

Давайте посмотрим для начала на то, как мы можем добавить буферизацию к нашему Stream. Допустим, что у нас есть поток данных, который выдает информацию достаточно часто, а нам нужно получать ее не по одному, а сразу несколько штук. Для этого мы можем воспользоваться добавлением еще одного потока данных, который будет отмерять время, просто передать количество, функцию для определения конца отрезка или необходимый отрезок времени, а функции buffer, bufferCount, bufferTest и bufferTime вернут нам список данных, который накопился.

Также иногда нам нужно отбрасывать данные и брать только последнее, что приходит. Для этого пригодятся методы debounce и debounceTime. В первый мы можем передать какой-нибудь другой Stream и при получении в него событий, получать данные и в изначальном. Во второй мы просто передадим время для промежутков с выдачей информации.

А что делать, если нужно объединить несколько потоков данных? Для этого у RxDart заготовлены несколько конструкторов. Например, MergeStream, который просто добавляет события из всех стримов в себя, или CombineLatestStream, который позволяет производить какие-либо вычисления, когда в один из стримов приходит событие, в этом случае, для других берутся последние значения.

Делитесь в чате, используете ли вы RxDart на своих проектах и как он вам помогает? А мы поделимся еще несколькими полезными концептами во второй части!